Adds a profile-gated step-ca service to docker-compose.federated.yml so the federated tier has its own internal CA. No code uses the CA yet — that lands in M2-04 (CA service).
What's in this PR
docker-compose.federated.yml: new step-ca service (image smallstep/step-ca:0.27.4, named volume step_ca_data, port 9000, [federated] profile gate, healthcheck with 30s start_period)
infra/step-ca/init.sh: idempotent first-boot init creating CA + JWK provisioner mosaic-fed
infra/step-ca/dev-password.example: sample dev password (real file gitignored)
infra/step-ca/templates/federation.tpl: SAN OID template skeleton (TODO: M2-04 lands real OID-bearing CSRs)
.gitignore: ignore real dev password file
Manual verification
docker compose -f docker-compose.federated.yml --profile federated up -d step-ca brings the CA up
docker compose -f docker-compose.federated.yml --profile federated logs step-ca shows "Server is starting on :9000"
curl -k https://localhost:9000/health returns 200
Idempotent: re-run up -d does not re-init (init.sh skips when /home/step/config/ca.json already exists)
M2-04 will exercise CSR submission against this CA
Decisions to confirm before M2-04
Image tag: Used smallstep/step-ca:0.27.4 (pinned stable as of late 2025). If Docker Hub shows a newer 0.27.* patch by the time M2-04 lands, bump then.
OID template skeleton:infra/step-ca/templates/federation.tpl is wired but incomplete. It sets keyUsage: digitalSignature and extKeyUsage: clientAuth — correct for federation client certs. The custom OID extension block (OIDs 1.3.6.1.4.1.99999.1 / .2) is stubbed with a TODO comment because the exact Step-CA template syntax for otherName SAN extensions requires testing against a live CA. M2-04 (CA service + CSR submission) will prove this end-to-end.
--no-db flag on init: Step-CA is initialized with --no-db (no BadgerDB). This is appropriate for a sidecar whose cert state lives in the named volume; it avoids BadgerDB locking issues in containers. If OCSP/CRL storage is needed later, this flag should be revisited.
Password mount path: Dev uses a bind-mount at ./infra/step-ca/dev-password → /run/secrets/ca_password. Production should replace the bind-mount with a Docker secret (same mount path, init.sh unchanged).
## Summary
Adds a profile-gated `step-ca` service to `docker-compose.federated.yml` so the federated tier has its own internal CA. No code uses the CA yet — that lands in M2-04 (CA service).
## What's in this PR
- `docker-compose.federated.yml`: new `step-ca` service (image `smallstep/step-ca:0.27.4`, named volume `step_ca_data`, port 9000, `[federated]` profile gate, healthcheck with 30s start_period)
- `infra/step-ca/init.sh`: idempotent first-boot init creating CA + JWK provisioner `mosaic-fed`
- `infra/step-ca/dev-password.example`: sample dev password (real file gitignored)
- `infra/step-ca/templates/federation.tpl`: SAN OID template skeleton (TODO: M2-04 lands real OID-bearing CSRs)
- `.gitignore`: ignore real dev password file
## Manual verification
- `docker compose -f docker-compose.federated.yml --profile federated up -d step-ca` brings the CA up
- `docker compose -f docker-compose.federated.yml --profile federated logs step-ca` shows "Server is starting on :9000"
- `curl -k https://localhost:9000/health` returns 200
- Idempotent: re-run `up -d` does not re-init (init.sh skips when `/home/step/config/ca.json` already exists)
## Test plan
- [x] `pnpm format:check` passes
- [x] `pnpm typecheck` passes (all cached, no TS changes)
- [x] `pnpm lint` passes (all cached, no TS changes)
- [x] `docker compose -f docker-compose.federated.yml config --quiet` validates YAML syntax
- [ ] CI green
- [ ] M2-04 will exercise CSR submission against this CA
## Decisions to confirm before M2-04
1. **Image tag:** Used `smallstep/step-ca:0.27.4` (pinned stable as of late 2025). If Docker Hub shows a newer `0.27.*` patch by the time M2-04 lands, bump then.
2. **OID template skeleton:** `infra/step-ca/templates/federation.tpl` is wired but incomplete. It sets `keyUsage: digitalSignature` and `extKeyUsage: clientAuth` — correct for federation client certs. The custom OID extension block (OIDs `1.3.6.1.4.1.99999.1` / `.2`) is stubbed with a TODO comment because the exact Step-CA template syntax for `otherName` SAN extensions requires testing against a live CA. M2-04 (CA service + CSR submission) will prove this end-to-end.
3. **`--no-db` flag on init:** Step-CA is initialized with `--no-db` (no BadgerDB). This is appropriate for a sidecar whose cert state lives in the named volume; it avoids BadgerDB locking issues in containers. If OCSP/CRL storage is needed later, this flag should be revisited.
4. **Password mount path:** Dev uses a bind-mount at `./infra/step-ca/dev-password` → `/run/secrets/ca_password`. Production should replace the bind-mount with a Docker secret (same mount path, init.sh unchanged).
Refs #461
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Adds a profile-gated
step-caservice todocker-compose.federated.ymlso the federated tier has its own internal CA. No code uses the CA yet — that lands in M2-04 (CA service).What's in this PR
docker-compose.federated.yml: newstep-caservice (imagesmallstep/step-ca:0.27.4, named volumestep_ca_data, port 9000,[federated]profile gate, healthcheck with 30s start_period)infra/step-ca/init.sh: idempotent first-boot init creating CA + JWK provisionermosaic-fedinfra/step-ca/dev-password.example: sample dev password (real file gitignored)infra/step-ca/templates/federation.tpl: SAN OID template skeleton (TODO: M2-04 lands real OID-bearing CSRs).gitignore: ignore real dev password fileManual verification
docker compose -f docker-compose.federated.yml --profile federated up -d step-cabrings the CA updocker compose -f docker-compose.federated.yml --profile federated logs step-cashows "Server is starting on :9000"curl -k https://localhost:9000/healthreturns 200up -ddoes not re-init (init.sh skips when/home/step/config/ca.jsonalready exists)Test plan
pnpm format:checkpassespnpm typecheckpasses (all cached, no TS changes)pnpm lintpasses (all cached, no TS changes)docker compose -f docker-compose.federated.yml config --quietvalidates YAML syntaxDecisions to confirm before M2-04
smallstep/step-ca:0.27.4(pinned stable as of late 2025). If Docker Hub shows a newer0.27.*patch by the time M2-04 lands, bump then.infra/step-ca/templates/federation.tplis wired but incomplete. It setskeyUsage: digitalSignatureandextKeyUsage: clientAuth— correct for federation client certs. The custom OID extension block (OIDs1.3.6.1.4.1.99999.1/.2) is stubbed with a TODO comment because the exact Step-CA template syntax forotherNameSAN extensions requires testing against a live CA. M2-04 (CA service + CSR submission) will prove this end-to-end.--no-dbflag on init: Step-CA is initialized with--no-db(no BadgerDB). This is appropriate for a sidecar whose cert state lives in the named volume; it avoids BadgerDB locking issues in containers. If OCSP/CRL storage is needed later, this flag should be revisited../infra/step-ca/dev-password→/run/secrets/ca_password. Production should replace the bind-mount with a Docker secret (same mount path, init.sh unchanged).Refs #461
d1925149c6to586129faf4