feat(federation): Step-CA sidecar in federated compose [FED-M2-02] #490

Merged
jason.woltje merged 2 commits from feat/federation-m2-stepca into main 2026-04-22 02:21:51 +00:00
Owner

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

  • pnpm format:check passes
  • pnpm typecheck passes (all cached, no TS changes)
  • pnpm lint passes (all cached, no TS changes)
  • 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

## 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
jason.woltje added 2 commits 2026-04-22 02:00:58 +00:00
Pure Zod-based validator for federation grant scope objects per PRD §8.1.
Independent of CA, DB, and NestJS wiring — those land in M2-06.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat(federation): add Step-CA sidecar to federated compose stack [FED-M2-02]
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
d1925149c6
Adds a profile-gated `step-ca` service to `docker-compose.federated.yml`
so the federated tier has its own internal CA. No gateway code consumes
the CA yet — that lands in M2-04 (ca.service.ts).

- docker-compose.federated.yml: new `step-ca` service using image
  `smallstep/step-ca:0.27.4` (pinned stable; `latest` forbidden by
  Mosaic image policy), named volume `step_ca_data`, port 9000,
  `[federated]` profile gate, healthcheck with 30s start_period
- infra/step-ca/init.sh: idempotent first-boot init; runs `step ca init`
  with JWK provisioner `mosaic-fed` if /home/step/config/ca.json absent;
  otherwise starts CA directly
- infra/step-ca/dev-password.example: sample dev password (real file
  is gitignored)
- infra/step-ca/templates/federation.tpl: X.509 template skeleton for
  custom OID SAN extensions (grantId 1.3.6.1.4.1.99999.1,
  subjectUserId 1.3.6.1.4.1.99999.2); TODO comment links M2-04 as the
  landing point
- .gitignore: ignores infra/step-ca/dev-password (real password)

Refs #461

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jason.woltje force-pushed feat/federation-m2-stepca from d1925149c6 to 586129faf4 2026-04-22 02:09:47 +00:00 Compare
jason.woltje merged commit c56dda74aa into main 2026-04-22 02:21:51 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#490