# scripts/cert-manager 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. ## Layout | 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) | — | ## Typical flow ### 1. Bootstrap a new environment ```bash # 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 ``` ### 2. Annual rotation (30 days before intermediate expires) ```bash # 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 ``` ### 3. Test fixtures ```bash scripts/cert-manager/test-certs.sh # generate scripts/cert-manager/test-certs.sh clean # remove ``` ## Security notes - **Root key never touches the cluster.** Generate it offline, store it offline, only use it to sign the intermediate. If the root key is compromised, the entire PKI is compromised (see `docs/runbooks/mtls-incident.md` §"CA compromise"). - **Passphrase management.** `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. - **test-certs.sh is throwaway.** The `testdata/` dir is gitignored because it contains private keys. The real PKI is generated by `ca-init.sh`, never committed. ## Related docs - `M14_SECURITY_PLAN.md` — full milestone plan - `docs/runbooks/mtls-incident.md` — incident response - `internal/auth/mtls.go` — the verifier (companion to the test certs) - `internal/auth/mtls_test.go` — table-driven tests that use the test certs