Add STIG hrdening
This commit is contained in:
+18
-1
@@ -45,7 +45,24 @@ Done:
|
||||
transiently grabs `en*`, fall back to also baking a static `en*`-unmanaged file
|
||||
for secure-only builds.
|
||||
|
||||
## [ ] 2. Apply RHEL9 STIG hardening to the built image
|
||||
## [~] 2. Apply RHEL9 STIG hardening to the built image
|
||||
|
||||
**IMPLEMENTED 2026-06-18 — awaiting build/bench verification.** OpenSCAP + SSG
|
||||
remediation in a provisioner (`enable_stig` var, default on), tailored for the
|
||||
diskless/UKI/kiosk architecture via `files/stig/diskless-tailoring.xml`. STIG
|
||||
kernel params baked into the UKI cmdline (`extract-artifacts.sh`). Decisions:
|
||||
**FIPS off**, **audit local-only**, **CI report-only**. Deviations are documented
|
||||
in **[stig-deviations.md](stig-deviations.md)** (curated register) and reported
|
||||
per-build by **[../scripts/stig-deviations.sh](../scripts/stig-deviations.sh)**
|
||||
(non-pass rules tagged intentional vs REVIEW); CI publishes
|
||||
`stig-report.html` + `stig-arf.xml` + `stig-deviations.md`. Scanner tooling is
|
||||
removed from the shipped image. **BENCH-VERIFY:** SSG datastream name; boot/login
|
||||
still work; reproducible verity hash across two builds; triage REVIEW rows.
|
||||
|
||||
Remaining (future hardening, currently waived — see the register): FIPS,
|
||||
firewall, remote audit offload, per-host/rotatable admin access.
|
||||
|
||||
### Original spec (retained for reference)
|
||||
|
||||
OpenSCAP + SCAP Security Guide (`xccdf_org.ssgproject.content_profile_stig`), run
|
||||
as a Packer provisioner after `dnf install` and before the dracut/initramfs build
|
||||
|
||||
@@ -78,6 +78,12 @@ These don't contradict the PDF (it doesn't specify them) but are worth recording
|
||||
- **Reference serving stack.** §7's SPDK + nginx + DHCP/DNS serving stack has a
|
||||
concrete docker-compose implementation in [../serving/](../serving/), incl.
|
||||
Docker image-volume mounting of the OCI artifact image.
|
||||
- **STIG hardening.** DISA STIG (SSG) is applied with a diskless/UKI/kiosk
|
||||
tailoring; several rule families are deviated because the architecture makes
|
||||
them N/A (no partitions, no GRUB, AIDE→dm-verity) or by lab decision (FIPS off,
|
||||
firewall off, audit local-only). Full rationale + compensating controls:
|
||||
[stig-deviations.md](stig-deviations.md). STIG kernel params live in the UKI
|
||||
cmdline (the no-GRUB compensating control).
|
||||
- Assorted build-time fixes (HCL `%%{` escaping, file-provisioner staging,
|
||||
dracut module naming) are documented in
|
||||
[../CLAUDE.md](../CLAUDE.md) → "RHEL 9.x build notes".
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# STIG deviation register — diskless fleet image
|
||||
|
||||
DISA STIG (SSG `xccdf_org.ssgproject.content_profile_stig` for RHEL 9) is applied
|
||||
to the image via `oscap --remediate` with a tailoring file
|
||||
([../files/stig/diskless-tailoring.xml](../files/stig/diskless-tailoring.xml)).
|
||||
This register documents every **intentional** deviation — the rules unselected in
|
||||
that tailoring, plus posture decisions — with the rationale and compensating
|
||||
control. The per-build **generated** report
|
||||
(`stig-deviations.md` / `stig-report.html`, produced by
|
||||
[../scripts/stig-deviations.sh](../scripts/stig-deviations.sh) from the
|
||||
post-remediation scan) cross-references this file: any non-`pass` rule **not**
|
||||
listed here is flagged **REVIEW**.
|
||||
|
||||
Posture decisions (2026-06-18, lab phase):
|
||||
- **FIPS: OFF** — revisit alongside UKI signing / Secure Boot.
|
||||
- **Audit offload: LOCAL-only** — auditd runs and logs to the tmpfs overlay; no
|
||||
remote collector yet.
|
||||
- **CI: report-only** — `oscap eval` publishes ARF + HTML; never fails the build.
|
||||
|
||||
## Intentional deviations
|
||||
|
||||
| # | STIG/SSG area (rules) | Why it doesn't apply / can't be met | Compensating control |
|
||||
|---|---|---|---|
|
||||
| 1 | **Separate partitions + their mount options** for `/var`, `/var/log`, `/var/log/audit`, `/tmp`, `/home`, `/var/tmp`, `/dev/shm` (`partition_for_*`, `mount_option_*`) | Root is a single immutable **erofs** image on NVMe/TCP with a **tmpfs volatile overlay** — there is no local disk to partition, and these dirs are not separate mounts. The related `mount_option_*` rules will therefore show as findings; they are covered by this entry. | The entire root is **read-only** (erofs+dm-verity) and the writable layer is **tmpfs (RAM)**, which structurally bounds the tampering / space-exhaustion risks the partition+option rules target. `noexec` on `/tmp` is intentionally NOT forced — the browser kiosk uses `/tmp` for its profile/cache and can break under `noexec`. |
|
||||
| 2 | **Bootloader / GRUB** (`grub2_*`: password, admin user, and the `*_argument` kernel-param rules) | Boot is a **UKI** (systemd-stub) over UEFI HTTP boot — there is **no GRUB**. | The STIG kernel params (`audit=1`, `audit_backlog_limit`, `slub_debug=P`, `page_poison=1`, `vsyscall=none`, `init_on_alloc/free`) are baked into the **signed UKI cmdline** ([../scripts/extract-artifacts.sh](../scripts/extract-artifacts.sh)); the cmdline is immutable once signed (Secure Boot covers it), which is stronger than an editable GRUB config. |
|
||||
| 3 | **AIDE / file integrity** (`package_aide_installed`, `aide_build_database`, `aide_periodic_cron_checking`, `aide_scan_notification`, `aide_verify_*`) | The root is read-only and cryptographically verified; an on-disk AIDE db would also be non-deterministic and break the reproducible verity hash. | **dm-verity** verifies the whole root filesystem against a signed root hash on every read — continuous, hardware-backed integrity that supersedes AIDE's periodic scan. |
|
||||
| 4 | **Firewall** (`service_firewalld_enabled`, `package_firewalld_installed`, `set_firewalld_default_zone`) | Currently `firewall --disabled`; auto-enabling firewalld/nftables risks the NVMe/TCP (`:4420`) and NetworkManager paths during bring-up. | Isolated boot LAN; egress controlled at the serving-stack / network firewall layer. **Future hardening** — re-enable with explicit allow rules once the secure path is verified. |
|
||||
| 5 | **FIPS** (`enable_fips_mode`, `grub2_enable_fips_mode`, `package_dracut-fips_installed`) | Decision: off for the lab phase (interacts with the still-unsigned UKI / Secure Boot story). | Revisit with signing. No weakened crypto is configured in the meantime. |
|
||||
| 6 | **Remote audit offload** (`auditd_audispd_configure_remote_server`, `..._encrypt_sent_records`, `..._network_failure_action`, `rsyslog_remote_loghost`) | Decision: local-only for now; no collector deployed. | auditd enabled and recording locally. **Future hardening** — `audisp-remote`/rsyslog to a per-environment collector DNS name (host-generic, no per-host state). |
|
||||
| 7 | **Smart card login** (`require_smart_card_login`, `sssd_enable_smartcard`, `install_smartcard_packages`) | Diskless hosts have no smartcard reader; admin access is SSH key-based. Enforcing smartcard login would lock the host out. | Admin auth is an SSH **key-only** account; root is locked; password auth disabled. |
|
||||
| 8 | **NOPASSWD sudo** (`sudo_remove_nopasswd`) | The baked admin is a **passwordless, key-only** account, so `sudo` cannot prompt for a password — NOPASSWD is required for it to function at all. | Single fleet-generic admin, key-only, `wheel`; no interactive password exists to protect. Tracked for the eventual per-host/rotatable access model (SSH CA / config-agent). |
|
||||
| 9 | **GUI / autologin / GDM** (gnome screensaver, GDM banner, automatic-login rules) | No GNOME/GDM is installed; the kiosk is an **intentional** autologin single-app (weston + Chromium) session on kiosk builds. | Kiosk user is unprivileged and passwordless; the session runs a single locked-down browser (no shell, no nav). Applies to kiosk builds only; non-kiosk builds don't install the GUI stack. |
|
||||
|
||||
## Things that are NOT deviations (kept / enforced)
|
||||
|
||||
- **SELinux enforcing** (`selinux --enforcing`; labels preserved into the erofs).
|
||||
- **auditd** enabled; STIG audit rules applied; logs locally.
|
||||
- **sshd hardening**: root login off, password & keyboard-interactive auth off,
|
||||
key-only (drop-in `50-fleet-hardening.conf`); STIG ciphers/MACs/KEX applied.
|
||||
- **No per-host state**: SSH host keys stripped at build, regenerated at first
|
||||
boot into the overlay; `machine-id` empty.
|
||||
- Password-policy / faillock rules remain selected — harmless (no interactive
|
||||
passwords) and keep the report honest.
|
||||
|
||||
## How to read the generated report
|
||||
|
||||
`scripts/stig-deviations.sh stig-arf.xml` emits a table of every rule whose
|
||||
result is not `pass`. Each row is tagged:
|
||||
- **intentional** — matches an unselected rule / area in this register.
|
||||
- **REVIEW** — a real finding to triage (fix it, or add a justified entry here).
|
||||
Reference in New Issue
Block a user