diff --git a/docs/TODO.md b/docs/TODO.md index 2e09803..519a2eb 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -572,13 +572,67 @@ this item builds the rollout machinery around it. stay served); security rollback via dbx-revoke / TPM monotonic counter (design §6). - **Drain & retire** an old version only when SPDK/nvmet reports zero connected - controllers on it. + controllers on it. **Drain accuracy:** account for hosts powered off mid-rollout + — an assigned-but-offline host will reconnect to a version that may be gone, so + don't remove a version while any host that could return is still assigned to it. - **Availability**: run NVMe/TCP target replicas behind distinct discovery entries (design §7) — never serve the whole fleet from one unreplicated node, especially mid-rollout (N versions + reboot bursts). - Note: kiosk *content* changes are server-side (the website) and need no fleet update — only OS/UKI/rootfs changes require a reboot. +### SPDK restart / availability is the load-bearing constraint (analysis 2026-06-25) + +SPDK sits in the **live data path of every running host's root fs**, so a process +restart isn't like restarting a stateless service — it tears down **every** +NVMe/TCP association at once (fleet-wide, NOT version-scoped). The rollout design +must be built around **never restarting the process that holds live connections.** + +- **Core principle:** a host changes root version *only by rebooting* (no live + swap). So: **version transitions = reboots; SPDK changes = additive/online; the + process stays up.** Routine rollouts never restart SPDK. +- **Online add/remove via JSON-RPC, no restart.** `nvmf_tgt` is fully + runtime-configurable (`bdev_aio_create`, `nvmf_create_subsystem`, + `nvmf_subsystem_add_ns`, `..._add_listener`). Add vY's namespaces live (existing + vX connections untouched); remove vX live once drained. **Per-version subsystem + (distinct NQN)** matters here: adding vY as a *new subsystem* never disturbs the + subsystem vX hosts are attached to — adding a *namespace* to a shared subsystem + fires a namespace-change AER + rescan at running hosts. Per-version subsystem + also gives clean per-release drain + per-release hostNQN allow-listing. +- **What a connection loss does to a running host:** root is **read-only erofs + + tmpfs overlay**. tmpfs writes (RAM) + page-cached reads keep working; **cold + reads block**. The nvme-tcp host driver reconnects (`reconnect_delay`) until + `ctrl_loss_tmo`. **Fast SPDK restart** (same NQN/UUID/addr within the window) → + host **survives with a stall**; **long/changed/expired** → root I/O errors, host + needs a reboot ("keep alive timeout / no route to host"). Read-only + + content-addressed makes reconnect unusually clean (no write/reservation state). +- **Cheap host-side mitigation:** tune `ctrl_loss_tmo` (even `-1` = retry forever) + + `reconnect_delay` at `nvme connect` time (dracut nvmf step). For a diskless box + "hang and recover" beats "die" — there's nothing better to fail to. +- **Structural answer — HA + NVMe-oF multipath (ANA):** run ≥2 targets serving + **byte-identical, same-UUID** namespaces; hosts multipath across them, so one + target can be restarted/upgraded while the other serves (no root loss). The + **read-only, content-addressed root makes replication trivially coherent** (no + write-coherence problem) — this architecture is ideally suited to it. Wrinkle: + the **immutable UKI cmdline** points at one discovery address, so multipath needs + discovery to return multiple paths (or a DNS name resolving to several targets). +- **Planned SPDK restarts only via drain/failover** (multipath fail-over, or a + maintenance window with generous `ctrl_loss_tmo` so hosts recover not die). There + is **no zero-impact single-node SPDK restart.** +- **Implementation gap (current lab):** SPDK runs as a container with + `spdk-setup.sh` as entrypoint + the artifact image mounted → effectively + **single-version, configured at container start**, so changing versions = + container restart = drop all connections. The work here is a **long-lived target + + an RPC-driven control path** ("add release / remove release / drain status") + decoupled from container lifecycle, with per-version subsystems. +- **Capacity:** each concurrently-served version costs target memory/hugepages + + file-backed bdev overhead — plan for N live versions (current + previous + + canary) during long rollouts. +- **Attach gating + discovery:** with several subsystems advertised, the dracut + connect + per-subsystem hostNQN allow-list must ensure a vX host attaches **only** + vX's namespace (the by-UUID verity ref protects *what mounts*, but gate the + *attach* too); `rd.nvmf.discover` selection must be deterministic, not connect-all. + ## [ ] 7. Harden / evaluate the NVMe-oF target (serving-node resilience) There is **no production-grade memory-safe NVMe-oF target** — the Rust NVMe work @@ -607,7 +661,13 @@ export is read-only + single-namespace = inherently small attack surface. - **Network-isolate :4420** so only the boot VLAN reaches the target (compose with 802.1X/MACsec segmentation in prod). - **Replicas** behind distinct discovery entries (availability is the real risk - — it's the fleet's disk). Ties into #6 (rolling updates) availability. + — it's the fleet's disk). The target is a **fleet-wide SPOF in every running + host's root data path**; a process restart drops *all* NVMe/TCP associations at + once. Structural fix = **HA + NVMe-oF multipath (ANA)** over byte-identical, + same-UUID, read-only namespaces (replication is coherent precisely because the + root is read-only + content-addressed); cheap mitigation = generous host + `ctrl_loss_tmo`/`reconnect_delay`. See the **"SPDK restart / availability"** + analysis under #6 (rolling updates) — same constraint, fuller treatment. - Revisit a memory-safe target only if/when an off-the-shelf one matures (e.g. a future Rust-for-Linux `nvmet`). @@ -631,6 +691,13 @@ smoke test. documented upgrade on the same CA. **BENCH-VERIFY** items flagged in the files: hostapd MACsec build, openssl tpm2 provider vs pkcs11 key ref, EAP-TLS version. PKI product decision (greenfield): start step-ca; OpenBao/EJBCA/Dogtag later. + - **First step (DONE 2026-06-22): `enrol/tpm-selftest.sh`** — non-destructive TPM + capability check (run on the booted open-port image, no `disable_dot1x` needed) + to de-risk the TPM half before the full path. Likely first finding: no openssl + TPM2 **provider** in the image (`tpm2-tss` ≠ the provider) — add `tpm2-openssl`. + - **Enrolment of the per-host identity** (key@`0x81010001` + cert@NV) is its own + design — see **#11** (TOFU + OOB fingerprint broker, pre-auth permit-set, + provisioning UKI). `tpm-provision.sh` is the manual bench stand-in for it. - **NVMe discovery by DNS:** `nvmet_traddr=nvme.fleet.lab` (CI), resolved at boot by the serving dnsmasq — drop the hardcoded `192.168.50.10`. - **Signing — SIGNED-UKI + SECURE-BOOT-ON PATH BENCH-VERIFIED on the Proxmox VM @@ -645,7 +712,9 @@ smoke test. EFI attribute prefix in a bash variable (`ATTR=$'\x27\x00\x00\x00'`). Bash strings can't hold NULs → only `0x27` survived → efivarfs read a corrupt attribute u32 → every write failed with `EINVAL` ("dd: Invalid argument"). - Now emitted directly with `printf` at write time. (Red herring on the way: + Now emitted directly with `printf` at write time. **Baked fix VERIFIED on the + 820 (2026-06-22): `sudo fleet-sb-enroll` enrolled db→KEK→PK cleanly, no inline + workaround.** (Red herring on the way: Proxmox EFI disks default to `pre-enrolled-keys=1`, so `mokutil --db` shows the MS cert even in Setup Mode — looked like a non-empty-db rejection but was the NUL bug all along; it failed identically on a clean `pre-enrolled-keys=0` @@ -657,12 +726,15 @@ smoke test. (`.gitea/workflows/dnsmasq-image.yaml`) — no longer built at deploy time, and carries no keys/binary. (Decision: separate `fleet-ipxe` image, not baked into dnsmasq, so the daemon image stays key-agnostic across SB environments.) + - **820 / PXE signed chain — BENCH-VERIFIED 2026-06-22.** Real hardware (HP + EliteBook 820 G4), Secure Boot **enforcing** (`mokutil --sb-state` = enabled, + Setup Mode no, db/KEK/PK enrolled): firmware verifies the signed `snponly.efi` + (fleet-ipxe image) → signed UKI and boots. Confirms the whole `fleet-ipxe` + signing pipeline + the PXE→iPXE→UKI chain under SB on real silicon (the VM only + covered the HTTP path, which has no iPXE stage). - **REMAINING for production:** (a) **secure path** — re-verify signed boot with - `disable_dot1x=false` (802.1X/MACsec), not just the test path; (b) **820 / PXE - path** — BENCH-VERIFY the signed `fleet-ipxe` chain on the 820 (the VM's HTTP - path has no iPXE stage, so the signed iPXE binary is still unverified on real - PXE hardware); (c) confirm signing is reproducible (verity hash stable across - two signed builds). + `disable_dot1x=false` (802.1X/MACsec), not just the test path; (b) confirm + signing is reproducible (verity hash stable across two signed builds). - **PXE-only iPXE fallback — BENCH-VERIFIED on the EliteBook 820 G4 (2026-06-20).** Firmware without UEFI HTTP boot chainloads `serving/tftp/snponly.efi` → renders `autoexec.ipxe` (ifopen + DHCP-retry + `imgargs `) → UKI over HTTP @@ -757,7 +829,9 @@ full ~17-min Packer build. Speed up bringing up new workstations: bake the **public** PK/KEK/db certs into an image, detect Secure Boot **Setup Mode** at boot, and auto-enroll them so a fresh/reset box adopts the fleet trust root without a manual firmware-setup visit. -Pairs with #8 (signing) + [secure-boot-keys.md](secure-boot-keys.md). +Pairs with #8 (signing) + [secure-boot-keys.md](secure-boot-keys.md). Shares the +provisioning-UKI + isolated-environment vehicle with **#11** (which enrols the +*network* identity); build them together. **Tooling gap (found 2026-06-20): `efitools` is NOT packaged for EL9** — no `efi-updatevar`/`efi-readvar`/`KeyTool`. Resolved for the OS-side path by writing @@ -817,3 +891,70 @@ enrolls. reasonable for a controlled fleet; the highest-assurance path is attended/firmware or manufacturer pre-provisioning. Default recommendation: **separate provisioning UKI + isolated VLAN + read-back verify.** + +## [ ] 11. (DESIGN) TPM 802.1X identity enrolment (TOFU + OOB fingerprint) + +How a host gets its **802.1X EAP-TLS client identity** the first time — the key at +TPM handle `0x81010001` + cert at NV `0x01800001` that invariant #1 says must come +from enrolment, never the image. Distinct from #10 (that enrols *Secure Boot* +PK/KEK/db); this enrols the *network identity*. They share the provisioning UKI + +isolated-environment vehicle. Pairs with #8 + the CA decision (start **step-ca**, +[serving/dot1x-bench/](../serving/dot1x-bench/); OpenBao/EJBCA/Dogtag later). + +**Chosen pattern — trust-on-first-use with out-of-band fingerprint approval:** +on an empty TPM the host generates a key *in* the TPM, builds a CSR, submits it to +an enrolment **broker**, and shows a **fingerprint** on the Plymouth splash; an +admin visually cross-checks that fingerprint against the broker's pending-list and +approves; the broker has step-ca sign the CSR and returns the cert; the host writes +it to NV `0x01800001` and proceeds to authenticate. Sound, recognised pattern +(SSH/WireGuard/Signal-style OOB verification); good fit for invariant #1. + +**Key design decisions (settled 2026-06-22):** + +- **The pre-auth permit-set already exists — enrolment rides in it.** The firmware + can't do 802.1X, so the UKI fetch (DHCP/DNS/TFTP/HTTP to the serving host) is + **always pre-auth**; the secure model therefore *requires* a pre-auth permit-set + (low-impact 802.1X / pre-auth dACL / boot→secure dynamic VLAN; RADIUS returns the + secure VLAN on Access-Accept). The enrolment endpoint is just **another entry in + that set** — expose it as an `/enrol/` location on the **existing serving nginx**, + reverse-proxied to the broker. No new network mechanism. +- **The broker is the only new component.** step-ca signs but has no human-approval + queue: broker = `POST CSR + context → PENDING + fingerprint → admin approves → + step-ca signs → host polls → cert`. Exposed to *unauthenticated* devices by + design → harden: OOB-fingerprint gate (no auto-sign), rate-limit, time-boxed + pending, HTTPS (image trusts the serving CA), pre-auth permit scoped to only + these services + the serving host. +- **Trigger on EMPTY-NV only — split the three events:** empty NV → enrol (human); + valid key + expired/near-expiry cert → **renew** automatically (no human, it + already holds a trusted key); auth fails with a present cert → **alert + retry, + do NOT re-enrol** (auto-reenrol on failure would mask revocation and churn keys + on a transient RADIUS outage). Re-enrol after the first time = explicit admin + action (clear/flag the host). +- **Fingerprint: what it proves + entropy.** It proves "this CSR came from *this + physical device*" (approver reads the screen) — it does NOT prove TPM residency. + Pair it with **TPM attestation** (step-ca ACME `device-attest-01`, EK/AK) for the + residency proof: fingerprint = human authorises the device, attestation = the key + is really in a TPM (together → strong SSP R-01 story). Size for entropy — a short + hex fp is grindable; use a word-list/BIP39-style encoding (reads short, more + bits) + rate-limiting + a server nonce. Show device context (asset/port/location) + so the admin knows *what* they're approving. +- **Pre-auth traffic has no MACsec (plaintext at L2).** Fine for enrol (CSR/cert + are public; OOB fingerprint gives authenticity) — and it's exactly **why the UKI + must be Secure-Boot-signed** (#8): integrity of the boot comes from the signature, + not the network. Enrolment design + Secure Boot are two halves of the same + "pre-auth path is hostile" assumption. +- **Run it in a provisioning UKI/mode, not the production initramfs** — the boot + hook ordering is load-bearing (invariant #2); don't park it on a multi-minute + human-approval wait. Shares the #10 provisioning-UKI vehicle. + +**Bench caveat:** `hostapd` wired is strictly port-based (no pre-auth per-flow +ACL) — before EAP success it passes nothing, so the bench can't reproduce +"boot + enrol pre-auth on the same port." Model the pre-auth state with a separate +provisioning bridge/VLAN on the rig; the in-place low-impact pre-auth permit is a +real-switch feature to validate on the 820's switch. + +**First step DONE (2026-06-22):** `serving/dot1x-bench/enrol/tpm-selftest.sh` — a +non-destructive TPM capability check (device, key persist, NV roundtrip, openssl +TPM2 provider, CSR signed by the TPM key) to de-risk the TPM half before any of +this. Expected gap: the image installs `tpm2-tss` but no openssl TPM2 **provider** +(`tpm2-openssl`) — add it to the build once the self-test confirms. diff --git a/docs/design-v2-errata.md b/docs/design-v2-errata.md index 5c7e131..6d105c2 100644 --- a/docs/design-v2-errata.md +++ b/docs/design-v2-errata.md @@ -4,7 +4,7 @@ Records where the as-built implementation diverges from `diskless-fleet-design-v2.pdf`. The PDF remains authoritative for the architecture; this note captures deltas discovered bringing the build up on **Rocky 9.8** until they are folded into a consolidated **v3**. Last updated -2026-06-15. +2026-06-22. The architecture is unchanged: 802.1X EAP-TLS with a TPM-held key, MACsec on the access link, dm-verity-protected erofs root over NVMe/TCP, signed UKI, and the @@ -57,6 +57,43 @@ indices shift with the transient discovery controller. [../serving/spdk-setup.sh](../serving/spdk-setup.sh) (2nd bdev/namespace), and CLAUDE.md invariant #9. +## 1c. Design-level addition — pre-auth boot path & per-host identity enrolment + +Two design points the PDF leaves implicit; both are load-bearing for the secure +model and are recorded here pending v3. + +**The boot path is inherently pre-auth, so a pre-auth permit-set is mandatory.** +The firmware (UEFI HTTP boot / PXE→iPXE) fetches the UKI **before** any OS or +supplicant exists — there is no firmware 802.1X supplicant — so the UKI/initramfs +fetch always happens in the unauthenticated state. The secure model therefore +requires the authenticator to permit a *scoped* set of services pre-auth +(DHCP/DNS + TFTP + HTTP to the serving host) and only complete 802.1X once the +**OS** is running; RADIUS returns the secure VLAN on Access-Accept, after which +DHCP-for-the-running-system and the NVMe/TCP root ride **`macsec0`** (post-auth, +encrypted). Mechanism on a real switch: low-impact 802.1X / a pre-auth dACL / +boot→secure dynamic VLAN. Consequences: +- Pre-auth traffic has **no MACsec** (plaintext at L2). Integrity of the boot + therefore comes from **Secure Boot** (the signed UKI + signed `fleet-ipxe`), not + the network — this is *why* signing matters: the boot transport is untrusted. +- `hostapd` wired (the bench authenticator) is strictly port-based and can't model + a pre-auth per-flow permit — the in-place pre-auth ACL is a real-switch feature, + validated on the 820's switch, not the VM rig (see `serving/dot1x-bench/`). + +**Per-host 802.1X identity is created by an enrolment flow, never baked in** +(invariant #1). Chosen pattern: **trust-on-first-use with out-of-band fingerprint +approval** — on an empty TPM the host generates a key *in* the TPM +(`0x81010001`), submits a CSR to an enrolment **broker** (reachable via the same +pre-auth permit-set, e.g. an `/enrol/` location on the serving nginx → broker → +step-ca signer), and shows a fingerprint on the Plymouth splash; an admin +cross-checks it and approves; the signed cert is written to NV `0x01800001` and +authentication proceeds. Enrol triggers on **empty-NV only** (expired cert → +automatic renew; auth-failure-with-cert → alert, not re-enrol). The fingerprint +authorises *which device*; **TPM attestation** (step-ca ACME `device-attest-01`) +proves *key residency* — use both for full assurance. Runs in a provisioning +UKI/mode, not the production initramfs. Full design + decisions: **TODO #11** (the +network-identity counterpart to **#10**, which enrols Secure Boot PK/KEK/db; both +share the provisioning-UKI vehicle). + ## 2. Implementation notes (not architectural) These don't contradict the PDF (it doesn't specify them) but are worth recording. diff --git a/docs/secure-boot-keys.md b/docs/secure-boot-keys.md index da64f6b..da02273 100644 --- a/docs/secure-boot-keys.md +++ b/docs/secure-boot-keys.md @@ -154,10 +154,14 @@ Mode failure (that surfaces as EACCES / "Permission denied"). Diagnose top-down: `pre-enrolled-keys=0` disk if the NUL bug is present. For a clean OVMF test bed: `qm set --efidisk0 :1,efitype=4m,pre-enrolled-keys=0`. -**Verified:** with the fix, a Proxmox VM enrols db→KEK→PK from the baked `.auth` -files and boots the CI-signed UKI under **enforcing Secure Boot** over the HTTP -boot path (`mokutil --sb-state` = enabled). The PXE/iPXE front-end additionally -needs `snponly.efi` signed with `db` — see the iPXE section below. +**Verified end-to-end on real hardware (2026-06-22):** `fleet-sb-enroll` (the +baked, fixed script) enrols db→KEK→PK cleanly, and both boot transports verify +under **enforcing Secure Boot** (`mokutil --sb-state` = enabled): +- **Proxmox VM — HTTP boot:** firmware → signed UKI (2026-06-20). +- **HP EliteBook 820 G4 — PXE boot:** firmware → signed `snponly.efi` (fleet-ipxe) + → signed UKI (2026-06-22). +So the full signing pipeline (UKI + `fleet-ipxe`, both against the same enrolled +`db`) is proven on real silicon, not just in the VM. ## 4. Wire `db` into the build diff --git a/docs/ssp-annex-a-ism-residual-risk.md b/docs/ssp-annex-a-ism-residual-risk.md index 0ee117e..bc59136 100644 --- a/docs/ssp-annex-a-ism-residual-risk.md +++ b/docs/ssp-annex-a-ism-residual-risk.md @@ -9,10 +9,12 @@ > **A&A framework:** ASD *Information Security Manual* (ISM) — engagement target > **May 2026 release**. > -> **Annex version:** 0.2 (2026-06-20). v0.2 reflects: STIG hardening applied + -> bench-verified, USBGuard device control enabled, admin `staff_u` confinement, -> audit offload verified, UKI signing wired (not yet enforced — new R-13), -> PXE/iPXE boot path added. +> **Annex version:** 0.2 (updated 2026-06-22). v0.2 reflects: STIG hardening +> applied + bench-verified, USBGuard device control enabled, admin `staff_u` +> confinement, audit offload verified, PXE/iPXE boot path added, and **UEFI Secure +> Boot signing verified enforcing on real hardware** (VM HTTP + 820 PXE) — R-13 +> reduced from Medium to Low (residual is making signed + SB-on the standing +> default). --- @@ -150,7 +152,7 @@ ISM/PSPF style). Ratings are **pre-treatment** unless stated; the AO accepts the | Topic | ISM (rep.) | Requirement intent | System implementation | Status | Residual | |---|---|---|---|---|---| | Media sanitisation / no data remanence | ISM-0347*, ISM-1218* | Prevent data remanence. | **Diskless** — no persistent local media; writable layer is **RAM tmpfs**, gone at power-off. | Implemented | — | -| Tamper-evidence / boot integrity | ISM-1542*, ISM-0312* | Detect firmware/boot tampering. | Design: **UEFI Secure Boot** + signed UKI (+ signed iPXE on the PXE path) + immutable signed cmdline + dm-verity; TPM measurements available. Signing **wired in CI** + key/enrolment procedure + baked `fleet-sb-enroll` helper, but **lab runs unsigned with SB OFF**. | Partial | **R-13** (signing not enforced), R-11 (no remote attestation) | +| Tamper-evidence / boot integrity | ISM-1542*, ISM-0312* | Detect firmware/boot tampering. | Design: **UEFI Secure Boot** + signed UKI (+ signed iPXE on the PXE path) + immutable signed cmdline + dm-verity; TPM measurements available. **Verified enforcing on real hardware (2026-06-22):** CI-signed UKI under SB on a VM (HTTP) and signed iPXE→UKI under SB on an 820 (PXE), `db` enrolled via `fleet-sb-enroll`. Residual is making signed + SB-on the standing default for all builds/hosts. | Implemented (default-posture residual) | **R-13** (not yet default), R-11 (no remote attestation) | | Device identification | ISM-1195* | Uniquely identify devices. | Identity derived from TPM cert (hostnqn from cert serial); no spoofable per-host config. | Implemented | — | ### 2.11 Guidelines for Procurement, Outsourcing & Evaluated Products @@ -176,7 +178,7 @@ asked to accept. Treatments map to [docs/TODO.md](TODO.md) (working POA&M). | **R-05** | **NOPASSWD key-only admin**; no per-host or rotatable access model; human admin MFA on the mgmt plane not enforced. | 2.4 (ISM-1507*/1173*) | Possible × Moderate → **Medium** | Single fleet-generic admin, key-only, root disabled, ECDSA/RSA key, **now SELinux-confined to `staff_u`** with sudo role/type elevation, fully audited; no interactive password to attack. | Per-host/rotatable access (SSH CA / config-agent) + admin MFA on the management plane. | **Low–Medium** | Platform team — *roadmap* | | **R-06** | **FIPS off**; ISM **AACA/AACP conformance not formally asserted**; crypto-module evaluation status unconfirmed. | 2.9 (ISM-0471*/1139*/1277*) | Possible × Major → **Medium** | Modern STIG crypto policy; non-exportable TPM key; no deliberately weak algorithms. Acceptable at OFFICIAL; **mandatory to resolve for PROTECTED+.** | Assert AACA/AACP conformance; decide on FIPS module / evaluated crypto alongside Secure Boot signing. | **Low** (at appropriate classification) | Security + Platform — *before PROTECTED+* | | **R-07** | Audit-log **offload plaintext**, **single-site**, **no SIEM alerting**; logs interceptable/repudiable and not actively reviewed. (Transport **bench-verified working** 2026-06-20.) | 2.6 (ISM-1432*/0109*/0859*) | Possible × Moderate → **Medium** | Isolated LAN; disk-assisted queue (no loss on collector blip); auditd local copy; per-source files; LinuxAudit captures USBGuard events. | rsyslog **TLS** (gtls/6514) + bundle CA in image (re-select the 3 waived `rsyslog_encrypt_offload_*` rules); define retention + integrity; forward to SIEM with alerting. | **Low** | Platform/SecOps — *production* | -| **R-13** | **Boot chain unsigned / Secure Boot OFF** in the lab — the UKI and (on the PXE path) iPXE are not signed and SB is not enforced, so a substituted boot artifact would not be rejected by firmware. | 2.10 (ISM-1542*/0312*) | Possible × Major → **Medium** | dm-verity still rejects a tampered *root*; isolated lab LAN; signing is **wired in CI** + key/enrolment procedure + baked `fleet-sb-enroll` ready; only the enforcement is pending. | Enrol fleet `db` (PK/KEK/db per [secure-boot-keys.md](secure-boot-keys.md)); set `SB_KEY`/`SB_CERT` CI secrets; `sbsign` the UKI **and** iPXE; enable Secure Boot on targets. | **Low** | Platform team — *before production* | +| **R-13** | **Boot-chain signing not yet the standing default** — signing + Secure Boot enforcement are **verified end-to-end on real hardware** (VM HTTP UKI 2026-06-20; 820 PXE signed iPXE→UKI 2026-06-22, SB enforcing, `db` enrolled), but unsigned/SB-OFF lab builds remain possible (CI signs only when `SB_KEY`/`SB_CERT` are set) and not every host has `db` enrolled, so a substituted artifact could still boot on an un-enrolled/unsigned host. | 2.10 (ISM-1542*/0312*) | Unlikely × Major → **Low** | Capability **proven on hardware**; dm-verity rejects a tampered *root* regardless; isolated lab LAN. | Make signed + Secure-Boot-on the **default for all production builds/hosts**: keep `SB_KEY`/`SB_CERT` CI secrets set; enrol `db` fleet-wide; treat an unsigned build as lab-only. | **Low** | Platform team — *before production* | | **R-08** | **Placeholder RADIUS CA** (`files/etc/dot1x/radius-ca.pem`) — real EAP-TLS trust anchor not provisioned; auth cannot succeed and trust is unestablished. | 2.4 (ISM-1546*) | Certain (lab) × Major → **Medium** | Documented as a placeholder; environment-specific provisioning is the intended path; no real CA committed. | Provision the environment's real RADIUS CA out-of-band (not committed unless classification permits). | **Low** | Environment owner — *before production* | | **R-09** | **Host firewall disabled**; host exposes more network surface than necessary. | 2.7 (ISM-1416*) | Possible × Moderate → **Low–Medium** | Isolated LAN; minimal services; egress controlled at the network/serving layer; no kiosk inbound. | Re-enable firewalld with explicit allow rules (NVMe/TCP `:4420`, DHCP, log offload, SSH) once the secure path is verified. | **Low** | Platform/network — *after R-01* | | **R-10** | **Patch SLA undefined** — no documented cadence for rebuilding/redeploying against new CVEs. | 2.5 (ISM-1144*/1472*) | Likely × Moderate → **Medium** | Atomic rebuild/redeploy model makes patching fast & uniform; reproducible builds. | Define patch cadence & critical-CVE SLA; automate rebuild triggers. | **Low** | Platform team — *roadmap* | @@ -204,7 +206,9 @@ R-12 and R-13** (and confirming R-03's `enable_stig=true`) are **closed before production**, and R-04/R-05/R-06/R-07/R-09/R-10/R-11 are either accepted with compensating controls (isolated-LAN lab) or carried on the POA&M with target dates. Newly strengthened in v0.2: device control (USBGuard, now Implemented), -admin SELinux confinement (R-05), and STIG applied+verified (R-03 reduced).* +admin SELinux confinement (R-05), STIG applied+verified (R-03 reduced), and +**Secure Boot signing verified enforcing on real hardware (R-13 reduced +Medium→Low)**.* --- @@ -224,9 +228,11 @@ admin SELinux confinement (R-05), and STIG applied+verified (R-03 reduced).* anything flagged **REVIEW** is an unjustified finding. 6. **Confirm lab-only flags are off** in the assessed build: `debug_shell=false`, `enable_stig=true`, `disable_dot1x=false`, real RADIUS CA in place. -7. **Confirm boot-chain signing is enforced** (R-13): `db` enrolled, Secure Boot - ON, UKI + iPXE signed (`sbverify`). The PXE/iPXE path's SB-OFF cmdline - workaround should be a no-op once SB is on. +7. **Confirm boot-chain signing is the default** (R-13): signing + Secure Boot + enforcement are **verified on hardware** (VM HTTP + 820 PXE, 2026-06-22) — + confirm the *assessed* build is signed and the *assessed* hosts have `db` + enrolled with Secure Boot ON (`mokutil --sb-state`, `sbverify` UKI + iPXE). The + PXE/iPXE SB-OFF cmdline workaround is a no-op once SB is on. 8. **Witness device control** (USBGuard): a USB mass-storage device is blocked and logged, console HID still works. diff --git a/docs/ssp.md b/docs/ssp.md index 0858114..e4a3223 100644 --- a/docs/ssp.md +++ b/docs/ssp.md @@ -16,9 +16,9 @@ | System name | Diskless Fleet Boot System ("the fleet") | | Document type | System Security Plan (SSP) | | Version | 0.2 (DRAFT — pre-assessment) | -| Date | 2026-06-20 | +| Date | 2026-06-22 | | Author / maintainer | System Owner (delegate) | -| Status | Lab / pre-production. STIG hardening applied + bench-verified (unsigned, Secure Boot OFF); secure path (802.1X/MACsec) not yet bench-verified. | +| Status | Lab / pre-production. STIG hardening applied + bench-verified; **UEFI Secure Boot signing verified enforcing on real hardware** (VM HTTP + EliteBook 820 PXE); secure path (802.1X/MACsec) not yet bench-verified. | | Companion documents | [Annex A — ISM control cross-reference & residual risk](ssp-annex-a-ism-residual-risk.md); [Architecture design v2](diskless-fleet-design-v2.pdf); [Design errata](design-v2-errata.md); [STIG deviation register](stig-deviations.md); [Backlog / POA&M source](TODO.md) | > **Currency caveat (read before relying on control numbers).** This SSP and its @@ -164,19 +164,23 @@ This system follows the ISM A&A lifecycle: Milestones (POA&M)** (this repo's [TODO.md](TODO.md) is the working POA&M source). -**Current status:** Pre-assessment **DRAFT (v0.2)**. The system is functional in a -lab configuration. **Progress since v0.1 (2026-06-20):** DISA STIG hardening -applied and **bench-verified** on a real build (kiosk, key-only SSH, audit -offload, DHCP-named host, NVMe/verity root all good with hardening on); USBGuard -device control enabled + verified; admin confined to `staff_u`; UKI signing wired -in CI + Secure Boot key/enrolment procedure documented; both **HTTP-boot and -PXE/iPXE** boot paths working (the latter verified on an EliteBook 820 G4). -Several controls remain **partial or deferred**, tracked as residual risks with -treatments (Annex A §3). The two largest gates on a production ATO: (a) the -secure path (**802.1X/MACsec**) is implemented but **not yet bench-verified** and -is disabled in test builds (`disable_dot1x=true`, R-01); (b) the lab runs -**unsigned with Secure Boot OFF** (R-13) and with a lab-only **passwordless-root -debug shell** that must be removed (R-02). +**Current status:** Pre-assessment **DRAFT (v0.2, updated 2026-06-22)**. The system +is functional in a lab configuration. **Progress since v0.1 (2026-06-20):** DISA +STIG hardening applied and **bench-verified** on a real build (kiosk, key-only SSH, +audit offload, DHCP-named host, NVMe/verity root all good with hardening on); +USBGuard device control enabled + verified; admin confined to `staff_u`; and — +newly — **UEFI Secure Boot signing verified enforcing end-to-end on real +hardware**: the CI-signed UKI boots under Secure Boot on a VM (HTTP boot), and the +signed iPXE→UKI chain boots under Secure Boot on an EliteBook 820 G4 (PXE boot), +with `db`/`KEK`/`PK` enrolled via the baked `fleet-sb-enroll` helper. Several +controls remain **partial or deferred**, tracked as residual risks with treatments +(Annex A §3). The largest remaining gates on a production ATO: (a) the secure path +(**802.1X/MACsec**) is implemented but **not yet bench-verified** and is disabled +in test builds (`disable_dot1x=true`, R-01); (b) a lab-only **passwordless-root +debug shell** must be removed (R-02). Boot-chain signing / Secure Boot — a major +v0.1 gate — is now **demonstrated on hardware**; the residual (R-13) is reduced to +making signed + Secure-Boot-on the **standing default** for every production +host/build. ## 5. Security control implementation — by ISM cyber security principle @@ -281,7 +285,7 @@ production ATO: | R-07 | Audit-log offload **plaintext** (bench-verified working), single-site, no SIEM alerting. | **Medium** | Treat — TLS offload + monitoring on roadmap. | | R-08 | **Placeholder RADIUS CA** in the repo; real trust anchor not yet provisioned. | **Medium** | Treat — environment-specific, before production. | | R-09 | Host **firewall disabled** (workstation). | **Low–Medium** | Accept (isolated LAN) / treat with explicit allow-rules. | -| R-13 | **Boot chain unsigned / Secure Boot OFF** in the lab (UKI + iPXE); CI signing wired but not enforced. | **Medium** | Treat — enrol `db`, enforce SB + sign UKI & iPXE before production. | +| R-13 | **Boot-chain signing not yet the standing default.** Signing + Secure Boot enforcement **verified end-to-end on hardware** (VM HTTP UKI 2026-06-20; 820 PXE signed iPXE→UKI 2026-06-22), but unsigned/SB-OFF lab builds are still possible (CI signs only when `SB_KEY`/`SB_CERT` are set) and not all hosts have `db` enrolled. | **Low** | Treat — make signed + Secure-Boot-on the default for all production builds/hosts (set CI secrets; enrol `db` fleet-wide). | Each is detailed, ISM-cross-referenced, rated and assigned a treatment in Annex A.