diff --git a/rhel-stig/Dockerfile.rhel9 b/rhel-stig/Dockerfile.rhel9 index d7b86f06..0f20c63f 100644 --- a/rhel-stig/Dockerfile.rhel9 +++ b/rhel-stig/Dockerfile.rhel9 @@ -1,17 +1,18 @@ FROM quay.io/kairos/kairos-init:v0.5.28 AS kairos-init FROM registry.access.redhat.com/ubi9-init:9.4-6 -ARG USERNAME -ARG PASSWORD ARG KAIROS_VERSION=v3.5.9 RUN dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm -y -# Subscription manager in redhat does not run directly in containers unless you run on a redhat host, hence we remove the rhsm-host, login to the redhat subscription and add the repos -RUN rm /etc/rhsm-host && subscription-manager register --username ${USERNAME} --password ${PASSWORD} \ +# subscription-manager runs inside the UBI container (host OS does not need to be RHEL). Remove rhsm-host so it does not try to use host entitlements. +# Uses BuildKit secrets (--secret id=rhsm_username,src=... --secret id=rhsm_password,src=...) - credentials never stored in image +RUN --mount=type=secret,id=rhsm_username \ + --mount=type=secret,id=rhsm_password \ + sh -c 'rm /etc/rhsm-host && subscription-manager register --username "$(cat /run/secrets/rhsm_username)" --password "$(cat /run/secrets/rhsm_password)" \ && yum repolist \ && subscription-manager attach --auto \ && subscription-manager repos --enable rhel-9-for-x86_64-appstream-rpms \ - && yum repolist + && yum repolist' # Let Kairos install packages first (provides network config, luet packages, etc.) RUN --mount=type=bind,from=kairos-init,src=/kairos-init,dst=/kairos-init \ diff --git a/rhel-stig/Dockerfile.rhel9-fips b/rhel-stig/Dockerfile.rhel9-fips index 79907ca8..058634b0 100644 --- a/rhel-stig/Dockerfile.rhel9-fips +++ b/rhel-stig/Dockerfile.rhel9-fips @@ -1,20 +1,21 @@ FROM quay.io/kairos/kairos-init:v0.5.28 AS kairos-init FROM registry.access.redhat.com/ubi9-init:9.4-6 -ARG USERNAME -ARG PASSWORD ARG KAIROS_VERSION=v3.5.9 # Don't get asked while running apt commands ENV DEBIAN_FRONTEND=noninteractive RUN dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm -y -# Subscription manager in redhat does not run directly in containers unless you run on a redhat host, hence we remove the rhsm-host, login to the redhat subscription and add the repos -RUN rm /etc/rhsm-host && subscription-manager register --username ${USERNAME} --password ${PASSWORD} \ +# subscription-manager runs inside the UBI container (host OS does not need to be RHEL). Remove rhsm-host so it does not try to use host entitlements. +# Uses BuildKit secrets (--secret id=rhsm_username,src=... --secret id=rhsm_password,src=...) - credentials never stored in image +RUN --mount=type=secret,id=rhsm_username \ + --mount=type=secret,id=rhsm_password \ + sh -c 'rm /etc/rhsm-host && subscription-manager register --username "$(cat /run/secrets/rhsm_username)" --password "$(cat /run/secrets/rhsm_password)" \ && yum repolist \ && subscription-manager attach --auto \ && subscription-manager repos --enable rhel-9-for-x86_64-appstream-rpms \ - && yum repolist + && yum repolist' RUN echo "install_weak_deps=False" >> /etc/dnf/dnf.conf COPY overlay/rhel9/ / diff --git a/rhel-stig/README.md b/rhel-stig/README.md index 5387d0ea..a47f7af9 100644 --- a/rhel-stig/README.md +++ b/rhel-stig/README.md @@ -11,9 +11,11 @@ RHEL 9 STIG (Security Technical Implementation Guide) compliance is required for ### Prerequisites - Red Hat subscription credentials (username and password) -- Docker installed and running +- Docker installed and running (BuildKit enabled; the build script sets `DOCKER_BUILDKIT=1`) - Access to Red Hat repositories (RHEL 9 packages required) +**Building on non-RHEL hosts (Ubuntu, etc.):** subscription-manager runs inside the UBI container, so the host OS does not need to be RHEL. The build works on any Docker host. + ### Building Non-FIPS STIG Image ```bash @@ -36,7 +38,7 @@ Example: bash build.sh.rhel9 myuser@example.com mypassword rhel9-byoi-stig-fips true ``` -**Note**: Red Hat subscription credentials are required to build these images as RHEL 9 STIG packages are only available through Red Hat repositories. +**Note**: Red Hat subscription credentials are required to build these images as RHEL 9 STIG packages are only available through Red Hat repositories. Credentials are passed via Docker BuildKit secrets (not build args) and are never stored in image layers. Requires Docker BuildKit (default in Docker 23+; set `DOCKER_BUILDKIT=1` for older versions). ## Using the Base Image @@ -143,6 +145,8 @@ The build process automatically applies STIG remediation rules including: STIG disables `net.ipv4.ip_forward` and `net.ipv4.conf.all.forwarding` for general servers. Kubernetes nodes require both `=1` for CNI pod networking (Calico, Flannel, etc.). The build overrides STIG via `/etc/sysctl.d/99-zzz-kubernetes-ip-forward.conf` and applies it during build. +STIG sets `rp_filter=1` (strict); overlay clusters (Calico, etc.) require `rp_filter=0`. The build overrides via `/etc/sysctl.d/99-zzz-kubernetes-rp-filter.conf`. + ### Firewall Configuration No firewall ports or zones are opened by default. Configure firewall rules via **user-data** or **cluster profile** as needed for your environment. diff --git a/rhel-stig/build.sh.rhel9 b/rhel-stig/build.sh.rhel9 index 3af19ab3..eddeff84 100755 --- a/rhel-stig/build.sh.rhel9 +++ b/rhel-stig/build.sh.rhel9 @@ -1,14 +1,28 @@ #!/bin/bash +# Run from rhel-stig/ so static/ and stig-remediate.sh are in build context +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + USERNAME=$1 PASSWORD=$2 BASE_IMAGE="${3:-rhel9-byoi-stig}" FIPS_ENABLED="${4:-false}" +# BuildKit required for --secret (credentials never stored in image layers) +export DOCKER_BUILDKIT=1 + +# Use BuildKit secrets - credentials passed via temp files, never in image layers or build args +tmpdir=$(mktemp -d) +trap "rm -rf $tmpdir" EXIT +echo -n "$USERNAME" > "$tmpdir/rhsm_username" +echo -n "$PASSWORD" > "$tmpdir/rhsm_password" +chmod 600 "$tmpdir"/* + if [ "$FIPS_ENABLED" = "true" ]; then echo "Building RHEL 9 STIG FIPS image..." - docker build --build-arg USERNAME="$USERNAME" --build-arg PASSWORD="$PASSWORD" -t "$BASE_IMAGE" -f Dockerfile.rhel9-fips . + docker build --secret id=rhsm_username,src="$tmpdir/rhsm_username" --secret id=rhsm_password,src="$tmpdir/rhsm_password" -t "$BASE_IMAGE" -f Dockerfile.rhel9-fips . else echo "Building RHEL 9 STIG image..." - docker build --no-cache --build-arg USERNAME="$USERNAME" --build-arg PASSWORD="$PASSWORD" -t "$BASE_IMAGE" -f Dockerfile.rhel9 . + docker build --secret id=rhsm_username,src="$tmpdir/rhsm_username" --secret id=rhsm_password,src="$tmpdir/rhsm_password" -t "$BASE_IMAGE" -f Dockerfile.rhel9 . fi diff --git a/rhel-stig/stig-remediate.sh b/rhel-stig/stig-remediate.sh index 26fcc85b..fef1b75b 100755 --- a/rhel-stig/stig-remediate.sh +++ b/rhel-stig/stig-remediate.sh @@ -358,6 +358,33 @@ current=$(cat /proc/sys/net/ipv4/ip_forward 2>/dev/null || echo "MISSING") current_all=$(cat /proc/sys/net/ipv4/conf/all/forwarding 2>/dev/null || echo "MISSING") print_debug "Runtime after apply: net.ipv4.ip_forward=$current net.ipv4.conf.all.forwarding=$current_all" +# STIG sets rp_filter=1 (strict); overlay clusters/CNI require rp_filter=0 for pod networking +echo "Applying Kubernetes exception: net.ipv4.conf.all.rp_filter=0 and net.ipv4.conf.default.rp_filter=0 (required for overlay clusters)..." + +shopt -s nullglob +for f in /etc/sysctl.conf /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/lib/sysctl.d/*.conf; do + [ -f "$f" ] || continue + sed -i \ + -e 's/^[[:space:]]*net\.ipv4\.conf\.all\.rp_filter[[:space:]]*=.*$/# STIG exception (Kubernetes overlay clusters require rp_filter=0): &/' \ + -e 's/^[[:space:]]*net\.ipv4\.conf\.default\.rp_filter[[:space:]]*=.*$/# STIG exception (Kubernetes overlay clusters require rp_filter=0): &/' \ + "$f" 2>/dev/null || true +done +shopt -u nullglob 2>/dev/null || true + +cat > /etc/sysctl.d/99-zzz-kubernetes-rp-filter.conf <<'EOF' +# Kubernetes exception: STIG/Red Hat set rp_filter=1; overlay clusters (Calico, etc.) require rp_filter=0 +# 50-redhat.conf uses net.ipv4.conf.*.rp_filter=1 per-interface; override with same wildcard (loads later) +net.ipv4.conf.*.rp_filter = 0 +net.ipv4.conf.all.rp_filter = 0 +net.ipv4.conf.default.rp_filter = 0 +EOF +chown root:root /etc/sysctl.d/99-zzz-kubernetes-rp-filter.conf +chmod 0644 /etc/sysctl.d/99-zzz-kubernetes-rp-filter.conf + +/sbin/sysctl -w net.ipv4.conf.all.rp_filter=0 2>/dev/null || true +/sbin/sysctl -w net.ipv4.conf.default.rp_filter=0 2>/dev/null || true +/sbin/sysctl --system 2>/dev/null || true + # Ensure required drivers and modules are in dracut config (backup in case STIG removed them) for conf_file in /etc/dracut.conf.d/*.conf; do if [ -f "$conf_file" ]; then