From d2b2156a22eb4c9fe4d720895b77dac68c05f455 Mon Sep 17 00:00:00 2001 From: Audiobuzz Date: Sat, 20 Jun 2026 12:16:37 +0000 Subject: [PATCH] Fixed the secure boot enrollment script --- docs/TODO.md | 30 ++++++++- docs/secure-boot-keys.md | 29 ++++++++ docs/ssp-annex-a-ism-residual-risk.md | 38 +++++++---- docs/ssp.md | 97 +++++++++++++++++++-------- docs/stig-deviations.md | 24 +++++++ files/secureboot/fleet-sb-enroll | 10 ++- files/stig/diskless-tailoring.xml | 11 +++ 7 files changed, 194 insertions(+), 45 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index c18e4f6..3542115 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -622,10 +622,29 @@ smoke test. Removes `rd.dot1x=0`/`ip=dhcp`; the hook owns the link (macsec0). - **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:** wire `sb_key`/`sb_cert` (CI/HSM secrets) for a signed UKI; enable - Secure Boot on targets. Until then the loud unsigned warning must stay. +- **Signing — SIGNED-UKI + SECURE-BOOT-ON PATH BENCH-VERIFIED on the Proxmox VM + (HTTP boot, 2026-06-20).** CI signs the UKI when `SB_KEY`/`SB_CERT` secrets are + set (`db` key); `fleet-sb-enroll` enrols db→KEK→PK from the baked public `.auth` + files; OVMF then boots the signed UKI under enforcing Secure Boot. Confirmed + end-to-end: CI-signed UKI verifies (`sbverify`), enrolment succeeds, VM boots + with `mokutil --sb-state` = enabled. **Key generation + enrolment procedure: [secure-boot-keys.md](secure-boot-keys.md)** (the PK/KEK/db trio; `db` = `sb_key`/`sb_cert`, PK/KEK stay offline). + - **Bug fixed during bring-up (2026-06-20):** `fleet-sb-enroll` built its 4-byte + 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: + 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` + disk.) + - **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** — sign `snponly.efi` with the same `db` key so the iPXE front-end also + passes Secure Boot (the VM's HTTP path has no iPXE stage, so this is still + unverified on real PXE hardware); (c) 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 @@ -638,6 +657,13 @@ smoke test. (iii) iPXE aborts the script on any unguarded command failure (use `||`); (iv) Secure Boot OFF makes systemd-stub take iPXE's load options as the cmdline, so the entrypoint extracts the UKI's real `.cmdline` and feeds it via `imgargs`. + **Tidied 2026-06-20** once PortFast was identified: dropped the `autoexec.ipxe` + + `ifopen` + DHCP-retry STP workaround; `boot.ipxe` is now just + `dhcp`+`imgfetch`+`imgargs`+`boot` reached via normal iPXE autoboot. Re-verified + on the 820 G4. **SB-on simplification:** when Secure Boot is enabled the cmdline + injection is unnecessary (the stub ignores the loader cmdline) — collapse + `boot.ipxe` to a plain `chain` of the UKI and drop the `.cmdline` extraction + + `binutils` + the artifact mount from the dnsmasq service. - **Sign iPXE for the PXE-only fallback (Secure Boot, production).** The lab `snponly.efi` is **UNSIGNED** → that path needs **Secure Boot OFF**. For production: `sbsign` the iPXE binary with the fleet **db** key (only `db` needed — diff --git a/docs/secure-boot-keys.md b/docs/secure-boot-keys.md index e774b3e..aeff457 100644 --- a/docs/secure-boot-keys.md +++ b/docs/secure-boot-keys.md @@ -130,6 +130,35 @@ on a trusted/isolated network (see TODO #10). Only the **public** certs/`.esl`/`.auth` reach the workstation — never copy `PK.key`/`KEK.key`/`db.key` onto a host. +### Troubleshooting `fleet-sb-enroll` (bench, 2026-06-20) + +**`dd: ... Invalid argument` (EINVAL) on the `db` write** = the firmware's +`SetVariable` rejected the payload (EFI_INVALID_PARAMETER), *not* an auth/Setup +Mode failure (that surfaces as EACCES / "Permission denied"). Diagnose top-down: + +- **Is the `.auth` actually a signed authenticated variable?** First 16 bytes are + an `EFI_TIME`: `sudo od -An -tx1 -N16 db.auth` → a valid one starts with the + year little-endian (2026 = `ea 07`). If it starts with `a1 59 c0 a5 …` it's a + bare **ESL** (the `sign-efi-sig-list` step was skipped); `30 82 …` is a raw DER + cert. Re-create the `.auth` (step 2) and re-bake. +- **The 4-byte attribute prefix must contain its NUL bytes.** A bash *variable* + **cannot** hold NULs — `ATTR=$'\x27\x00\x00\x00'` silently truncates to the + single byte `0x27`, so efivarfs reads a corrupt attribute u32 and the kernel + returns EINVAL **before the firmware is even called**. This was a real bug in an + early `fleet-sb-enroll`; the fix emits the prefix with `printf '\x27\x00\x00\x00'` + at write time (never via a variable). If you hand-roll an enrolment, do the same. +- **Proxmox / OVMF red herring:** VM EFI disks default to `pre-enrolled-keys=1`, + so `mokutil --db` shows the **Microsoft** cert (`Windows Production PCA 2011`, + owner `77fa9abd-…`) *even in Setup Mode*. This looks like a "db not empty → + rejected" problem but isn't — the write fails identically on a clean + `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. + ## 4. Wire `db` into the build ```bash diff --git a/docs/ssp-annex-a-ism-residual-risk.md b/docs/ssp-annex-a-ism-residual-risk.md index aa2c150..0ee117e 100644 --- a/docs/ssp-annex-a-ism-residual-risk.md +++ b/docs/ssp-annex-a-ism-residual-risk.md @@ -8,6 +8,11 @@ > > **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. --- @@ -74,8 +79,9 @@ ISM/PSPF style). Ratings are **pre-treatment** unless stated; the AO accepts the | Topic | ISM (rep.) | Requirement intent | System implementation | Status | Residual | |---|---|---|---|---|---| -| Hardened OS to ASD/vendor guidance | ISM-1407*, ISM-1408* | Apply a recognised hardening baseline. | **DISA STIG** (SSG `profile_stig`, RHEL 9) via `oscap --remediate` with documented tailoring ([stig-deviations.md](stig-deviations.md)). | Partial | R-03 (CI report-only/disabled now) | -| Application control / allow-listing | ISM-0843*, ISM-1490*, ISM-1657* | Only approved executables run. | **fapolicyd** (RPM-trust + one trusted kiosk binary) + immutable dm-verity root. | Implemented | — | +| Hardened OS to ASD/vendor guidance | ISM-1407*, ISM-1408* | Apply a recognised hardening baseline. | **DISA STIG** (SSG `profile_stig`, RHEL 9) via `oscap --remediate` + documented tailoring + per-build deviations report ([stig-deviations.md](stig-deviations.md)); **applied + bench-verified 2026-06-20**, all REVIEW rows triaged. | Partial | R-03 (CI must ship `enable_stig=true` + signed/SB-on) | +| Application control / allow-listing | ISM-0843*, ISM-1490*, ISM-1657* | Only approved executables run. | **fapolicyd** (RPM-trust + two trusted local binaries: kiosk-session, fleet-sb-enroll) + immutable dm-verity root. | Implemented | — | +| Device / removable-media control | ISM-0341*, ISM-1551* | Control USB / peripheral devices. | **USBGuard enforcing** (`ImplicitPolicyTarget=block` + hardware-independent HID/hub allowlist + LinuxAudit). Verified: USB mass storage blocked, console input preserved. | Implemented | — | | Software / OS integrity | ISM-1493* | Detect unauthorised modification. | **dm-verity** continuous block-level verification of the read-only root against a signed hash; **reproducible** build. | Implemented | — | | Disable unneeded functionality | ISM-0380*, ISM-1416* | Minimise installed software/services. | `@^minimal` install; scanner tooling removed post-scan; FROM-scratch artifact image; kiosk = single app. | Implemented | — | | Hardened user applications (browser) | ISM-1486*, ISM-1412* | Lock down high-risk apps. | Chromium enterprise lockdown policy; weston kiosk; no shell/nav/plugins. | Implemented | — | @@ -89,7 +95,7 @@ ISM/PSPF style). Ratings are **pre-treatment** unless stated; the AO accepts the | Topic | ISM (rep.) | Requirement intent | System implementation | Status | Residual | |---|---|---|---|---|---| | Strong machine authentication | ISM-1546*, ISM-0974* | Mutual, certificate-based auth. | **802.1X EAP-TLS**, client key non-exportable in **TPM** (`0x81010001`), cert in TPM NV. | Partial | R-01 (not bench-verified; disabled in test builds), R-08 (placeholder CA) | -| Privileged access restricted | ISM-1507*, ISM-0445* | Limit & control admin access. | Single key-only `wheel` admin; root login disabled. | Partial | R-05 | +| Privileged access restricted | ISM-1507*, ISM-0445* | Limit & control admin access. | Single key-only `wheel` admin; root login disabled; admin **confined to `staff_u`** with a sudo `sysadm_r`/`sysadm_t` role/type transition (SELinux context elevates on privilege use). | Partial | R-05 | | Secure remote administration | ISM-1506*, ISM-1232* | Hardened admin channel. | SSH **key-only**, password & keyboard-interactive off, hardened ciphers/MACs/KEX (STIG crypto policy; ECDSA/RSA keys — ed25519 excluded by policy). | Implemented | — | | No default/shared credentials | ISM-1304* | No baked credentials. | No per-host secrets in any artifact; identity from TPM at runtime; `machine-id` empty; host keys regenerated at first boot. | Implemented | — | | MFA for privileged users | ISM-1173*, ISM-1504* | MFA on privileged access. | Machine auth is cert+TPM (strong). **Human admin MFA on the management plane not yet enforced.** | Partial | R-05 | @@ -109,7 +115,7 @@ ISM/PSPF style). Ratings are **pre-treatment** unless stated; the AO accepts the | Topic | ISM (rep.) | Requirement intent | System implementation | Status | Residual | |---|---|---|---|---|---| -| Centralised event logging | ISM-0988*, ISM-1405* | Logs centrally collected. | `audisp-syslog`→rsyslog `omfwd` (disk-assisted queue) → dedicated collector (`serving/audit-collector/`). | Partial | R-07 | +| Centralised event logging | ISM-0988*, ISM-1405* | Logs centrally collected. | `audisp-syslog`→rsyslog `omfwd` (disk-assisted queue) → dedicated collector (`serving/audit-collector/`); **bench-verified end-to-end 2026-06-20**. auditd `space_left_action`→syslog (no MTA). | Partial | R-07 | | Required events logged | ISM-0582*, ISM-1537* | Log security-relevant events. | STIG auditd rule set; kernel `audit=1`, `audit_backlog_limit=8192`. | Implemented | — | | Protect logs in transit | ISM-1432*, ISM-0407* | Encrypt/authenticate log transport. | **Plaintext** offload on isolated LAN (rsyslog TLS rules waived — see [STIG #6](stig-deviations.md)). | Waived (lab) | **R-07** | | Log retention & integrity | ISM-0859*, ISM-1815* | Retain & protect logs. | Per-source-host files on collector; retention policy & tamper-protection not yet defined. | Planned | R-07 | @@ -144,7 +150,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. | **UEFI Secure Boot** + signed UKI + immutable signed cmdline + dm-verity; TPM measurements available for attestation. | Partial | R-11 (remote attestation not yet 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) | | 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 @@ -165,11 +171,12 @@ asked to accept. Treatments map to [docs/TODO.md](TODO.md) (working POA&M). |---|---|---|---|---|---|---|---| | **R-01** | Secure path (802.1X EAP-TLS + MACsec) **implemented but not bench-verified end-to-end**; disabled in test builds (`disable_dot1x=true`). Until verified, devices may attach to an unauthenticated/unencrypted link. | 2.4, 2.7, 2.8 (ISM-1546*, 0520*, 1187*) | Likely × Major → **High** | Test path on an isolated lab LAN; full auth stack present (dormant); design follows ASD intent. | Full boot-chain bench test (swtpm + hostapd + FreeRADIUS): port `Authorized`, macsec0 addressed, MKA survives switch-root, NVMe root mounts. Re-enable in CI. | **Low** (once verified) | Platform team — *before production ATO* | | **R-02** | `debug_shell=true` bakes a **passwordless root shell** on a tty — an undocumented privileged access path. | 2.5 (ISM-1491*) | Possible × Severe → **High** | Lab-only; physical access required; isolated environment. | Set `debug_shell=false` for all non-lab builds; assert absent in CI. | **Low** | Platform team — *before production* | -| **R-03** | STIG hardening currently **report-only / disabled in CI** (`enable_stig=false`); shipped images may be unhardened. | 2.3 (ISM-1407*/1408*) | Possible × Major → **Medium** | Tailoring + register exist; SELinux/fapolicyd still on; re-enable is a single flag. | Flip `ENABLE_STIG="true"`; confirm scan evaluates >0 rules; triage REVIEW rows (buckets A/B/C in TODO #2). | **Low** | Platform team — *before assessment* | +| **R-03** | STIG hardening **applied + bench-verified** (2026-06-20, all REVIEW rows triaged); residual is ensuring **CI ships `enable_stig=true`** for production builds (it was toggled off during bring-up). | 2.3 (ISM-1407*/1408*) | Possible × Moderate → **Medium** | Tailoring + register + per-build report; SELinux/fapolicyd/USBGuard on; verified on a real hardened build. | Confirm `ENABLE_STIG="true"` in CI; keep the 0-eval guard; re-run the deviations report on the assessed build. | **Low** | Platform team — *before assessment* | | **R-04** | NVMe/TCP **root traffic plaintext** on the LAN — exposure of OS content / injection if the segment is compromised. | 2.9 (ISM-1139*/1453*) | Unlikely × Major → **Medium** | Isolated boot LAN; MACsec on the access link (once R-01 done) protects the first hop; dm-verity makes injected root blocks fail verification (integrity preserved even if confidentiality isn't). | Production: TLS/IPsec for the storage transport, or rely on MACsec end-to-end across the fabric; document the decision. | **Low–Medium** | Platform/network — *production* | -| **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, 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-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. | 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. | 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-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-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* | @@ -192,10 +199,12 @@ Likelihood ↓ ``` *The heatmap shows pre-treatment positions; every item has a treatment to Low / -Low–Medium. The AO is asked to authorise on the basis that R-01, R-02, R-03, R-08 -and R-12 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.* +Low–Medium. The AO is asked to authorise on the basis that **R-01, R-02, R-08, +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).* --- @@ -215,6 +224,11 @@ the POA&M with target dates.* 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. +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 0067c3d..0858114 100644 --- a/docs/ssp.md +++ b/docs/ssp.md @@ -15,10 +15,10 @@ |---|---| | System name | Diskless Fleet Boot System ("the fleet") | | Document type | System Security Plan (SSP) | -| Version | 0.1 (DRAFT — pre-assessment) | -| Date | 2026-06-19 | +| Version | 0.2 (DRAFT — pre-assessment) | +| Date | 2026-06-20 | | Author / maintainer | System Owner (delegate) | -| Status | Lab / pre-production. Secure path (802.1X/MACsec) not yet bench-verified. | +| Status | Lab / pre-production. STIG hardening applied + bench-verified (unsigned, Secure Boot OFF); 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 @@ -72,8 +72,19 @@ manifest. One commit = one release = one digest = one manifest entry. ### 2.2 Boot and trust chain (control flow) -1. **UEFI Secure Boot** measures and verifies the UKI signature (vendor/Secure - Boot key) before execution. +> **Boot transport (two paths, auto-selected):** HTTP-boot-capable firmware +> fetches the UKI directly over UEFI HTTP boot. Firmware **without** HTTP boot +> (e.g. HP EliteBook 820 G4) falls back to **PXE → iPXE → HTTP** (dnsmasq serves +> iPXE over TFTP, which chainloads the same UKI). Both land on the same signed +> UKI. **Lab status:** Secure Boot is currently **OFF** and the UKI/iPXE are +> **unsigned** — signing is wired in CI and the key procedure is documented +> ([secure-boot-keys.md](secure-boot-keys.md)) but not yet enforced (residual +> R-03/signing in Annex A). + +1. **UEFI Secure Boot** (when enabled) measures and verifies the UKI signature + against the enrolled `db` key before execution. On the PXE/iPXE path the iPXE + binary is likewise verified against `db`; only the fleet's own `db` need be + enrolled (no Microsoft CA / shim). 2. **UKI (systemd-stub)** carries the kernel, the `--no-hostonly` initramfs, and an **immutable, signed kernel command line** (includes the dm-verity root hash and STIG kernel parameters). No GRUB. @@ -102,7 +113,9 @@ the tmpfs overlay, the TPM-backed identity, and the hardened OS configuration. The boundary's external interfaces are: (a) the **access link** (802.1X-controlled port, MACsec-encrypted); (b) the **NVMe/TCP** root storage session; (c) **outbound audit log** forwarding; (d) the **kiosk application** endpoint the -browser renders. Each crossing is a control point addressed in Annex A. +browser renders; (e) the **boot transport** (UEFI HTTP boot, or PXE/TFTP→iPXE) +that delivers the UKI; and (f) **USB/peripheral** ports, now mediated by USBGuard +(§5.2). Each crossing is a control point addressed in Annex A. ### 2.4 Data and classification @@ -151,12 +164,19 @@ 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**. The system is functional in a lab -configuration. Several controls are **partial or deferred** and are tracked as -residual risks with treatment plans (Annex A §3). The security-critical path -(802.1X/MACsec) is **implemented but not yet bench-verified end-to-end** and is -currently disabled in test builds (`disable_dot1x=true`) — this is the single -largest gate on a production authorisation. +**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). ## 5. Security control implementation — by ISM cyber security principle @@ -180,23 +200,43 @@ summarises the implementation posture. - **Boot & OS integrity:** **Secure Boot** + **signed UKI** with an **immutable cmdline**; **dm-verity** read-only root (continuous cryptographic integrity); **erofs** immutable filesystem; **reproducible** build (verity hash is a pure - function of inputs). -- **System hardening:** **DISA STIG** baseline via OpenSCAP/SSG with a documented - tailoring; **SELinux enforcing**; **fapolicyd** application allow-listing (the - exec guard for the writable RAM overlay that dm-verity cannot cover); - **key-only SSH**, root login disabled, hardened ciphers/MACs/KEX; **kiosk - lockdown** (single locked browser, no shell/navigation). + function of inputs). Signing is **wired in CI** (`sbsign`/ukify via + `SB_KEY`/`SB_CERT` secrets) with a documented PK/KEK/db key + enrolment + procedure ([secure-boot-keys.md](secure-boot-keys.md)) and a baked, efitools-free + enrolment helper (`fleet-sb-enroll`); **not yet enforced in the lab** (SB OFF, + unsigned — see Annex A). +- **System hardening (DISA STIG applied + bench-verified 2026-06-20):** STIG + baseline via OpenSCAP/SSG with a documented tailoring + per-build deviations + report; all REVIEW rows triaged. **SELinux enforcing**; **fapolicyd** + application allow-listing (the exec guard for the writable RAM overlay dm-verity + cannot cover); **USBGuard enforcing** (`ImplicitPolicyTarget=block` + a + hardware-independent HID/hub allowlist + LinuxAudit — verified blocks USB mass + storage while keeping console input; device/removable-media control); **key-only + SSH**, root login disabled, hardened ciphers/MACs/KEX (note: STIG crypto policy + drops ed25519 → admin key must be ECDSA/RSA); **kiosk lockdown** (single locked + browser, no shell/navigation); **postfix removed** (no local MTA — audit-failure + notification rides the remote offload, not email). +- **Privileged access:** single **key-only `wheel` admin**, root locked. The admin + is now **confined to the `staff_u` SELinux user** with a sudoers role/type + transition (`sysadm_r`/`sysadm_t`) so privilege elevation re-labels the SELinux + context (STIG `selinux_context_elevation_for_sudo`). Still **NOPASSWD** (the + account is passwordless/key-only) and fleet-generic — see R-05. - **No persistent attack surface:** diskless; overlay discarded each boot; no per-host secrets in the image. -- *Gaps / decisions:* **FIPS OFF** (lab phase); **host firewall disabled**; - NVMe/TCP and audit offload **plaintext on the isolated LAN**; NOPASSWD key-only - admin; **placeholder RADIUS CA**. See Annex A residual risks. +- *Gaps / decisions:* **FIPS OFF** (lab phase); **host firewall disabled** (the + workstation; the serving host's ufw is configured); NVMe/TCP and audit offload + **plaintext on the isolated LAN**; NOPASSWD admin; **placeholder RADIUS CA**; + signing not yet enforced (SB OFF). See Annex A residual risks. ### 5.3 Detect - **auditd** enabled with STIG audit rules; kernel `audit=1` + - `audit_backlog_limit` in the signed cmdline. + `audit_backlog_limit` in the signed cmdline. USBGuard device events logged via + the LinuxAudit backend. auditd `space_left_action` repointed to **syslog** + (no local MTA), so audit-volume alerts ride the remote offload. - **Centralised log offload** via rsyslog (`audisp-syslog` → rsyslog `omfwd`, disk-assisted queue) to a dedicated **audit collector** (`serving/audit-collector/`). + **Bench-verified end-to-end 2026-06-20** (records reach the collector, + per-source-host files). - *Gaps:* offload currently **plaintext** (TLS planned, R-07); offload is **local site only**; no SIEM correlation/alerting defined yet. @@ -215,11 +255,11 @@ AO will expect a maturity view: | Mitigation | Posture | Notes | |---|---|---| -| Application control | **Strong** | fapolicyd allow-listing + immutable verified root; only RPM-trusted (plus one trusted kiosk binary) executables run. | +| Application control | **Strong** | fapolicyd allow-listing + immutable verified root; only RPM-trusted (plus two trusted local binaries: kiosk-session, fleet-sb-enroll) executables run. USBGuard enforces device control on USB ports. | | Patch applications | **Process** | Patching = rebuild + re-sign + redeploy a new digest; cadence/SLA to be defined. | | Configure MS Office macros | **N/A** | No office suite; kiosk is a locked browser. | | User application hardening | **Strong (kiosk)** | Chromium locked down via enterprise policy; no plugins/shell/nav. | -| Restrict admin privileges | **Partial** | Single key-only `wheel` admin (NOPASSWD, see R-05); no per-host/rotatable model yet. | +| Restrict admin privileges | **Partial** | Single key-only `wheel` admin, root locked, now **SELinux-confined to `staff_u`** with a sudo role/type transition (NOPASSWD, see R-05); no per-host/rotatable model or admin-MFA yet. | | Patch operating systems | **Process** | Same rebuild/redeploy model; immutable image means uniform, atomic OS updates. | | Multi-factor authentication | **Strong (machine)** | Host auth is certificate + TPM-bound key (something-the-machine-has, non-exportable). Human admin MFA to be addressed for the management plane. | | Regular backups | **N/A (by design)** | Diskless, stateless hosts hold nothing to back up; recoverability is re-provisioning from a pinned digest. Backup applies to serving-side state, out of scope here. | @@ -234,13 +274,14 @@ production ATO: |---|---|---|---| | R-01 | Secure path (802.1X/MACsec) not yet bench-verified; currently disabled in test builds. | **High** | Treat — verify before production authorisation. | | R-02 | `debug_shell=true` bakes a **passwordless root shell** on a tty. | **High** | Treat — must be removed before production (lab-only). | -| R-03 | STIG hardening temporarily **report-only / disabled in CI** (`enable_stig=false`). | **Medium** | Treat — re-enable before assessment. | +| R-03 | STIG hardening **applied + bench-verified** (2026-06-20); residual is that CI ships **unsigned, Secure Boot OFF** and `enable_stig` must be confirmed on for production builds. | **Medium** | Treat — confirm `enable_stig=true` in CI + enforce signing/SB before assessment. | | R-04 | NVMe/TCP root traffic **plaintext** on the lab LAN. | **Medium** | Accept (isolated LAN) / treat for production. | -| R-05 | NOPASSWD key-only admin; no per-host or rotatable access model. | **Medium** | Accept with compensating controls / roadmap. | +| R-05 | NOPASSWD key-only admin (now `staff_u`-confined); no per-host/rotatable access model or admin-MFA. | **Medium** | Accept with compensating controls / roadmap. | | R-06 | **FIPS off**; ASD-approved-cryptography conformance not yet asserted. | **Medium** | Treat — required for PROTECTED+; revisit with signing. | -| R-07 | Audit-log offload **plaintext**, single-site, no SIEM alerting. | **Medium** | Treat — TLS offload + monitoring on roadmap. | +| 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**. | **Low–Medium** | Accept (isolated LAN) / treat with explicit allow-rules. | +| 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. | Each is detailed, ISM-cross-referenced, rated and assigned a treatment in Annex A. diff --git a/docs/stig-deviations.md b/docs/stig-deviations.md index c187796..db28c8e 100644 --- a/docs/stig-deviations.md +++ b/docs/stig-deviations.md @@ -114,6 +114,30 @@ reverts to a justified deviation; (b) reproducibility — `semanage` rebuilds th SELinux policy store, so confirm the verity hash is stable across two builds (and normalize in 2d hygiene if it drifts). +## 13. Ctrl+Alt+Del console reboot hotkey kept enabled (decision 2026-06-20) + +The RHEL 9 STIG disables the console reboot hotkey via two rules, both **waived** +in the tailoring: +- `disable_ctrlaltdel_reboot` — masks `ctrl-alt-del.target` (single Ctrl+Alt+Del). +- `disable_ctrlaltdel_burstaction` — sets `CtrlAltDelBurstAction=none` in + `/etc/systemd/system.conf` (the rapid 7× burst reboot). + +**Why waived:** the STIG intent is to stop an **unauthenticated console user from +causing an unplanned reboot/outage**. On this fleet that risk is structurally +absent — the hosts are **diskless and stateless** (immutable erofs+dm-verity root, +tmpfs volatile overlay), so a console reboot loses nothing and persists nothing, +and a **field technician needs to recover a wedged kiosk from the keyboard when +SSH is unavailable**. The two rules are unselected so `--remediate` leaves the +systemd default (the hotkey reboots). + +**Compensating control / scope:** the hotkey only triggers a **clean stateless +reboot** — it cannot exfiltrate data, alter the read-only root, or bypass auth to +a shell. Authenticated reboot paths (`systemctl reboot` over the key-only admin +SSH session) are unaffected either way. The console itself is otherwise locked +down (kiosk autologin to a single locked browser; no getty shell on tty1; +`debug_shell` is lab-only and never shipped). If a future deployment wants the +hotkey disabled (e.g. a physically exposed terminal), re-select both rule IDs. + ## Things that are NOT deviations (kept / enforced) - **SELinux enforcing** (`selinux --enforcing`; labels preserved into the erofs). diff --git a/files/secureboot/fleet-sb-enroll b/files/secureboot/fleet-sb-enroll index c8440af..913a36d 100644 --- a/files/secureboot/fleet-sb-enroll +++ b/files/secureboot/fleet-sb-enroll @@ -28,8 +28,12 @@ EFIVARS=/sys/firmware/efi/efivars GUID_GLOBAL=8be4df61-93ca-11d2-aa0d-00e098032b8c # PK, KEK, SetupMode GUID_DB=d719b2cb-3d3a-4596-a3bc-dad00e67656f # db, dbx # EFI var attributes: NON_VOLATILE|BOOTSERVICE_ACCESS|RUNTIME_ACCESS| -# TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 0x27, little-endian 4 bytes. -ATTR=$'\x27\x00\x00\x00' +# TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 0x27, written as a 4-byte little-endian +# u32 prefixed to the .auth payload. These 4 bytes contain NULs, so they are +# emitted with printf at WRITE TIME, never stored in a variable: a bash string +# can't hold a NUL (it truncates at the first one), which would leave only the +# 0x27 byte, corrupt the 4-byte attribute field efivarfs reads, and make the +# kernel reject the write with EINVAL ("dd: Invalid argument"). die() { echo "fleet-sb-enroll: $*" >&2; exit 1; } @@ -57,7 +61,7 @@ enrol_one() { # $1=var name $2=guid $3=.auth path local f="$EFIVARS/$1-$2" tmp [ -f "$3" ] || die "missing $3" tmp=$(mktemp) - printf '%s' "$ATTR" > "$tmp" + printf '\x27\x00\x00\x00' > "$tmp" # 4-byte LE attribute prefix (see note above) cat "$3" >> "$tmp" # Existing EFI vars carry the immutable flag; clear it before overwriting. [ -e "$f" ] && chattr -i "$f" 2>/dev/null || true diff --git a/files/stig/diskless-tailoring.xml b/files/stig/diskless-tailoring.xml index 2f253b5..833a242 100644 --- a/files/stig/diskless-tailoring.xml +++ b/files/stig/diskless-tailoring.xml @@ -162,5 +162,16 @@ + + + +