ssh-hostcert + enrol-broker: fix defects surfaced by the F-02 bench
F-02/P-17 SSH host-cert flow was proven end-to-end on comet-beacon-54
(2026-07-11); these fix the correctness/scoping/cleanup defects the bench
surfaced:
- ssh-hostcert: derive the FQDN principal <hostname>.<domain> from the DHCP
search domain at runtime (resolv.conf), alongside the short hostname, so an
admin `@cert-authority *.<domain>` line accepts by-FQDN logins instead of
"name is not a listed principal". Never baked (invariant #1). + tests.
- ssh-ca-bootstrap.sh: export the SSH host CA public key in OpenSSH wire format
(ssh-keygen -i -m PKCS8), not the PEM/PKCS8 that `step crypto keypair` writes
-- a PEM @cert-authority anchor is unparseable and silently falls back to TOFU.
- ssh-ca-bootstrap.sh: fail loudly (and verify enableSSHCA=true) if the
provisioner SSH-enable step fails, instead of swallowing it and shipping a CA
that 502s every runtime sign.
- dot1x-bench: point the co-located broker at https://localhost:8443, not the
fleet dnsmasq name ca.fleet.lab (unresolvable from the broker's host netns ->
"no such host" at sign time). localhost is in the CA cert SAN.
- serving: remove the mock-signer enrol-broker copy -- two host-networked
brokers collide on 9090/9443 and the mock issued untrusted certs. The real
broker lives with step-ca (dot1x-bench); nginx /enrol/ still proxies to it on
loopback.
- enrol-broker: retire the HTML admin console; the fleet-manager passkey SPA now
drives the mTLS JSON API (/pending, /approve|deny). Removes a weaker,
client-cert-only parallel human-approve path.
Design-doc bench note deferred: docs/ssh-host-certificate-design.md lives on the
secure-ntp branch (fbb8100), not here -- to be added there / on reconcile.
This commit is contained in:
@@ -193,50 +193,15 @@ services:
|
||||
# For HTTPS boot (production), terminate TLS here with the boot-infra CA's
|
||||
# cert and switch the dhcp-boot URL above to https://. Left HTTP for the lab.
|
||||
|
||||
# --- TPM enrolment broker (TEMPORARY home — see docs/TODO.md #11) -----------
|
||||
# The `http` nginx already reverse-proxies /enrol/ -> 127.0.0.1:9090 (this
|
||||
# broker's public listener), so an empty-NV host can POST its CSR and poll for
|
||||
# the signed cert via the normal serving host. For now the broker runs HERE so
|
||||
# a single `compose up` in serving/ brings the enrolment endpoint up.
|
||||
#
|
||||
# ⚠ PROPER HOME is its own stack co-located with step-ca (the fleet CA), i.e.
|
||||
# serving/dot1x-bench/, since REAL signing needs that CA. SPLIT THIS BACK OUT
|
||||
# once the CA lives beside it. Do NOT run this AND dot1x-bench's enrol-broker on
|
||||
# the same host — they collide on 9090/9443.
|
||||
#
|
||||
# Defaults below are the zero-dependency LAB path: `mock` signer (ephemeral
|
||||
# in-process CA — certs are NOT trusted by RADIUS/NVMe) + plaintext loopback
|
||||
# admin. For REAL enrolment, in .env set ENROL_SIGNER=step + point
|
||||
# ENROL_STEP_CA_* at a reachable step-ca, and set ENROL_ADMIN_INSECURE=false
|
||||
# after generating the admin mTLS PKI (a DEDICATED admin CA, not the fleet root):
|
||||
# ./enrol-broker/admin-pki.sh ./enrol-pki/broker <admin-name>
|
||||
# then import enrol-pki/broker/clients/<admin>.p12 and open https://127.0.0.1:9443/
|
||||
enrol-broker:
|
||||
build: ./enrol-broker
|
||||
network_mode: host # public on 127.0.0.1:9090 (nginx proxies /enrol/)
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
BROKER_PUBLIC_ADDR: "127.0.0.1:9090" # nginx /enrol/ proxies here
|
||||
BROKER_ADMIN_ADDR: "127.0.0.1:9443" # mgmt/loopback ONLY — never the boot LAN
|
||||
BROKER_SIGNER: ${ENROL_SIGNER:-mock}
|
||||
BROKER_PENDING_TTL: ${ENROL_PENDING_TTL:-10m}
|
||||
BROKER_WORDS: ${ENROL_WORDS:-8}
|
||||
HOSTNQN_BASE: ${ENROL_HOSTNQN_BASE:-nqn.2026-06.org.example.fleet:host-}
|
||||
# --- step signer (only consulted when ENROL_SIGNER=step) ---
|
||||
STEP_CA_URL: ${ENROL_STEP_CA_URL:-}
|
||||
STEP_CA_ROOT: ${ENROL_STEP_CA_ROOT:-/pki/root_ca.crt}
|
||||
STEP_PROVISIONER: ${ENROL_STEP_PROVISIONER:-enrol}
|
||||
STEP_PROVISIONER_PASSWORD_FILE: ${ENROL_STEP_PASSWORD_FILE:-/pki/password}
|
||||
STEP_CERT_NOT_AFTER: ${ENROL_CERT_NOT_AFTER:-720h}
|
||||
# --- admin mTLS (set ENROL_ADMIN_INSECURE=false for real mTLS) ---
|
||||
BROKER_ADMIN_INSECURE: ${ENROL_ADMIN_INSECURE:-true}
|
||||
BROKER_ADMIN_CERT: ${ENROL_ADMIN_CERT:-/pki/broker/server.crt}
|
||||
BROKER_ADMIN_KEY: ${ENROL_ADMIN_KEY:-/pki/broker/server.key}
|
||||
BROKER_ADMIN_CLIENT_CA: ${ENROL_ADMIN_CLIENT_CA:-/pki/broker/admin-ca.crt}
|
||||
volumes:
|
||||
# CA root + provisioner password (step signer) and admin mTLS certs live
|
||||
# here; unused/empty for the default mock+insecure path. Gitignored.
|
||||
- ./enrol-pki:/pki:ro
|
||||
# --- TPM enrolment broker — NOT defined here (lives in the CA stack) --------
|
||||
# The broker's home is co-located with step-ca (the fleet CA), i.e.
|
||||
# serving/dot1x-bench/, since real signing needs that CA. A `mock`-signer
|
||||
# convenience copy used to run here too, but two host-networked brokers collide
|
||||
# on 9090/9443 and the mock copy issued certs RADIUS/NVMe don't trust — so it was
|
||||
# removed (bench 2026-07-11; see docs/ssh-host-certificate-design.md). The `http`
|
||||
# nginx still reverse-proxies /enrol/ -> 127.0.0.1:9090; bring up
|
||||
# serving/dot1x-bench on the same host (its broker binds that loopback port) to
|
||||
# serve enrolment. Do NOT re-add a second broker here.
|
||||
|
||||
# --- Kiosk SPA (spaceship terminal shown by the diskless kiosk) ------------
|
||||
# Svelte + Tailwind app built into a static bundle served by its own nginx.
|
||||
|
||||
Reference in New Issue
Block a user