Skip to content

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:

  1. Ingress / host routing — the gateway maps the hostname to the adapter Service (and only the adapter — see Network hardening).
  2. 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.
  3. 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.

  • 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.com in 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/v1
kind: Ingress
metadata:
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 values

With 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:

Terminal window
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:

Terminal window
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:

Terminal window
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.

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.

Terminal window
# 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 1
curl -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.
  • 200 on 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.

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.

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.

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.