feat(federation): Step-CA sidecar in federated compose [FED-M2-02] (#490)
Some checks failed
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline failed

This commit was merged in pull request #490.
This commit is contained in:
2026-04-22 02:21:49 +00:00
parent 9f1a08185e
commit c56dda74aa
6 changed files with 187 additions and 15 deletions

View File

@@ -0,0 +1 @@
dev-only-step-ca-password-do-not-use-in-production

60
infra/step-ca/init.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/sh
# infra/step-ca/init.sh
#
# Idempotent first-boot initialiser for the Mosaic Federation CA.
#
# On the first run (no /home/step/config/ca.json present) this script:
# 1. Initialises Step-CA with a JWK provisioner named "mosaic-fed".
# 2. Writes the CA configuration to the persistent volume at /home/step.
#
# On subsequent runs (config already exists) this script skips init and
# starts the CA directly.
#
# The provisioner name "mosaic-fed" is consumed by:
# apps/gateway/src/federation/ca.service.ts (added in M2-04)
#
# Password source:
# Dev: mounted from ./infra/step-ca/dev-password via bind mount.
# Prod: mounted from a Docker secret at /run/secrets/ca_password.
#
# OID template:
# infra/step-ca/templates/federation.tpl is copied into the CA config
# directory so the JWK provisioner can reference it. The template
# skeleton is wired in M2-04 when the CA service lands the SAN-bearing
# CSR work.
set -e
CA_CONFIG="/home/step/config/ca.json"
PASSWORD_FILE="/run/secrets/ca_password"
if [ ! -f "${CA_CONFIG}" ]; then
echo "[step-ca init] First boot detected — initialising Mosaic Federation CA..."
step ca init \
--name "Mosaic Federation CA" \
--dns "localhost" \
--dns "step-ca" \
--address ":9000" \
--provisioner "mosaic-fed" \
--password-file "${PASSWORD_FILE}" \
--provisioner-password-file "${PASSWORD_FILE}" \
--no-db
echo "[step-ca init] CA initialised."
# Copy the X.509 template into the Step-CA config directory so the
# provisioner can reference it in M2-04.
if [ -f "/etc/step-ca-templates/federation.tpl" ]; then
mkdir -p /home/step/templates
cp /etc/step-ca-templates/federation.tpl /home/step/templates/federation.tpl
echo "[step-ca init] Federation X.509 template copied to /home/step/templates/."
fi
echo "[step-ca init] Startup complete."
else
echo "[step-ca init] Config already exists — skipping init."
fi
echo "[step-ca init] Starting Step-CA on :9000..."
exec step-ca /home/step/config/ca.json --password-file "${PASSWORD_FILE}"

View File

@@ -0,0 +1,48 @@
{
"subject": {{ toJson .Subject }},
"sans": {{ toJson .SANs }},
{{- /*
Mosaic Federation X.509 Certificate Template
============================================
This template is used by the "mosaic-fed" JWK provisioner to sign
federation client certificates.
Custom OID extensions (per PRD §6):
1.3.6.1.4.1.99999.1 mosaic.federation.grantId (UUID string)
1.3.6.1.4.1.99999.2 mosaic.federation.subjectUserId (UUID string)
TODO (M2-04): Wire actual OID extensions below once the CA service
(apps/gateway/src/federation/ca.service.ts) lands the SAN-bearing CSR
work and the template can be exercised end-to-end.
Step-CA template reference:
https://smallstep.com/docs/step-ca/templates
Expected final shape of the extensions block (placeholder not yet
activated):
"extensions": [
{
"id": "1.3.6.1.4.1.99999.1",
"critical": false,
"value": {{ toJson (first .Token.mosaic_grant_id) }}
},
{
"id": "1.3.6.1.4.1.99999.2",
"critical": false,
"value": {{ toJson (first .Token.mosaic_subject_user_id) }}
}
],
The provisioner must pass these values in the ACME/JWK token payload
(token claims `mosaic_grant_id` and `mosaic_subject_user_id`) when
submitting the CSR. M2-04 owns that work.
*/ -}}
"keyUsage": ["digitalSignature"],
"extKeyUsage": ["clientAuth"],
"basicConstraints": {
"isCA": false
}
}