diff --git a/scripts/sca-fix.sh b/scripts/sca-fix.sh new file mode 100644 index 0000000..21ef0f1 --- /dev/null +++ b/scripts/sca-fix.sh @@ -0,0 +1,502 @@ +#!/usr/bin/env bash +############################################################################### +# Targeted SCA Remediation — Based on sca-verify.sh diagnostic output +# +# Fixes all FAIL items from the diagnostic. Does NOT touch items that +# already PASS. Safe to re-run (idempotent). +# +# Usage: chmod +x sca-fix.sh && sudo ./sca-fix.sh +############################################################################### +set -euo pipefail +export DEBIAN_FRONTEND=noninteractive + +if [[ $EUID -ne 0 ]]; then echo "Run as root." >&2; exit 1; fi + +banner() { printf '\n\e[1;36m>>> %s\e[0m\n' "$1"; } + +ADMIN_USER="${ADMIN_USER:-chris}" + +############################################################################### +banner "FIX 1: Filesystem module blacklist (35509) — config file MISSING" +############################################################################### + +cat > /etc/modprobe.d/cis-filesystems.conf <<'EOF' +install afs /bin/false +install ceph /bin/false +install cifs /bin/false +install exfat /bin/false +install fat /bin/false +install fscache /bin/false +install fuse /bin/false +install gfs2 /bin/false +install nfs_common /bin/false +install nfsd /bin/false +install smbfs_common /bin/false +install cramfs /bin/false +install freevxfs /bin/false +install jffs2 /bin/false +install hfs /bin/false +install hfsplus /bin/false +install squashfs /bin/false +install udf /bin/false + +blacklist afs +blacklist ceph +blacklist cifs +blacklist exfat +blacklist fat +blacklist fscache +blacklist fuse +blacklist gfs2 +blacklist nfs_common +blacklist nfsd +blacklist smbfs_common +blacklist cramfs +blacklist freevxfs +blacklist jffs2 +blacklist hfs +blacklist hfsplus +blacklist squashfs +blacklist udf +EOF + +echo " ✓ /etc/modprobe.d/cis-filesystems.conf created" + +############################################################################### +banner "FIX 2: Network protocol module blacklist (35604-35607) — config file MISSING" +############################################################################### + +cat > /etc/modprobe.d/cis-network-protocols.conf <<'EOF' +install dccp /bin/false +install sctp /bin/false +install rds /bin/false +install tipc /bin/false + +blacklist dccp +blacklist sctp +blacklist rds +blacklist tipc +EOF + +echo " ✓ /etc/modprobe.d/cis-network-protocols.conf created" + +############################################################################### +banner "FIX 3: USB storage (uses /bin/true, should be /bin/false)" +############################################################################### + +cat > /etc/modprobe.d/cis-usb-storage.conf <<'EOF' +install usb-storage /bin/false +blacklist usb-storage +EOF + +echo " ✓ /etc/modprobe.d/cis-usb-storage.conf created" + +############################################################################### +banner "FIX 4: Rebuild initramfs with new module blacklists" +############################################################################### + +update-initramfs -u +echo " ✓ initramfs rebuilt" + +############################################################################### +banner "FIX 5: Sysctl — ip_forward and log_martians (35608, 35616)" +############################################################################### + +# Docker overrides ip_forward at runtime. To keep it disabled while +# allowing Docker networking, we need to tell Docker not to touch it +# and handle forwarding via iptables only. +# However, if Docker is actively in use, disabling ip_forward breaks +# container networking. The pragmatic fix: + +if command -v docker &>/dev/null; then + echo " Docker is installed. ip_forward=1 is required for container networking." + echo " This is a known conflict — document as compensating control." + echo " Creating /etc/sysctl.d/99-docker-override.conf for non-forwarding params only." + + # Don't override ip_forward if Docker is present, but DO fix log_martians + cat > /etc/sysctl.d/99-docker-override.conf <<'EOF' +# Docker requires ip_forward=1 — accepted as compensating control +# These params are safe to enforce alongside Docker: +net.ipv4.conf.all.log_martians = 1 +net.ipv4.conf.default.log_martians = 1 +EOF +else + # No Docker — enforce everything + sysctl -w net.ipv4.ip_forward=0 +fi + +# Apply log_martians immediately regardless +sysctl -w net.ipv4.conf.all.log_martians=1 +sysctl -w net.ipv4.conf.default.log_martians=1 +sysctl --system > /dev/null 2>&1 + +echo " ✓ log_martians enabled" +echo " ✓ sysctl reloaded" + +############################################################################### +banner "FIX 6: Purge nftables package (35626, 35634)" +############################################################################### + +if dpkg -s nftables &>/dev/null; then + apt-get purge -y nftables + echo " ✓ nftables purged" +else + echo " ✓ nftables already absent" +fi + +############################################################################### +banner "FIX 7: SSH config — merge drop-in into main config (35644-35661)" +############################################################################### + +# Wazuh scans /etc/ssh/sshd_config directly and doesn't parse drop-in files. +# All settings PASS via sshd -T but FAIL in Wazuh because they're only in the drop-in. +# Fix: append drop-in contents to main config and remove the drop-in. + +DROPIN="/etc/ssh/sshd_config.d/nist-hardening.conf" +MAIN="/etc/ssh/sshd_config" + +if [[ -f "$DROPIN" ]]; then + # Remove any existing lines in main config that we're about to add + # (avoid duplicates) + while IFS= read -r line; do + # Skip comments and empty lines + [[ "$line" =~ ^#|^$ ]] && continue + param=$(echo "$line" | awk '{print $1}') + # Remove existing uncommented instances of this param from main config + sed -i "/^\s*${param}\b/d" "$MAIN" 2>/dev/null || true + done < "$DROPIN" + + # Append the hardening config + echo "" >> "$MAIN" + echo "# ===== NIST 800-53 SSH Hardening (merged from drop-in) =====" >> "$MAIN" + cat "$DROPIN" >> "$MAIN" + + # Remove the drop-in + rm -f "$DROPIN" + + # Test and restart + if sshd -t; then + systemctl restart ssh + echo " ✓ SSH config merged into $MAIN, drop-in removed, SSH restarted" + else + echo " ERROR: sshd config test failed. Check $MAIN" + exit 1 + fi +else + echo " ✓ No drop-in to merge (already in main config)" +fi + +############################################################################### +banner "FIX 8: Password aging for root and nobody (35694, 35695, 35698)" +############################################################################### + +# root — set aging but keep locked (no password login) +chage -M 60 -m 1 -W 14 -I 30 root +echo " ✓ root: max=60 min=1 warn=14 inactive=30" + +# nobody — system account, set aging for compliance +chage -M 60 -m 1 -W 14 -I 30 nobody +echo " ✓ nobody: max=60 min=1 warn=14 inactive=30" + +############################################################################### +banner "FIX 9: Journald MaxRetentionSec (35708)" +############################################################################### + +JOURNALD_DROPIN="/etc/systemd/journald.conf.d/nist.conf" + +if [[ -f "$JOURNALD_DROPIN" ]]; then + if ! grep -q 'MaxRetentionSec' "$JOURNALD_DROPIN"; then + echo "MaxRetentionSec=1month" >> "$JOURNALD_DROPIN" + echo " ✓ MaxRetentionSec added to drop-in" + fi +fi + +# Also add to main config since Wazuh may only read the main file +JOURNALD_MAIN="/etc/systemd/journald.conf" +if ! grep -q '^\s*SystemMaxUse=' "$JOURNALD_MAIN"; then + # If the main config doesn't have our settings, add them + sed -i '/^\[Journal\]/a SystemMaxUse=1G\nSystemKeepFree=1G\nSystemMaxFileSize=100M\nMaxRetentionSec=1month' "$JOURNALD_MAIN" + echo " ✓ Journald settings added to main config for Wazuh" +else + if ! grep -q '^\s*MaxRetentionSec=' "$JOURNALD_MAIN"; then + sed -i '/^\[Journal\]/a MaxRetentionSec=1month' "$JOURNALD_MAIN" + echo " ✓ MaxRetentionSec added to main journald.conf" + fi +fi + +systemctl restart systemd-journald +echo " ✓ journald restarted" + +############################################################################### +banner "FIX 10: Log file permissions (35722)" +############################################################################### + +# Fix dpkg.log and sysstat files +find /var/log -type f -exec chmod g-wx,o-rwx {} + +find /var/log -type d -exec chmod g-wx,o-rwx {} + + +echo " ✓ /var/log permissions tightened" + +# Prevent sysstat from creating world-readable files in the future +if [[ -f /etc/sysstat/sysstat ]]; then + # Set umask for sysstat + if ! grep -q 'umask' /etc/sysstat/sysstat; then + echo 'UMASK="0027"' >> /etc/sysstat/sysstat + fi +fi + +# Also set default umask in rsyslog +if [[ -f /etc/rsyslog.d/99-nist-perms.conf ]]; then + if ! grep -q 'DirCreateMode' /etc/rsyslog.d/99-nist-perms.conf; then + echo '$DirCreateMode 0750' >> /etc/rsyslog.d/99-nist-perms.conf + echo '$Umask 0027' >> /etc/rsyslog.d/99-nist-perms.conf + fi +fi + +echo " ✓ Future log file creation modes tightened" + +############################################################################### +banner "FIX 11: Audit rules file (35731-35748) — rules file MISSING" +############################################################################### + +# Rules are loaded (50 rules) but the file Wazuh scans doesn't exist. +# The rules seem to be in /etc/audit/rules.d/cis-nist.rules instead of +# nist-800-53.rules. Wazuh scans the rules.d directory, so we need to +# ensure the rules file is there with the correct content. + +cat > /etc/audit/rules.d/nist-800-53.rules <<'AUDITRULES' +## ---- Remove any existing rules ---- +-D + +## ---- Buffer size ---- +-b 8192 + +## ---- Failure mode ---- +-f 1 + +## ---- Time changes (35734) ---- +-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change +-a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k time-change +-w /etc/localtime -p wa -k time-change + +## ---- User/group changes (35737) ---- +-w /etc/passwd -p wa -k identity +-w /etc/group -p wa -k identity +-w /etc/shadow -p wa -k identity +-w /etc/gshadow -p wa -k identity +-w /etc/security/opasswd -p wa -k identity + +## ---- Network configuration (35735) ---- +-a always,exit -F arch=b64 -S sethostname,setdomainname -k network-config +-a always,exit -F arch=b32 -S sethostname,setdomainname -k network-config +-w /etc/hosts -p wa -k network-config +-w /etc/hostname -p wa -k network-config +-w /etc/netplan/ -p wa -k network-config +-w /etc/network/ -p wa -k network-config +-w /etc/networks -p wa -k network-config + +## ---- Login/logout events (35741) ---- +-w /var/log/lastlog -p wa -k logins +-w /var/log/faillog -p wa -k logins +-w /var/log/wtmp -p wa -k logins +-w /var/log/btmp -p wa -k logins + +## ---- Session initiation (35740) ---- +-w /var/run/utmp -p wa -k session + +## ---- Sudo scope changes (35731) ---- +-w /etc/sudoers -p wa -k scope +-w /etc/sudoers.d -p wa -k scope + +## ---- Sudo log (35733) ---- +-w /var/log/sudo.log -p wa -k actions + +## ---- Actions as another user (35732) ---- +-a always,exit -F arch=b64 -S execve -C euid!=uid -F auid!=unset -k user_emulation +-a always,exit -F arch=b32 -S execve -C euid!=uid -F auid!=unset -k user_emulation + +## ---- Privileged commands ---- +-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=4294967295 -k privileged +-a always,exit -F arch=b32 -S execve -F euid=0 -F auid>=1000 -F auid!=4294967295 -k privileged + +## ---- Unsuccessful file access (35736) ---- +-a always,exit -F arch=b64 -S open,openat,creat,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access +-a always,exit -F arch=b64 -S open,openat,creat,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access +-a always,exit -F arch=b32 -S open,openat,creat,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access +-a always,exit -F arch=b32 -S open,openat,creat,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access + +## ---- DAC permission changes (35738) ---- +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b32 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod + +## ---- File system mounts (35739) ---- +-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts +-a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts + +## ---- File deletion events (35742) ---- +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=4294967295 -k delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=4294967295 -k delete + +## ---- Mandatory access control changes (35743) ---- +-w /etc/apparmor/ -p wa -k MAC-policy +-w /etc/apparmor.d/ -p wa -k MAC-policy + +## ---- chcon (35744) ---- +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=4294967295 -k perm_chng + +## ---- setfacl (35745) ---- +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=4294967295 -k perm_chng + +## ---- chacl (35746) ---- +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=4294967295 -k perm_chng + +## ---- usermod (35747) ---- +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=4294967295 -k usermod + +## ---- Kernel module loading (35748) ---- +-a always,exit -F arch=b64 -S init_module,finit_module,delete_module,create_module,query_module -k kernel_modules +-a always,exit -F arch=b32 -S init_module,finit_module,delete_module,create_module,query_module -k kernel_modules +-w /sbin/insmod -p x -k kernel_modules +-w /sbin/rmmod -p x -k kernel_modules +-w /sbin/modprobe -p x -k kernel_modules +-w /bin/kmod -p x -k kernel_modules + +## ---- Make configuration immutable ---- +-e 2 +AUDITRULES + +chmod 640 /etc/audit/rules.d/nist-800-53.rules + +# Remove old rules file if it exists and is different +if [[ -f /etc/audit/rules.d/cis-nist.rules ]]; then + rm -f /etc/audit/rules.d/cis-nist.rules + echo " ✓ Removed old cis-nist.rules" +fi + +# Load the new rules +augenrules --load 2>/dev/null || true +systemctl restart auditd 2>/dev/null || true + +echo " ✓ nist-800-53.rules created and loaded" +echo " Rules loaded: $(auditctl -l 2>/dev/null | wc -l)" + +############################################################################### +banner "FIX 12: AIDE audit tool coverage (35760)" +############################################################################### + +AIDE_CUSTOM="/etc/aide/aide.conf.d/99_nist_custom" +if [[ -f "$AIDE_CUSTOM" ]]; then + # Check if audit tools are already covered + if ! grep -q 'auditctl' "$AIDE_CUSTOM"; then + cat >> "$AIDE_CUSTOM" <<'EOF' + +# CIS 4.1.4.11 - Audit tool integrity +/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +EOF + echo " ✓ Audit tool integrity rules added to AIDE config" + else + echo " ✓ Audit tools already in AIDE config" + fi +else + # Create the file + cat > "$AIDE_CUSTOM" <<'EOF' +# NIST 800-53 SI-7: File integrity monitoring +/boot Full +/bin Full +/sbin Full +/lib Full +/lib64 Full +/usr/bin Full +/usr/sbin Full +/usr/lib Full +/etc Full +!/etc/mtab +!/etc/adjtime +!/var/log +!/var/spool +!/var/cache + +# CIS 4.1.4.11 - Audit tool integrity +/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +EOF + echo " ✓ AIDE custom config created with audit tool coverage" +fi + +echo " NOTE: Run 'sudo aideinit -y -f' to rebuild the database (takes 10-15 min)" + +############################################################################### +banner "FIX 13: at.allow — add admin user (35601)" +############################################################################### + +if [[ -f /etc/at.allow ]]; then + if ! grep -q "$ADMIN_USER" /etc/at.allow; then + echo "$ADMIN_USER" >> /etc/at.allow + echo " ✓ $ADMIN_USER added to /etc/at.allow" + else + echo " ✓ $ADMIN_USER already in /etc/at.allow" + fi +fi + +# Same for cron.allow +if [[ -f /etc/cron.allow ]]; then + if ! grep -q "$ADMIN_USER" /etc/cron.allow; then + echo "$ADMIN_USER" >> /etc/cron.allow + echo " ✓ $ADMIN_USER added to /etc/cron.allow" + fi +fi + +############################################################################### +banner "COMPLETE" +############################################################################### + +cat <<'DONE' + +╔══════════════════════════════════════════════════════════════════╗ +║ SCA Remediation Complete ║ +╠══════════════════════════════════════════════════════════════════╣ +║ ║ +║ Fixed: ║ +║ • Filesystem module blacklist file created ║ +║ • Network protocol module blacklist file created ║ +║ • USB storage module fixed (/bin/false) ║ +║ • initramfs rebuilt with blacklists ║ +║ • log_martians enabled at runtime ║ +║ • nftables package purged ║ +║ • SSH config merged from drop-in into main sshd_config ║ +║ • Password aging set for root and nobody ║ +║ • Journald MaxRetentionSec added ║ +║ • Log file permissions tightened ║ +║ • Full audit ruleset created in nist-800-53.rules ║ +║ • AIDE audit tool integrity rules added ║ +║ • at.allow / cron.allow updated ║ +║ ║ +║ Known exceptions (suppress in Wazuh): ║ +║ • 35540 GRUB password (manual step) ║ +║ • 35589 systemd-timesyncd (chrony is active) ║ +║ • 35608 ip_forward=1 (Docker requires it) ║ +║ • 35710 journal-upload (using Wazuh instead) ║ +║ • 35720 remote syslog (using Wazuh instead) ║ +║ • 35626-35639 firewall cross-checks (using UFW only) ║ +║ ║ +║ Still needed: ║ +║ • Rebuild AIDE database: sudo aideinit -y -f ║ +║ • Reboot to apply module blacklists ║ +║ • Re-run sca-verify.sh to confirm fixes ║ +║ • Re-run Wazuh SCA scan ║ +║ ║ +╚══════════════════════════════════════════════════════════════════╝ +DONE \ No newline at end of file