tpm-enrol: inline empty-NV first-boot enrolment (host side, TODO #11)

Builds the host side of TPM identity enrolment — the "inline single-UKI"
vehicle from docs/tpm-enrolment-design.md. A production (secure) UKI now
auto-enrols a fresh host with no client cert, then reboots into the normal
secure path.

New dracut hook files/dracut/95dot1x-macsec/tpm-enrol.sh (initqueue/settled
prio 25, BEFORE dot1x-macsec-up at 30):
  - no-op when a cert is already present in NV 0x01800001 (normal boot) or
    under rd.dot1x=0 (test path has no trustworthy identity to enrol);
  - on EMPTY NV: generate the signing key in the TPM @0x81010001 (openssl tpm2
    provider builds the CSR; key never leaves the TPM), pre-auth DHCP on the
    PHYSICAL NIC (nmcli re-manages en*, overriding the secure-mode unmanaged
    drop-in), POST CSR+context to rd.fleet.enrol_url, show the broker
    fingerprint on Plymouth, poll /status, write the issued leaf cert (DER) to
    NV, and reboot to converge.
  - Empty-NV and the secure path are mutually exclusive, so this never reorders
    the load-bearing boot sequence (invariant #2).

Wiring:
  - module-setup.sh: require+install the tpm2 key-gen/NV tools, curl, awk, and
    the openssl tpm2 PROVIDER (ossl-modules/tpm2.so, asserted present — tpm2-tss
    alone can't sign with a handle: key); install the hook.
  - diskless.pkr.hcl: install tpm2-openssl + curl; assert hook/tools/provider in
    the initramfs (invariant #9). New enrol_url var threaded to extract-artifacts.
  - extract-artifacts.sh: bake rd.fleet.enrol_url into the cmdline.
  - fleet.script: enrol-active/enrol-wait/enrol-done banner; fingerprint on the
    message line (public — states only, invariant #1).

Shell JSON build/parse/cert-extract pipeline (no jq in the initramfs) verified
end-to-end against a live broker. BENCH-VERIFY list in docs/TODO.md #11 (tpm2
provider key-ref on real/swtpm, empty-NV detect, pre-auth DHCP override, Plymouth
render, NV write + reboot convergence, prod HTTPS).
This commit is contained in:
Audiobuzz
2026-07-02 09:00:18 +00:00
parent a370f80332
commit 8e1d40a30b
7 changed files with 333 additions and 9 deletions
+5
View File
@@ -23,6 +23,10 @@ DISABLE_DOT1X=${3:-false}
# environments. Defaults to the literal placeholder so build-oci.sh's warning
# fires if it was never set.
NVMET_TRADDR=${NVMET_TRADDR:-NVMET_TRADDR}
# Enrolment broker URL baked into the cmdline (rd.fleet.enrol_url) for the inline
# empty-NV first-boot enrolment. Per-environment DNS name (the serving nginx
# reverse-proxies /enrol/ to the broker); only used by a host with no TPM cert.
ENROL_URL=${ENROL_URL:-http://boot.fleet.lab/enrol}
# Plymouth boot splash toggle (from the boot_splash Packer var). "true" bakes
# "rhgb quiet" so Plymouth shows the graphical splash; anything else bakes
# "plymouth.enable=0" for a fully verbose console boot (boot-chain debugging).
@@ -217,6 +221,7 @@ printf '%s' \
"$DOT1X_FRAGMENT" \
"rd.neednet=1 systemd.volatile=overlay " \
"rd.dot1x.nvindex=0x01800001 rd.dot1x.timeout=60 " \
"rd.fleet.enrol_url=${ENROL_URL} " \
"rd.nvmf.discover=tcp,${NVMET_TRADDR},4420 " \
"root=/dev/mapper/root rootfstype=erofs rootflags=ro " \
"systemd.verity=yes roothash=${ROOT_HASH} " \