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:
2026-07-02 11:04:19 +10:00
parent feb44be76b
commit b5de55d4a6
3 changed files with 91 additions and 4 deletions

View File

@@ -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' \

View File

@@ -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.

View 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" }