3. Install and activate the Auth adapter
Auth sits in front of a private API and verifies every request locally. Control publishes a signed trust and policy bundle for this site’s registered distribution target. This chapter builds one EKS, arm64, single-replica example and checks both its private upstream and its Control-signed activation. A Ready pod by itself does not establish either property.
Previous: Target identity and trust. Next: Create or confirm the client and grant.
Check the cluster and site inputs
Section titled “Check the cluster and site inputs”This example requires reviewed access to an EKS context with Linux arm64 EC2 nodes, a
NetworkPolicy-enforcing CNI, AWS Load Balancer Controller with the alb IngressClass, an
encrypted retained gp3 StorageClass backed by EBS CSI, an ACM certificate for the exact
Auth hostname, and write access to its authoritative Route 53 zone. The VPC CNI’s network
policy feature is not enabled by default;
follow the AWS configuration guide
and verify enforcement before relying on this example. The ALB controller
and EBS CSI StorageClass
must already be installed. Review the cost of a dedicated public ALB and retained PVC.
For another architecture, CNI, ingress controller or storage provider, adapt and test the
manifests for that platform before deployment.
Set these values from the same selected Auth Site in Control and the reviewed cluster; never copy an ID, hostname, key or certificate from another site.
| Input | Source |
|---|---|
| Environment, fleet, site, distribution target, API, audience, route and trust domain | Selected Control site, protected surface, target registration and bootstrap handoff. |
| Control API URL and release descriptor | The selected site’s ready Bootstrap handoff, not the browser UI URL. |
| Auth hostname | The exact hostname whose DNS ownership was verified in Chapter 1. |
| Target private key and Control signer public key | Chapter 2’s owner-held file and verified site-scoped public-key download. |
| Kubernetes context, encrypted retained class, certificate ARN and DNS zone | Your cluster and DNS inventory. |
| Control egress CIDR | The reviewed reachable Control API destination for your CNI; do not use the example CIDR. |
The three downloadable manifests are reviewed templates, not live-site values:
Save them in an owner-controlled directory and inspect every line before applying:
umask 077curl -fsSLo echo.eks.yaml https://docs.auth.trustplane.dev/examples/managed-auth-site/echo.eks.template.yamlcurl -fsSLo auth.values.yaml https://docs.auth.trustplane.dev/examples/managed-auth-site/values.eks.template.yamlcurl -fsSLo ingress.eks.yaml https://docs.auth.trustplane.dev/examples/managed-auth-site/ingress.eks.template.yamlReplace every REPLACE_ value, example.invalid hostname, documentation-only CIDR,
certificate ARN, namespace and Service name with reviewed values. Set
controlDistribution.adapterVersion: v0.2.5 explicitly: chart 0.2.2 otherwise defaults
the agent’s adapter version to 0.2.2 even with image v0.2.5. Keep both image references
on the approved digest. The render-only values example
is a schema aid; it is not a deployment recipe.
Before creating anything, inspect the selected context and collisions:
kubectl config current-contextkubectl get nodes -o widekubectl get ingressclass albkubectl get storageclasskubectl get namespace trustplane-auth-tutorialkubectl get ingress -Ahelm list --namespace trustplane-auth-tutorialThe get namespace command should report NotFound for a new disposable namespace. Review
the selected class’s provisioner, encryption and reclaim policy; require Retain for this
exercise. Confirm that the reviewed CNI enforces NetworkPolicy, and that no existing
Ingress or public Service routes directly to the echo. Stop on name or ownership collisions.
Review the inputs and render
Section titled “Review the inputs and render”The inspected Helm chart is 0.2.2 (package SHA-256
b40987d453af1bbc979011bfce0184a45e8a9e568a26cd4ebe5bdb47a1c7f33b);
the inspected adapter image is
v0.2.5@sha256:7851520f47ca5d612fc8095dbb6a8861c2072f13b2dbc0b14fc7d78aec6f8d44.
Recheck the release ledger and selected-site descriptor before use.
helm pull oci://ghcr.io/trustplane-dev/charts/trustplane-auth --version 0.2.2printf '%s %s\n' b40987d453af1bbc979011bfce0184a45e8a9e568a26cd4ebe5bdb47a1c7f33b trustplane-auth-0.2.2.tgz | shasum -a 256 -c -helm template tutorial-auth ./trustplane-auth-0.2.2.tgz \ --namespace trustplane-auth-tutorial --values ./auth.values.yaml > ./auth.rendered.yamlCheck the rendered image digest, --adapter-version=v0.2.5, exact target binding, Secret
names and data keys, private upstream, retained PVC, durable replay and NetworkPolicy peers.
Review the entire render before applying it. The chart creates two internal Services and no
public TLS ingress. In particular, the signer Secret contains only Control’s public key;
the target Secret contains the Auth distribution private key. Never place either private
key in Helm values, the render, terminal output or documentation.
Install the private upstream and Auth
Section titled “Install the private upstream and Auth”After the context, template and name review, create the new namespace and private echo.
The echo Service must remain ClusterIP; its NetworkPolicy allows ingress only from the
adapter pod labels and denies echo egress.
kubectl create namespace trustplane-auth-tutorialkubectl -n trustplane-auth-tutorial apply -f ./echo.eks.yamlkubectl -n trustplane-auth-tutorial rollout status deployment/tutorial-echo --timeout=5mkubectl -n trustplane-auth-tutorial get service tutorial-echo -o wideCreate the two site-specific Secrets from the Chapter 2 files. These create commands
fail if either name already exists; investigate that collision instead of replacing a key.
kubectl -n trustplane-auth-tutorial create secret generic tutorial-auth-target-key \ --from-file=target-private-key=./target-private-keykubectl -n trustplane-auth-tutorial create secret generic tutorial-auth-control-signer \ --from-file=control-signing-public-key=./control-signing-public-keyhelm upgrade --install tutorial-auth ./trustplane-auth-0.2.2.tgz \ --namespace trustplane-auth-tutorial --values ./auth.values.yaml \ --atomic --timeout 15mkubectl -n trustplane-auth-tutorial get statefulset,pods,pvc,service,networkpolicyUse --kube-context or --context on every command if your shell might switch contexts;
the commands above assume the reviewed current context remains selected. Require the PVC
to be Bound to the retained encrypted class, the Auth StatefulSet pod healthy, and only internal
Services.
Publish only the Auth edge
Section titled “Publish only the Auth edge”Review ingress.eks.yaml after substitutions. Its exact host and /tutorial/echo path
must route to the adapter Service only. Never route to tutorial-echo, add a public echo
Service or attach this Ingress to a shared serving site’s ALB group. The template creates
a separate internet-facing ALB and redirects HTTP to HTTPS.
kubectl -n trustplane-auth-tutorial apply -f ./ingress.eks.yamlkubectl -n trustplane-auth-tutorial get ingress tutorial-auth -o widekubectl -n trustplane-auth-tutorial describe ingress tutorial-authWait for the Ingress ADDRESS, then create a CNAME from the subdomain Auth hostname
to that ALB DNS name in the authoritative Route 53 zone. Review the exact hostname, zone,
target and TTL before submitting a DNS change. This CNAME is separate from Chapter 1’s
TXT ownership proof; if your registrar delegates the zone elsewhere, edit the authoritative
provider instead. For a Route 53 hosted zone, set these from your reviewed records and
generate a change file without shell interpolation into JSON:
export TP_HOST='auth-tutorial.example.invalid'export TP_ALB_DNS='<Ingress ADDRESS without a trailing dot>'export TP_ZONE_ID='<authoritative Route 53 hosted zone ID>'python3 - <<'PY'import jsonimport osfrom pathlib import Path
host = os.environ["TP_HOST"].rstrip(".")target = os.environ["TP_ALB_DNS"].rstrip(".")if host.endswith("example.invalid") or not host or not target: raise SystemExit("Replace the example hostname and ALB address")change = {"Comment": "Dedicated tutorial Auth edge", "Changes": [{"Action": "CREATE", "ResourceRecordSet": { "Name": host + ".", "Type": "CNAME", "TTL": 300, "ResourceRecords": [{"Value": target + "."}]}}]}Path("auth-dns-change.json").write_text(json.dumps(change, indent=2) + "\n")PYcat auth-dns-change.jsonaws route53 change-resource-record-sets \ --hosted-zone-id "$TP_ZONE_ID" --change-batch file://auth-dns-change.jsonOnly submit the last command after comparing the generated record to the selected site’s
hostname and the dedicated Ingress ADDRESS. CREATE fails if a record with that name and
type already exists; inspect it instead of overwriting another tenant’s DNS. For a DNS
provider other than Route 53, use
its equivalent CNAME workflow. Confirm both DNS and the certificate hostname:
dig +short CNAME "$TP_HOST"curl -sS -o /dev/null -w 'HTTP %{http_code}; TLS verify %{ssl_verify_result}\n' \ "https://$TP_HOST/tutorial/echo"The sample hostname above is deliberately invalid: substitute the verified Auth hostname.
An unsigned GET /tutorial/echo returned HTTP 401 with valid TLS in the isolated
exercise. That checks edge reachability and fail-closed behavior, not client authorization.
Verify the private boundary and signed activation
Section titled “Verify the private boundary and signed activation”Use temporary probes in the same namespace. The allowed pod carries the adapter labels
selected by the echo NetworkPolicy; the second does not. The first must receive the echo
response. The second must fail with a connection timeout, not a DNS or image-pull error.
--rm removes the temporary pods when each command exits:
kubectl -n trustplane-auth-tutorial run echo-allowed-probe --rm -i --restart=Never \ --labels=app.kubernetes.io/instance=tutorial-auth,app.kubernetes.io/component=adapter \ --image=docker.io/library/busybox:1.36.1@sha256:bd44eb136a95dcc8dc58995e43abc40a413f2e8e3d4a2aae6bccbe94686acb05 \ --command -- wget -qO- -T 3 http://tutorial-echo:8080/tutorial/echokubectl -n trustplane-auth-tutorial run echo-denied-probe --rm -i --restart=Never \ --labels=app=tutorial-probe \ --image=docker.io/library/busybox:1.36.1@sha256:bd44eb136a95dcc8dc58995e43abc40a413f2e8e3d4a2aae6bccbe94686acb05 \ --command -- wget -qO- -T 3 http://tutorial-echo:8080/tutorial/echokubectl -n trustplane-auth-tutorial get service tutorial-echo -o widekubectl get ingress,service -AConfirm the Service stays ClusterIP, the reviewed CNI enforces policy, and no Ingress,
Gateway or public Service exposes the echo. A timeout alone is not proof of that boundary.
In Control Auth Site Detail → Readiness, refresh the site and compare the promoted target configuration, bundle/trust/policy digests and instance acknowledgement with the agent’s restricted local status. Require the loaded signed generation, active instance quorum, a future offline lease and the bound retained PVC. If you used pre-publication, the package already existed before the agent started; now require its ACK. In the recorded empty-policy-first path, the waiting agent advertised paired capability and Control then published/promoted a signed deny-all generation. Compare the site’s bootstrap public key byte-for-byte with the pinned startup key. An old browser view may show a stale lease; refresh before comparing against the agent’s current lease.
Then follow Chapter 4 and
Chapter 5 to prove a signed, granted GET returns
HTTP 200 and its JTI appears in Control Audit. Also send an unsigned request and an
ungranted operation and require denial without an upstream echo response. These are
separate from the network probes: they test Auth’s policy enforcement and audit trail.