Scripts for the M14-backend PKI (mTLS opt-in per source + internal gRPC mTLS). These run before K8s is involved — the root CA stays offline, the intermediate is what gets imported into cert-manager.
| File | Purpose | When to run |
|---|---|---|
ca-init.sh |
Generate root + intermediate for a new env | Once per env (dev, staging, prod) |
ca-rotate-intermediate.sh |
Rotate the intermediate (keep root) | Annually, ~30d before expiry |
test-certs.sh |
Generate throwaway test certs for internal/auth/mtls_test.go |
Whenever tests need refreshing |
README.md |
This file | — |
testdata/ |
Test cert fixtures (gitignored, regenerated) | — |
# On an air-gapped machine or encrypted USB
export BA_CA_PASSPHRASE='...' # use a password manager
scripts/cert-manager/ca-init.sh prod /secure/pki
# Move the root offline, keep the intermediate accessible
mv /secure/pki/prod/root-ca.key /offline-usb/
mv /secure/pki/prod/root-ca.crt /offline-usb/
# Import the intermediate into K8s (sealed-secrets or external-secrets)
# Apply the cert-manager ClusterIssuer manifest (deploy/cert-manager/...)
# Issue serving certs
# On the air-gapped machine
export BA_CA_PASSPHRASE='...'
scripts/cert-manager/ca-rotate-intermediate.sh prod /offline-usb /tmp/new-pki
# Re-import the new intermediate into K8s
# Restart cert-manager controller
# Re-issue serving certs (delete+apply, or annotate)
# Verify with the smoke test
scripts/cert-manager/test-certs.sh # generate
scripts/cert-manager/test-certs.sh clean # remove
docs/runbooks/mtls-incident.md §"CA compromise").BA_CA_PASSPHRASE is the dev/CI path.
For prod, prefer interactive prompts or a hardware token. Never
store the passphrase in the same place as the cert.testdata/ dir is gitignored
because it contains private keys. The real PKI is generated by
ca-init.sh, never committed.M14_SECURITY_PLAN.md — full milestone plandocs/runbooks/mtls-incident.md — incident responseinternal/auth/mtls.go — the verifier (companion to the test certs)internal/auth/mtls_test.go — table-driven tests that use the test certs