Files
stack/infra/step-ca/init.sh
Jarvis 9f5c28c0ce feat(federation): Step-CA client service for grant certs (FED-M2-04)
- Add CaService (@Injectable) that POSTs CSRs to step-ca /1.0/sign over
  HTTPS with a pinned CA root cert; builds HS256 OTT with custom claims
  mosaic_grant_id and mosaic_subject_user_id plus step.sha CSR fingerprint
- Add CaServiceError with cause + remediation for fail-loud contract
- Add IssueCertRequestDto and IssuedCertDto with class-validator decorators
- Add FederationModule exporting CaService; wire into AppModule
- Replace federation.tpl TODO placeholder with real step-ca Go template
  emitting OID 1.3.6.1.4.1.99999.1 (grantId) and .2 (subjectUserId) as
  DER UTF8String extensions (tag 0x0C, length 0x24, base64-encoded value)
- Update infra/step-ca/init.sh to patch mosaic-fed provisioner config with
  templateFile path via jq on first boot (idempotent)
- Append OID assignment registry and CA env var table to docs/federation/SETUP.md
- 11 unit tests pass: happy path, certChain fallbacks, HTTP 401/4xx, malformed
  CSR (no HTTP call), non-JSON response, connection error, JWT claim assertions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 22:34:05 -05:00

91 lines
3.4 KiB
Bash
Executable File

#!/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.
# 3. Copies the federation X.509 template into the CA config directory.
# 4. Patches the mosaic-fed provisioner entry in ca.json to reference the
# template via options.x509.templateFile (using jq — must be installed
# in the container image).
#
# 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 emits custom OID extensions:
# 1.3.6.1.4.1.99999.1 — mosaic_grant_id
# 1.3.6.1.4.1.99999.2 — mosaic_subject_user_id
set -e
CA_CONFIG="/home/step/config/ca.json"
PASSWORD_FILE="/run/secrets/ca_password"
TEMPLATE_SRC="/etc/step-ca-templates/federation.tpl"
TEMPLATE_DEST="/home/step/templates/federation.tpl"
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.
if [ -f "${TEMPLATE_SRC}" ]; then
mkdir -p /home/step/templates
cp "${TEMPLATE_SRC}" "${TEMPLATE_DEST}"
echo "[step-ca init] Federation X.509 template copied to ${TEMPLATE_DEST}."
else
echo "[step-ca init] WARNING: Template source ${TEMPLATE_SRC} not found — skipping copy."
fi
# Wire the template into the mosaic-fed provisioner via jq.
# This is idempotent: the block only runs once (first boot).
#
# jq filter: find the provisioner entry with name "mosaic-fed" and set
# .options.x509.templateFile to the absolute path of the template.
# All other provisioners and config keys are left unchanged.
if [ -f "${TEMPLATE_DEST}" ] && command -v jq > /dev/null 2>&1; then
echo "[step-ca init] Patching mosaic-fed provisioner with X.509 template..."
TEMP_CONFIG="${CA_CONFIG}.tmp"
jq --arg tpl "${TEMPLATE_DEST}" '
.authority.provisioners |= map(
if .name == "mosaic-fed" then
.options.x509.templateFile = $tpl
else
.
end
)
' "${CA_CONFIG}" > "${TEMP_CONFIG}" && mv "${TEMP_CONFIG}" "${CA_CONFIG}"
echo "[step-ca init] Provisioner patched."
elif ! command -v jq > /dev/null 2>&1; then
echo "[step-ca init] WARNING: jq not found — skipping provisioner template patch."
echo "[step-ca init] Install jq in the step-ca image to enable automatic template wiring."
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}"