Add STIG hrdening

This commit is contained in:
Audiobuzz
2026-06-18 10:00:41 +00:00
parent 210ab9283d
commit ada4aa4b18
9 changed files with 381 additions and 2 deletions
+58 -1
View File
@@ -130,6 +130,12 @@ variable "boot_splash" {
default = true
}
variable "enable_stig" {
type = bool
description = "true (default): apply DISA STIG hardening (OpenSCAP + SSG) with a diskless/UKI/kiosk tailoring (files/stig/diskless-tailoring.xml), bake STIG kernel params into the UKI cmdline, and emit a deviations report. false: skip hardening entirely — escape hatch if a build regresses the boot/secure-path bench. FIPS is OFF and audit offload is LOCAL-only regardless (see docs/stig-deviations.md)."
default = true
}
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."
@@ -416,6 +422,51 @@ build {
]
}
# --- 2c-STIG. DISA STIG hardening (OpenSCAP + SSG), diskless/UKI/kiosk tailored
# Runs after admin (so it hardens the full system incl. the admin/sshd config)
# and BEFORE hygiene 2d (so 2d can strip the scanner tooling + reports). The
# tailoring (files/stig/diskless-tailoring.xml) unselects rules that are N/A to
# this architecture or dangerous to auto-remediate (FIPS off, firewall off,
# AIDE, partitions, smartcard, GRUB) — each justified in docs/stig-deviations.md.
# FIPS=off, audit offload=local per decision. Gated by enable_stig.
provisioner "file" {
source = "files/stig"
destination = "/tmp/"
}
provisioner "shell" {
environment_vars = ["ENABLE_STIG=${var.enable_stig}"]
inline = [
"set -e",
"if [ \"$ENABLE_STIG\" != \"true\" ]; then echo 'enable_stig=false -> skipping STIG hardening.'; mkdir -p /root; echo '<html><body>STIG hardening disabled for this build (enable_stig=false).</body></html>' > /root/stig-report.html; : > /root/stig-arf.xml; exit 0; fi",
"echo '==> Installing OpenSCAP + SCAP Security Guide'",
"dnf install -y openscap-scanner scap-security-guide",
# SSG ships rhel9 content (usable on Rocky); some rebuilds name it rl9.
"DS=$(ls /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml /usr/share/xml/scap/ssg/content/ssg-rl9-ds.xml 2>/dev/null | head -1)",
"[ -n \"$DS\" ] || { echo 'ERROR: SSG rhel9 datastream not found' >&2; exit 1; }",
"install -D -m0644 /tmp/stig/diskless-tailoring.xml /etc/stig/diskless-tailoring.xml",
"echo \"==> Remediating against tailored STIG profile using $DS (this changes a lot)\"",
# --remediate returns nonzero when rules remain failed (expected) — tolerate.
"oscap xccdf eval --remediate --profile xccdf_org.ssgproject.content_profile_stig_diskless --tailoring-file /etc/stig/diskless-tailoring.xml --results-arf /root/stig-arf.xml --report /root/stig-report.html \"$DS\" || true",
# Clean post-remediation eval = the report/ARF of record (no --remediate).
"oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_stig_diskless --tailoring-file /etc/stig/diskless-tailoring.xml --results-arf /root/stig-arf.xml --report /root/stig-report.html \"$DS\" || true",
"rm -rf /tmp/stig",
"echo '==> STIG remediation complete (report at /root/stig-report.html, ARF at /root/stig-arf.xml)'",
]
}
# Pull the scan out to the host artifact dir for CI (before 2d removes it from
# the VM). Packer creates the local destination dir on download. Two single-
# source downloads (the file provisioner's download direction takes one source).
provisioner "file" {
direction = "download"
source = "/root/stig-report.html"
destination = "${var.artifact_dir}/${var.image_version}/stig-report.html"
}
provisioner "file" {
direction = "download"
source = "/root/stig-arf.xml"
destination = "${var.artifact_dir}/${var.image_version}/stig-arf.xml"
}
# --- 2d. Final image hygiene: strip caches/logs after the LAST dnf step --
# MUST be the last provisioner. The `dnf clean all` in step 2 runs BEFORE the
# kiosk (2b) and admin (2c) provisioners, and 2b enables EPEL + installs
@@ -427,6 +478,12 @@ build {
provisioner "shell" {
inline = [
"set -eux",
# Drop the STIG scanner tooling from the shipped image (the reports were
# already downloaded to the host) — slims the erofs + cuts attack surface.
# Keep auditd (it's part of the hardened runtime). Best-effort.
"dnf remove -y openscap-scanner scap-security-guide openscap-utils 2>/dev/null || true",
# The build-time STIG reports must not ship inside the read-only rootfs.
"rm -f /root/stig-report.html /root/stig-arf.xml",
"dnf clean all",
"rm -rf /var/cache/dnf/* /var/cache/PackageKit /var/cache/ldconfig/*",
"rm -rf /var/lib/dnf/repos/* /var/log/dnf.* /var/log/hawkey.log",
@@ -451,7 +508,7 @@ build {
# home dirs) — we dropped mkfs.erofs --all-root, which used to mask this by
# flattening everything to root:root. sudo is a no-op if already root, and
# the runner has passwordless sudo (cf. the `sudo apt-get` install step).
"sudo bash -c \"NVMET_TRADDR='${var.nvmet_traddr}' BOOT_SPLASH='${var.boot_splash}' bash scripts/extract-artifacts.sh output-qemu/diskless-build.qcow2 ${var.artifact_dir}/${var.image_version} ${var.disable_dot1x}\"",
"sudo bash -c \"NVMET_TRADDR='${var.nvmet_traddr}' BOOT_SPLASH='${var.boot_splash}' STIG='${var.enable_stig}' bash scripts/extract-artifacts.sh output-qemu/diskless-build.qcow2 ${var.artifact_dir}/${var.image_version} ${var.disable_dot1x}\"",
# Hand the OUTPUT FILES back to the build user (their in-erofs ownership is
# already baked correctly) so build-oci.sh can write the UKI and push with
# the build user's `buildah login`.