Gateway, certificates, and DNS
Standing up a new Auth hostname — the public name callers sign requests against — is three separate concerns that fail in three separate ways. Keep them separate and you can tell which one is broken:
- Ingress / host routing — the gateway maps the hostname to the adapter Service (and only the adapter — see Network hardening).
- Certificate issuance — a certificate for the hostname exists, is validated, and is attached to the listener. This page uses AWS ACM as the worked example.
- Public DNS — the world can resolve the hostname to the load balancer.
flowchart LR D["Public DNS<br/>auth.example.com"] --> LB["ALB / load balancer<br/>(ACM certificate)"] LB --> I["Ingress host rule"] I --> A["trustplane-adapter"] A --> U["Upstream API"]
This page uses placeholder values throughout: auth.example.com is the placeholder hostname,
111122223333 the placeholder AWS account ID, and every <...> token a value you must
replace. No value on this page is real infrastructure.
Prerequisites
Section titled “Prerequisites”- A deployed adapter behind a gateway/ingress, with the network boundary from Network hardening applied or planned.
- The AWS Load Balancer Controller (for the ALB ingress example) or an equivalent ingress implementation.
- AWS CLI access to the account owning the certificate, with permission to request and describe ACM certificates.
- Write access to the DNS zone for the parent domain (
example.comin the placeholders) — needed twice: once for the ACM validation record, once for the public record.
Where every entered value comes from:
| Value | Placeholder | Source |
|---|---|---|
| Auth hostname | auth.example.com |
Your naming decision; must be in a zone you control |
| AWS region | <aws-region> |
The region of the ALB/cluster |
| Certificate ARN | arn:aws:acm:<aws-region>:111122223333:certificate/<certificate-id> |
Output of aws acm request-certificate |
| Validation CNAME name/value | from ACM | Output of aws acm describe-certificate |
| ALB DNS name | <alb-dns-name> |
kubectl get ingress ADDRESS column after the ALB provisions |
| Adapter Service name and port | trustplane-adapter, 8081 |
Your Helm release (adapter.port in values) |
| Protected route path | /<protected-route> |
Your route policy map (see Route policy maps) |
Step 1 — Route the hostname to the adapter
Section titled “Step 1 — Route the hostname to the adapter”Required for production deployment. Create the ingress host rule sending auth.example.com to the
adapter Service. The backend is the adapter, never the upstream:
# Placeholder values throughout — replace before use.apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: trustplane-adapter namespace: <adapter-namespace> # placeholder annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]' alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:<aws-region>:111122223333:certificate/<certificate-id> # placeholder; from Step 2 labels: {}spec: ingressClassName: alb rules: - host: auth.example.com # placeholder hostname http: paths: - path: / pathType: Prefix backend: service: name: trustplane-adapter # the adapter Service — never the upstream port: number: 8081 # placeholder: adapter.port from Helm valuesWith target-type: ip the ALB targets adapter pod IPs directly — remember from
Network hardening that ALB security groups govern external
reachability while NetworkPolicy governs the in-cluster paths; configure both.
Expected state: the ALB provisions and kubectl get ingress -n <adapter-namespace>
shows an ADDRESS like <alb-dns-name> (a *.elb.amazonaws.com name). Safe failure: an
empty ADDRESS usually means the certificate ARN is not yet valid (Step 2) or the controller
lacks permissions — nothing is serving traffic yet, so nothing is broken for callers.
Step 2 — Request and validate the ACM certificate
Section titled “Step 2 — Request and validate the ACM certificate”Required for production deployment. Request the certificate for the hostname, with DNS validation:
aws acm request-certificate \ --domain-name auth.example.com \ --validation-method DNS \ --region <aws-region>The output is the certificate ARN — the value the ingress annotation in Step 1 needs. Then read the validation record ACM wants to see in your DNS zone:
aws acm describe-certificate \ --certificate-arn arn:aws:acm:<aws-region>:111122223333:certificate/<certificate-id> \ --region <aws-region> \ --query 'Certificate.DomainValidationOptions[].ResourceRecord'Create that CNAME record (name → value, exactly as returned) in the example.com zone. This
validation record is separate from the public record in Step 5 — it proves domain control to
ACM and must stay in place for automatic renewal. Wait for issuance:
aws acm wait certificate-validated \ --certificate-arn arn:aws:acm:<aws-region>:111122223333:certificate/<certificate-id> \ --region <aws-region>Expected state: certificate status ISSUED. Safe failure: PENDING_VALIDATION for
more than the DNS propagation window usually means the CNAME was entered with a typo or in
the wrong zone; the certificate simply never issues — re-check the record against the
describe-certificate output.
Step 3 — Attach the certificate
Section titled “Step 3 — Attach the certificate”Put the issued certificate’s ARN into the alb.ingress.kubernetes.io/certificate-arn
annotation from Step 1 and apply the ingress. The ALB now terminates TLS for
auth.example.com with the ACM certificate.
Step 4 — Pre-DNS verification with curl --resolve
Section titled “Step 4 — Pre-DNS verification with curl --resolve”Before publishing public DNS, verify the whole chain — TLS certificate, host
routing, and adapter placement — by pinning the hostname to the ALB yourself. This technique
is for pre-DNS testing only; it is not a production configuration, and nothing about
--resolve belongs in any deployed config, script, or client.
# Resolve the ALB's current address, then pin auth.example.com to it for this one request.ALB_IP="$(dig +short <alb-dns-name> | head -n 1)" # placeholder ALB DNS name from Step 1curl -s -o /dev/null -w '%{http_code}\n' --max-time 5 \ --resolve auth.example.com:443:"${ALB_IP}" \ https://auth.example.com/<protected-route>Expected success state: 401 or 403 — the request traversed TLS and host routing and
reached the adapter, which denied an unsigned request. That denial is the success signal:
it proves verification is in the path.
Expected safe failure states:
- TLS error (curl exit code, no status): certificate not attached or wrong hostname on the certificate — re-check Steps 2–3.
000: the ALB address is wrong or the listener is not up — re-check Step 1.200on a protected route without a signed request: the route is not going through the adapter. Stop; fix the ingress backend and re-check Network hardening before any DNS publication.
Step 5 — Publish public DNS
Section titled “Step 5 — Publish public DNS”Required for production deployment. Create the public record for auth.example.com pointing at the
ALB: in Route 53, an alias record to the ALB DNS name; in other DNS providers, a CNAME to
<alb-dns-name>. This is the switch that makes the hostname real for callers — do it last,
after Step 4 passes.
Expected state: dig +short auth.example.com returns the ALB’s addresses, and the Step 4
curl now succeeds without --resolve.
Target health is not adapter readiness
Section titled “Target health is not adapter readiness”These are different claims, verified in different places:
| Claim | Where to verify |
|---|---|
| The ALB can reach a listening adapter | ALB target health / kubectl get ingress |
| The adapter serves the intended bundle version and acknowledged the target | Bundle distribution and target acknowledgment in the Operate cockpit |
| A derived key’s routes are live on the adapter | Step 7 propagation checks in the OIDC JWKS enrollment runbook |
Run the propagation checks before declaring the new hostname ready for callers, not just the health checks.
Rollback boundary
Section titled “Rollback boundary”Each concern rolls back independently, in reverse order of exposure:
- DNS: delete or repoint the public record — callers stop resolving the hostname; the ALB, certificate, and adapter are untouched.
- Ingress: delete the ingress host rule — the ALB stops routing the host; the certificate remains issued in ACM for reuse.
- Certificate: an issued ACM certificate costs nothing to leave in place; delete it (and its validation CNAME) only when the hostname is being retired for good.
None of these steps touch bundles, keys, enrollments, or Control state — this page’s blast radius is reachability only.
Next steps
Section titled “Next steps”- Network hardening — the in-cluster boundary behind this public front door, including the Control trusted-proxy configuration.
- Gateway integration patterns — routing shapes for Envoy, NGINX, Kong, and Gateway API deployments.
- OIDC JWKS enrollment runbook — enroll workloads against the new hostname and verify propagation.
- Operate cockpit — bundle distribution and acknowledgment evidence for the readiness claims above.