feat(net): custom domain for the Fusion-facing API
Fusion couldn't resolve the default *.execute-api.amazonaws.com endpoint, so front the same HTTP API with hncb-deid.apim-apac-demo.com (ACM DNS-validated cert, apigatewayv2 REGIONAL custom domain, root api mapping, Route53 A ALIAS). Callers resolve our hostname straight to IPs and never touch an execute-api name; also survives API-id churn. Point POLICY_SETUP + README demo at the custom URLs. Verified live: resolves to IPs, 401 unauthenticated, tokenizes with the key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -84,7 +84,8 @@ in `CLAUDE.md`, which record the exact sequencing and gotchas):
|
|||||||
|
|
||||||
## Demo script (Fusion-less dry run — maps to the 8 steps)
|
## Demo script (Fusion-less dry run — maps to the 8 steps)
|
||||||
```bash
|
```bash
|
||||||
cd terraform && DEMO=$(terraform output -raw demo_url) && UI=$(terraform output -raw ui_url); cd ..
|
cd terraform && DEMO=$(terraform output -raw demo_url_custom) && UI=$(terraform output -raw ui_url); cd ..
|
||||||
|
# (demo_url_custom uses the Route53 custom domain Fusion can resolve; demo_url is the raw execute-api one)
|
||||||
|
|
||||||
# 1-2, 4-8: advisor query -> tokenize -> agent (on tokens) -> restore, in one call:
|
# 1-2, 4-8: advisor query -> tokenize -> agent (on tokens) -> restore, in one call:
|
||||||
curl -s -X POST "$DEMO" -H 'content-type: application/json' \
|
curl -s -X POST "$DEMO" -H 'content-type: application/json' \
|
||||||
|
|||||||
@@ -35,14 +35,20 @@ advisor ◀────┘
|
|||||||
The transforms run **inside the policy boundary**; the raw name never crosses it.
|
The transforms run **inside the policy boundary**; the raw name never crosses it.
|
||||||
|
|
||||||
## Backend the policy calls
|
## Backend the policy calls
|
||||||
- `TOKENIZE_URL` = `https://<api-id>.execute-api.<region>.amazonaws.com/tokenize`
|
Use the **custom domain** — Fusion could not resolve the default
|
||||||
— `terraform -chdir=terraform output -raw tokenize_url`
|
`*.execute-api.ap-southeast-1.amazonaws.com` endpoint, so the API is fronted by a
|
||||||
- `RESTORE_URL` = `https://<...>/restore` — `terraform ... output -raw restore_url`
|
Route53-aliased name that resolves straight to IPs (see `terraform/custom_domain.tf`).
|
||||||
|
- `TOKENIZE_URL` = `https://hncb-deid.apim-apac-demo.com/tokenize`
|
||||||
|
— `terraform -chdir=terraform output -raw tokenize_url_custom`
|
||||||
|
- `RESTORE_URL` = `https://hncb-deid.apim-apac-demo.com/restore`
|
||||||
|
— `terraform -chdir=terraform output -raw restore_url_custom`
|
||||||
- `AGENT_RUNTIME_ARN` (the route's upstream) = printed by `scripts/agentcore_setup.sh`
|
- `AGENT_RUNTIME_ARN` (the route's upstream) = printed by `scripts/agentcore_setup.sh`
|
||||||
- **Auth:** public API Gateway HTTP API; send the shared secret in the **`x-api-key`**
|
- **Auth:** public API Gateway HTTP API; send the shared secret in the **`x-api-key`**
|
||||||
header (`Authorization: Bearer <secret>` also works). The secret is the Terraform
|
header (`Authorization: Bearer <secret>` also works). The secret is the Terraform
|
||||||
`tokenize_api_key` var (gitignored `terraform/local.auto.tfvars`, never committed) —
|
`tokenize_api_key` var (gitignored `terraform/local.auto.tfvars`, never committed) —
|
||||||
hand it to the console operator out of band.
|
hand it to the console operator out of band.
|
||||||
|
- *Note:* the raw `…execute-api…` URLs (`output -raw tokenize_url` / `restore_url`)
|
||||||
|
still work for anything that can resolve them; Fusion should use the custom domain.
|
||||||
- *Note:* a Lambda Function URL was the first choice, but this account's SCP blocks
|
- *Note:* a Lambda Function URL was the first choice, but this account's SCP blocks
|
||||||
unauthenticated Function URLs, so the public front door is API Gateway.
|
unauthenticated Function URLs, so the public front door is API Gateway.
|
||||||
|
|
||||||
|
|||||||
80
terraform/custom_domain.tf
Normal file
80
terraform/custom_domain.tf
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Custom domain for the Fusion-facing API. Fusion couldn't resolve the default
|
||||||
|
# *.execute-api.amazonaws.com hostname, so we front the same HTTP API with a
|
||||||
|
# stable name in a zone we control and point it with a Route53 ALIAS (A record).
|
||||||
|
# Callers resolve hncb-deid.apim-apac-demo.com -> IPs directly; they never have to
|
||||||
|
# resolve an execute-api / amazonaws.com name. Also survives API-id churn.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
locals {
|
||||||
|
api_fqdn = "hncb-deid.apim-apac-demo.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "aws_route53_zone" "api" {
|
||||||
|
name = "apim-apac-demo.com"
|
||||||
|
private_zone = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Regional API Gateway custom domains need the ACM cert in the SAME region.
|
||||||
|
resource "aws_acm_certificate" "api" {
|
||||||
|
domain_name = local.api_fqdn
|
||||||
|
validation_method = "DNS"
|
||||||
|
tags = local.onprem_tag
|
||||||
|
lifecycle {
|
||||||
|
create_before_destroy = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route53_record" "api_cert_validation" {
|
||||||
|
for_each = {
|
||||||
|
for dvo in aws_acm_certificate.api.domain_validation_options : dvo.domain_name => {
|
||||||
|
name = dvo.resource_record_name
|
||||||
|
type = dvo.resource_record_type
|
||||||
|
record = dvo.resource_record_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zone_id = data.aws_route53_zone.api.zone_id
|
||||||
|
name = each.value.name
|
||||||
|
type = each.value.type
|
||||||
|
records = [each.value.record]
|
||||||
|
ttl = 60
|
||||||
|
allow_overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_acm_certificate_validation" "api" {
|
||||||
|
certificate_arn = aws_acm_certificate.api.arn
|
||||||
|
validation_record_fqdns = [for r in aws_route53_record.api_cert_validation : r.fqdn]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_apigatewayv2_domain_name" "api" {
|
||||||
|
domain_name = local.api_fqdn
|
||||||
|
domain_name_configuration {
|
||||||
|
certificate_arn = aws_acm_certificate_validation.api.certificate_arn
|
||||||
|
endpoint_type = "REGIONAL"
|
||||||
|
security_policy = "TLS_1_2"
|
||||||
|
}
|
||||||
|
tags = local.onprem_tag
|
||||||
|
}
|
||||||
|
|
||||||
|
# Map the custom domain at the root to the $default stage (routes: /tokenize, etc.)
|
||||||
|
resource "aws_apigatewayv2_api_mapping" "api" {
|
||||||
|
api_id = aws_apigatewayv2_api.gateway.id
|
||||||
|
domain_name = aws_apigatewayv2_domain_name.api.id
|
||||||
|
stage = aws_apigatewayv2_stage.default.id
|
||||||
|
}
|
||||||
|
|
||||||
|
# ALIAS (IPv4) -> returns IPs directly, so Fusion never resolves an AWS hostname.
|
||||||
|
resource "aws_route53_record" "api_alias" {
|
||||||
|
zone_id = data.aws_route53_zone.api.zone_id
|
||||||
|
name = local.api_fqdn
|
||||||
|
type = "A"
|
||||||
|
alias {
|
||||||
|
name = aws_apigatewayv2_domain_name.api.domain_name_configuration[0].target_domain_name
|
||||||
|
zone_id = aws_apigatewayv2_domain_name.api.domain_name_configuration[0].hosted_zone_id
|
||||||
|
evaluate_target_health = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "tokenize_url_custom" { value = "https://${local.api_fqdn}/tokenize" }
|
||||||
|
output "restore_url_custom" { value = "https://${local.api_fqdn}/restore" }
|
||||||
|
output "demo_url_custom" { value = "https://${local.api_fqdn}/demo" }
|
||||||
Reference in New Issue
Block a user