Merge remote-tracking branch 'origin/main' into poam-hardening
# Conflicts: # CLAUDE.md
This commit is contained in:
@@ -36,6 +36,14 @@ files/hostapi/ Go host-info agent (fleet-hostapi): re
|
||||
loopback :9099 /v1/host for the kiosk SPA
|
||||
(HOST-API.md contract). Built in 2b-hostapi,
|
||||
fapolicyd-trusted, golang removed in 2d.
|
||||
files/ssh-hostcert/ Go SSH host-cert client (fleet-ssh-hostcert):
|
||||
at boot signs this host's per-boot SSH host
|
||||
key with the fleet SSH CA via the enrol-broker
|
||||
host plane (TPM-identity mTLS), so admins trust
|
||||
via one @cert-authority line instead of per-boot
|
||||
TOFU (F-02/P-17). Built in 2b-ssh-hostcert,
|
||||
fapolicyd-trusted; enable_ssh_hostcert flag.
|
||||
docs/ssh-host-certificate-design.md.
|
||||
scripts/extract-artifacts.sh qcow2 -> kernel/initramfs/erofs+verity/cmdline
|
||||
scripts/build-oci.sh ukify + sbsign + roothash gate + OCI image
|
||||
scripts/build-ipxe-oci.sh sbsign snponly.efi -> FROM-scratch fleet-ipxe image
|
||||
@@ -156,7 +164,11 @@ instead of working around it.
|
||||
1. **Signing keys never enter the build VM or this repo.** `sb_key` /
|
||||
`sb_cert` are CI/HSM-provided paths on the build host. Never commit key
|
||||
material, never copy keys into provisioner steps, never echo them in
|
||||
logs. Unsigned builds are lab-only and must keep their loud warning.
|
||||
logs. Unsigned builds are **local-lab-only** (a bare `packer build` without
|
||||
keys) and must keep their loud warning; **CI fails closed** — the
|
||||
`gitea-main-ci.yaml`/`ipxe-image.yaml` "Production build gate" aborts unless
|
||||
`SB_KEY`/`SB_CERT` (and `FLEET_CA` for the UKI build) are set, so no unsigned
|
||||
or placeholder-CA artifact is producible via CI (POA&M P-03/P-05).
|
||||
1. **Reproducibility of the root image is a security property.**
|
||||
`mkfs.erofs` runs with fixed UUID and `-T 0`; the verity root hash must
|
||||
be a pure function of the input tree. Don’t introduce timestamps,
|
||||
@@ -214,8 +226,10 @@ instead of working around it.
|
||||
are evaluated, and `stig-deviations.sh` flags an INVALID SCAN + exits 2. Also
|
||||
confirm the `...content_profile_stig` profile id exists in the chosen
|
||||
datastream; (b) remediation must not break
|
||||
boot/login (NM/NVMe, key-only sshd, kiosk, SELinux enforcing) — `enable_stig=false`
|
||||
is the escape hatch; (c) **reproducibility**: build twice and confirm identical
|
||||
boot/login (NM/NVMe, key-only sshd, kiosk, SELinux enforcing) — waive the
|
||||
offending rule in the tailoring as a justified deviation (STIG is now
|
||||
**enforced-true** by Packer variable validation — `enable_stig=false` is no
|
||||
longer a build path; POA&M P-03); (c) **reproducibility**: build twice and confirm identical
|
||||
`root_hash` in verity.json — if it drifts, oscap wrote something
|
||||
nondeterministic (timestamp/uuid/key); find + normalize it in 2d hygiene;
|
||||
(d) read the generated `stig-deviations.md` — every REVIEW row is a real finding
|
||||
@@ -247,13 +261,36 @@ instead of working around it.
|
||||
**re-check reproducibility** (semanage rebuilds the policy store → possible
|
||||
verity-hash drift). The triage waivers/decisions live in the tailoring +
|
||||
docs/stig-deviations.md #12; the generated report should show 0–1 REVIEW.
|
||||
- NTP via DHCP option 42 → chrony (no public-pool egress): confirm on a booted
|
||||
host `chronyc sources` lists the DHCP-handed lab server (not `*.pool.ntp.org`).
|
||||
Assumes (a) NM exports the lease's NTP servers to the dispatcher as
|
||||
`$DHCP4_NTP_SERVERS`, and (b) RHEL 9 `/etc/chrony.conf` honours
|
||||
`sourcedir /run/chrony-dhcp` (the provisioner appends it if absent). If sources
|
||||
are empty, the dispatcher didn't fire or the env var name differs — check
|
||||
`journalctl -u NetworkManager` and `ls /run/chrony-dhcp/`.
|
||||
- **NTP is `secure_ntp=true` by default (NTS, RFC 8915) — CA-authenticated time**
|
||||
(closes F-03/P-18; docs/secure-ntp-design.md). **No FQDN is baked:** the NM
|
||||
dispatcher (`10-chrony-dhcp-ntp`, mode-aware) derives the server name at runtime
|
||||
as `<ntp_prefix>.<DHCP-provided-domain>` (default `ntp`.`$DHCP4_DOMAIN_NAME`,
|
||||
e.g. `ntp.fleet.lab`), writes `server … iburst nts` into `sourcedir
|
||||
/run/chrony-dhcp`, and chrony verifies the NTS-KE TLS cert against the **fleet
|
||||
CA** (`/etc/dot1x/radius-ca.pem` — reused; baked as `ntstrustedcerts`). Only the
|
||||
`ntp` label + the CA are baked, so the **site domain can change without a
|
||||
rebuild** (new DHCP domain + reissued NTS cert). A DHCP/NTP/DNS spoofer can only
|
||||
DoS (no fleet-CA-signed cert for the name it redirects to ⇒ NTS fails ⇒ no sync),
|
||||
never shift the clock. Serving needs: DHCP **option 15** (domain-name) handed to
|
||||
hosts, a **fleet-signed** NTS cert with DNS SAN `ntp.<domain>`
|
||||
(`serving/chrony/nts-cert.sh`), and **tcp/4460** (NTS-KE) open + udp/123. Confirm
|
||||
on a booted host: `chronyc -N authdata` shows the source in `NTS` mode, cookies
|
||||
> 0. `nocerttimecheck 1` lets a cold boot clock (no RTC) bootstrap the first sync.
|
||||
**Bench finding (2026-07-11, FIXED):** the chrony RPM ships its OWN NM dispatcher
|
||||
`/usr/lib/NetworkManager/dispatcher.d/20-chrony-dhcp`; NM merges `/etc` + `/usr/lib`
|
||||
dispatchers by name, so it ran AFTER our `10-chrony-dhcp-ntp` and overwrote the NTS
|
||||
line in the shared `/run/chrony-dhcp/<iface>.sources` with the plain option-42
|
||||
server — silently defeating NTS (authdata Mode `-`, Cook 0). The provisioner now
|
||||
`rm`s it (NTS then reached Cook 8 on the bench). Don't re-add a competing writer to
|
||||
that file. **Second bench finding (2026-07-12, FIXED):** removing that distro script
|
||||
also removed the `restorecon` that had been labelling the sources — the dispatcher's
|
||||
`mkdir -p` leaves `/run/chrony-dhcp` + files as generic `var_run_t`, which
|
||||
`chronyd_t` is SELinux-DENIED reading (loads 0 sources, `authdata` empty). Fixed by
|
||||
`/etc/tmpfiles.d/chrony-dhcp.conf` pre-creating the dir with the stock
|
||||
`chronyd_var_run_t` fcontext at sysinit, so the dispatcher's files inherit a readable
|
||||
label (no allow rules). The dispatcher also now skips `lo` (was writing a spurious
|
||||
duplicate). **Toggle:** `secure_ntp=false` reverts the dispatcher to the legacy
|
||||
unauthenticated **DHCP option 42** path (plain `server <ip> iburst`).
|
||||
- TLS audit offload (2f → `serving/audit-collector`, gtls/6514, since
|
||||
2026-07-12): the plaintext-514 path was bench-verified; the TLS upgrade is
|
||||
NOT yet. On a booted host confirm the sender is ESTABLISHED to **:6514**
|
||||
@@ -265,6 +302,7 @@ instead of working around it.
|
||||
same ufw trap as 514), image built with the real fleet CA. The three
|
||||
`rsyslog_encrypt_offload_*` STIG rules are enforced (waivers dropped) and
|
||||
must PASS in the report.
|
||||
- `fleet` Plymouth theme: confirm (a) the script plugin package actually
|
||||
installed (`/usr/lib64/plymouth/script.so` — else the build fell back to
|
||||
`spinner`, logged as a WARNING), (b) the scripted theme + a font got bundled
|
||||
into the initramfs (`lsinitrd | grep -E 'fleet.script|script.so'`), and (c) it
|
||||
@@ -320,7 +358,7 @@ These bit us bringing the build up on Rocky 9.8; they are easy to regress.
|
||||
stack stays in the image, dormant. The matching lab DHCP/HTTP/NVMe-oF serving
|
||||
stack lives in `serving/` (docker compose). Re-enable security by dropping the
|
||||
var / setting `DISABLE_DOT1X: "false"` in the CI workflow.
|
||||
- **STIG toggle**: `enable_stig=true` (default) applies DISA STIG via
|
||||
- **STIG toggle** (now **enforced-true**, not just default): `enable_stig=true` applies DISA STIG via
|
||||
OpenSCAP/SSG in a provisioner, tailored for the diskless/UKI/kiosk architecture
|
||||
(`files/stig/diskless-tailoring.xml` unselects the N/A or
|
||||
dangerous-to-remediate rules; every waiver is justified in
|
||||
@@ -332,8 +370,11 @@ These bit us bringing the build up on Rocky 9.8; they are easy to regress.
|
||||
**report-only**: it publishes `stig-report.html` + `stig-arf.xml` +
|
||||
`stig-deviations.md` (generated by `scripts/stig-deviations.sh`) as artifacts
|
||||
and never fails on score. The scanner tooling is removed from the shipped image
|
||||
(2d hygiene). `enable_stig=false` is the escape hatch if hardening regresses
|
||||
the boot/secure-path bench. NEVER let a waiver hide a real finding — anything
|
||||
(2d hygiene). **`enable_stig` is enforced `true` by Packer variable validation
|
||||
(POA&M P-03) — a build with it false fails `packer validate`/`build`; there is
|
||||
no unhardened build path.** If hardening regresses the boot/secure-path bench,
|
||||
waive the offending rule in the tailoring as a justified deviation (never by
|
||||
disabling STIG). NEVER let a waiver hide a real finding — anything
|
||||
non-pass that isn't in the register shows as **REVIEW** in the report.
|
||||
- **Boot splash toggle**: `boot_splash=true` (default) bakes `rhgb quiet` for the
|
||||
Plymouth splash; `false` bakes `plymouth.enable=0` for a verbose console boot —
|
||||
|
||||
Reference in New Issue
Block a user