- 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>
91 lines
3.4 KiB
Bash
Executable File
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}"
|