Merge remote-tracking branch 'origin/main' into poam-hardening
# Conflicts: # CLAUDE.md
This commit is contained in:
+63
-6
@@ -164,6 +164,18 @@ variable "audit_host" {
|
||||
default = "audit.fleet.lab"
|
||||
}
|
||||
|
||||
variable "secure_ntp" {
|
||||
type = bool
|
||||
description = "true (default): SECURE, CA-AUTHENTICATED time via NTS (RFC 8915). The NM dispatcher derives the NTP server name at runtime as <ntp_prefix>.<DHCP-provided-domain> (NO FQDN baked — the domain can change without a rebuild) and chrony verifies the server's NTS-KE TLS cert against the fleet CA (/etc/dot1x/radius-ca.pem — same anchor as RADIUS/agent). A DHCP/NTP/DNS spoofer can then only DoS (cause no-sync), never shift the clock to resurrect expired/revoked certs (closes F-03/P-18; docs/secure-ntp-design.md). The serving NTP server needs a fleet-signed NTS cert with SAN <ntp_prefix>.<domain> (serving/chrony/nts-cert.sh). false: legacy UNAUTHENTICATED DHCP option-42 NTP (the dispatcher writes the handed IPs) — lab/interop only."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "ntp_prefix" {
|
||||
type = string
|
||||
description = "secure_ntp=true only: the label prepended to the DHCP-provided domain to form the NTS NTP server name (<ntp_prefix>.<domain>, e.g. ntp.fleet.lab). Deliberately NOT a full FQDN — only this label + the fleet CA are baked, so the environment's domain can change (new DHCP domain + reissued NTS cert) without rebuilding the image. Must match the serving NTS cert SAN and resolve via dnsmasq."
|
||||
default = "ntp"
|
||||
}
|
||||
|
||||
variable "sb_key" {
|
||||
type = string
|
||||
description = "Secure Boot signing key (PEM) for the UKI. Empty = unsigned lab build. Production: CI/HSM-held; never enters the build VM."
|
||||
@@ -179,7 +191,7 @@ variable "sb_cert" {
|
||||
|
||||
variable "fleet_ca" {
|
||||
type = string
|
||||
description = "Path on the BUILD HOST to the fleet CA PEM baked to /etc/dot1x/radius-ca.pem — the trust anchor wpa_supplicant uses for RADIUS EAP-TLS, the fleet-agent uses to verify the fleet-manager, AND rsyslog uses to verify the audit collector (TLS offload, 2f). Defaults to the COMMITTED PLACEHOLDER (files/etc/dot1x/radius-ca.pem), which is NOT a usable CA (auth/telemetry/log-offload can't succeed with it). Override per-environment (CI secret -> temp file) with your real step-ca root (or a root+intermediate bundle for robustness). Never commit real internal CA material (CLAUDE.md)."
|
||||
description = "Path on the BUILD HOST to the fleet CA PEM baked to /etc/dot1x/radius-ca.pem — the trust anchor wpa_supplicant uses for RADIUS EAP-TLS, the fleet-agent uses to verify the fleet-manager, rsyslog uses to verify the audit collector (TLS offload, 2f), AND chrony uses to verify the NTS time server (ntstrustedcerts, secure_ntp). Defaults to the COMMITTED PLACEHOLDER (files/etc/dot1x/radius-ca.pem), which is NOT a usable CA (auth/telemetry/log-offload/NTS can't succeed with it). Override per-environment (CI secret -> temp file) with your real step-ca root (or a root+intermediate bundle for robustness). Never commit real internal CA material (CLAUDE.md)."
|
||||
default = "files/etc/dot1x/radius-ca.pem"
|
||||
}
|
||||
|
||||
@@ -421,15 +433,51 @@ build {
|
||||
# secure mode, reserving the physical NIC for wpa_supplicant.
|
||||
"chmod 0755 /usr/lib/dracut/modules.d/95dot1x-macsec/*.sh",
|
||||
|
||||
# ---- NTP from DHCP (option 42) -> chrony, no public-pool egress --------
|
||||
# NM dispatcher writes DHCP-provided NTP servers into chrony's sourcedir
|
||||
# (replaces the removed dhclient.d/chrony.sh bridge). Strip the baked public
|
||||
# pool so the host syncs ONLY from the lab NTP server handed via DHCP — the
|
||||
# workstation never reaches *.pool.ntp.org.
|
||||
# ---- NTP: CA-authenticated NTS (default) or legacy DHCP option 42 ------
|
||||
# BOTH modes run through the SAME machinery: the NM dispatcher writes a
|
||||
# source file into chrony's `sourcedir /run/chrony-dhcp`; the baked public
|
||||
# pool is stripped so the host never egresses to *.pool.ntp.org.
|
||||
# secure_ntp=true (NTS): the dispatcher derives the server NAME at runtime
|
||||
# as <ntp_prefix>.<DHCP-provided-domain> and writes `server NAME iburst
|
||||
# nts`. chrony verifies the NTS-KE TLS cert against the FLEET CA
|
||||
# (ntstrustedcerts below). NOTHING host- or domain-specific is baked
|
||||
# (only the `ntp` label + the CA) — the domain can change without a
|
||||
# rebuild. A DHCP/NTP/DNS spoofer can only DoS; it can't present a
|
||||
# fleet-CA-signed cert for the name it redirects to, so it can't shift
|
||||
# the clock (F-03/P-18). No fallback to unauthenticated time.
|
||||
# nocerttimecheck 1: a diskless host may boot with a bad/epoch clock (no
|
||||
# reliable RTC), failing the cert validity window (chicken-and-egg);
|
||||
# allow the FIRST measurement without the cert-time check to bootstrap,
|
||||
# then enforce. Isolated LAN + one-measurement window bound the trade-off.
|
||||
# secure_ntp=false (legacy): the dispatcher writes the DHCP option-42 IPs
|
||||
# as plain `server <ip> iburst` — UNAUTHENTICATED, lab/interop only.
|
||||
# The dispatcher is mode-aware via baked @NTP_MODE@/@NTP_PREFIX@ tokens.
|
||||
"install -D -m0755 /tmp/etc-stage/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp /etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp",
|
||||
"if [ \"${var.secure_ntp}\" = \"true\" ]; then NTP_MODE=nts; else NTP_MODE=dhcp; fi",
|
||||
"sed -i \"s/@NTP_MODE@/$NTP_MODE/; s/@NTP_PREFIX@/${var.ntp_prefix}/\" /etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp",
|
||||
"restorecon /etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp",
|
||||
# Remove the chrony package's OWN NM dispatcher. NetworkManager merges
|
||||
# dispatchers from /etc AND /usr/lib sorted by name, so /usr/lib/.../20-chrony-dhcp
|
||||
# runs AFTER our 10-chrony-dhcp-ntp and writes the DHCP option-42 server to the
|
||||
# SAME /run/chrony-dhcp/<iface>.sources — clobbering our NTS line and silently
|
||||
# defeating authenticated time (bench 2026-07-11, comet-beacon-54: chrony fell
|
||||
# back to plain NTP, authdata Mode '-', Cook 0; removing it → NTS Cook 8). Our
|
||||
# dispatcher supersedes it in BOTH modes, so drop it unconditionally. Leave
|
||||
# 20-chrony-onoffline (only marks sources online/offline — harmless).
|
||||
"rm -f /usr/lib/NetworkManager/dispatcher.d/20-chrony-dhcp",
|
||||
# ...but the distro script was ALSO (accidentally) labelling /run/chrony-dhcp so
|
||||
# chronyd could read the sources. Removing it exposed that gap: our dispatcher's
|
||||
# `mkdir -p` makes the dir generic var_run_t and the source files inherit it, and
|
||||
# chronyd_t (which only reads chronyd_var_run_t) is SELinux-DENIED reading them →
|
||||
# NTS silently loads 0 sources (bench 2026-07-12, comet-beacon-54). This tmpfiles.d
|
||||
# entry pre-creates /run/chrony-dhcp at sysinit with the stock chronyd_var_run_t
|
||||
# fcontext (before any NM event), so the dispatcher's files inherit a readable
|
||||
# label — no SELinux allow rules needed. Applies in BOTH modes.
|
||||
"install -D -m0644 /tmp/etc-stage/tmpfiles.d/chrony-dhcp.conf /etc/tmpfiles.d/chrony-dhcp.conf",
|
||||
"restorecon /etc/tmpfiles.d/chrony-dhcp.conf",
|
||||
"sed -i -E 's/^[[:space:]]*(pool|server)[[:space:]]/#&/' /etc/chrony.conf",
|
||||
"grep -q '^sourcedir /run/chrony-dhcp' /etc/chrony.conf || echo 'sourcedir /run/chrony-dhcp' >> /etc/chrony.conf",
|
||||
"if [ \"${var.secure_ntp}\" = \"true\" ]; then echo '==> secure NTP: NTS via <${var.ntp_prefix}>.<dhcp-domain> (fleet-CA-authenticated)'; { printf '\\n# Secure NTP (NTS, RFC 8915) — verify the server against the fleet CA. docs/secure-ntp-design.md\\n'; printf 'ntstrustedcerts /etc/dot1x/radius-ca.pem\\n'; printf 'nocerttimecheck 1\\n'; } >> /etc/chrony.conf; else echo '==> legacy NTP: unauthenticated DHCP option-42'; fi",
|
||||
# Enable chrony's tracking log so the fleet-agent can report clock health
|
||||
# (source/stratum/offset) WITHOUT touching chronyd's command interface. That
|
||||
# interface is unusable by the hardened agent: the unix socket is
|
||||
@@ -1064,6 +1112,15 @@ build {
|
||||
" echo '==> fapolicyd: watch_fs now includes overlay (guards the volatile-overlay root)'",
|
||||
" fi",
|
||||
"fi",
|
||||
# secure_ntp fail-closed: STIG's chronyd_specify_remote_server remediation
|
||||
# re-adds an UNAUTHENTICATED `server <pool> maxpoll 16` to chrony.conf AFTER
|
||||
# the secure_ntp chrony step commented the baked servers out (2c runs later).
|
||||
# That plain server is a silent fallback that defeats NTS's DoS-only guarantee
|
||||
# (a DNS/NTP spoofer could shift the clock through it — the F-03 risk). When
|
||||
# secure_ntp=true, re-strip any server/pool so ONLY the NTS sourcedir +
|
||||
# ntstrustedcerts remain. The rules are also unselected in the tailoring; this
|
||||
# is belt-and-suspenders. Bench 2026-07-11; docs/stig-deviations.md #17.
|
||||
"if [ \"${var.secure_ntp}\" = \"true\" ] && [ -f /etc/chrony.conf ]; then echo '==> secure_ntp: re-stripping STIG-injected plain NTP servers from chrony.conf (fail-closed: NTS only)'; sed -i -E 's/^[[:space:]]*(pool|server)[[:space:]]/#&/' /etc/chrony.conf; fi",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user