From cd9463f3a22687f16df2427b0ddf8637299a45f1 Mon Sep 17 00:00:00 2001 From: Christopher Berger Date: Thu, 28 May 2026 02:31:39 +0000 Subject: [PATCH] Upload files to "/" --- nist-800-53-harden-v2.sh | 1185 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1185 insertions(+) create mode 100644 nist-800-53-harden-v2.sh diff --git a/nist-800-53-harden-v2.sh b/nist-800-53-harden-v2.sh new file mode 100644 index 0000000..6993528 --- /dev/null +++ b/nist-800-53-harden-v2.sh @@ -0,0 +1,1185 @@ +#!/usr/bin/env bash +############################################################################### +# NIST 800-53 Hardening Script v2 – Ubuntu 24.04 LTS (Proxmox VM Template) +# +# CHANGES FROM v1: +# - iptables instead of UFW (Docker-compatible, CIS-clean) +# - Fixes: ssh service name (not sshd), TMOUT readonly guard, +# AIDE mkdir ordering, auditd.conf inline edits +# - Expanded audit rules (chcon, setfacl, chacl, usermod, mount, DAC, etc.) +# - CIS/Wazuh SCA alignment: kernel module blacklisting, cron/at allow +# files, journald rotation, sudo logging, nullok removal, GRUB audit +# flags, root umask, log permissions, opasswd, rsyslog file creation +# mode, apport disabled, telnet/ftp/rsync removed, AppArmor in GRUB, +# SSH AllowUsers, GSSAPIAuthentication, DisableForwarding, +# HostbasedAuthentication, IgnoreRhosts, PermitUserEnvironment +# +# Maps to control families: AC, AU, CM, IA, SC, SI, MP +# Run as root on a freshly installed Ubuntu 24.04 minimal server after +# running the partition-layout.sh script, then convert to Proxmox template. +# +# Usage: chmod +x nist-800-53-harden-v2.sh && sudo ./nist-800-53-harden-v2.sh +############################################################################### +set -euo pipefail +export DEBIAN_FRONTEND=noninteractive + +LOG="/var/log/nist-hardening-$(date +%Y%m%d-%H%M%S).log" +exec > >(tee -a "$LOG") 2>&1 + +banner() { printf '\n\e[1;36m>>> %s\e[0m\n' "$1"; } + +if [[ $EUID -ne 0 ]]; then + echo "ERROR: Run this script as root." >&2 + exit 1 +fi + +############################################################################### +# CONFIGURATION — edit these before running +############################################################################### +# The primary non-root admin user (used for SSH AllowUsers, cron.allow, etc.) +ADMIN_USER="${ADMIN_USER:-chris}" + +############################################################################### +# 0. PRE-FLIGHT – Update & install required packages +# CM-2 Baseline Configuration / SI-2 Flaw Remediation +############################################################################### +banner "CM-2 / SI-2: System update and baseline packages" + +apt-get update -y +apt-get upgrade -y +apt-get dist-upgrade -y + +apt-get install -y \ + auditd audispd-plugins \ + aide aide-common \ + libpam-pwquality \ + iptables iptables-persistent netfilter-persistent \ + chrony \ + rsyslog \ + acl \ + apparmor apparmor-utils \ + unattended-upgrades apt-listchanges \ + cloud-init \ + qemu-guest-agent \ + needrestart + +# --------------------------------------------------------------------------- +# CM-7 Remove unnecessary packages (CIS 2.4.x, 2.1.x) +# --------------------------------------------------------------------------- +banner "CM-7: Remove unnecessary packages" + +REMOVE_PKGS=( + telnet inetutils-telnet + ftp tnftp + rsync + ufw + nftables + apport + snapd +) +for pkg in "${REMOVE_PKGS[@]}"; do + if dpkg -s "$pkg" &>/dev/null; then + apt-get purge -y "$pkg" 2>/dev/null || true + fi +done + +apt-get autoremove -y + +# Disable apport if config file remains +if [[ -f /etc/default/apport ]]; then + sed -i 's/^enabled=.*/enabled=0/' /etc/default/apport +fi + +# Disable motd-news network fetches +systemctl disable --now motd-news.timer 2>/dev/null || true +if [[ -f /etc/default/motd-news ]]; then + sed -i 's/^ENABLED=.*/ENABLED=0/' /etc/default/motd-news +fi + +# Disable unnecessary motd scripts +for f in 10-help-text 50-motd-news 88-esm-announce 91-contract-ua-esm-status 91-release-upgrade 95-hwe-eol; do + chmod -x /etc/update-motd.d/$f 2>/dev/null || true +done + +############################################################################### +# 1. ACCESS CONTROL (AC) +############################################################################### + +# --------------------------------------------------------------------------- +# AC-2 Account Management +# --------------------------------------------------------------------------- +banner "AC-2: Account management" + +passwd -l root + +for usr in games lp news uucp proxy gnats; do + if id "$usr" &>/dev/null; then + userdel "$usr" 2>/dev/null || true + fi +done + +# Default umask 027 +sed -i 's/^UMASK.*/UMASK\t\t027/' /etc/login.defs + +# Root umask (CIS 5.5.6 / Wazuh 35703) +for rcfile in /root/.bash_profile /root/.bashrc; do + if [[ -f "$rcfile" ]]; then + sed -i '/^umask/d' "$rcfile" + else + touch "$rcfile" + fi + echo "umask 0077" >> "$rcfile" +done + +# --------------------------------------------------------------------------- +# AC-7 Unsuccessful Logon Attempts – pam_faillock +# --------------------------------------------------------------------------- +banner "AC-7: Account lockout (pam_faillock)" + +cat > /etc/security/faillock.conf <<'EOF' +# AC-7: Lock account after 5 failed attempts for 900 s +deny = 5 +fail_interval = 900 +unlock_time = 900 +even_deny_root +EOF + +# Write pam_faillock into common-auth +# Remove any existing faillock lines first to avoid duplicates +sed -i '/pam_faillock/d' /etc/pam.d/common-auth +sed -i '/pam_faillock/d' /etc/pam.d/common-account + +# Insert before pam_unix +sed -i '/^auth.*pam_unix.so/i auth required pam_faillock.so preauth' \ + /etc/pam.d/common-auth +sed -i '/^auth.*pam_unix.so/a auth [default=die] pam_faillock.so authfail' \ + /etc/pam.d/common-auth + +echo "account required pam_faillock.so" >> /etc/pam.d/common-account + +# --------------------------------------------------------------------------- +# AC-8 System Use Notification – login banners +# --------------------------------------------------------------------------- +banner "AC-8: Login banners" + +BANNER_TEXT="======================================================================== +WARNING: This system is for authorized use only. +All activity is monitored and recorded. Unauthorized access is prohibited +and subject to criminal and civil penalties. +========================================================================" + +echo "$BANNER_TEXT" > /etc/issue +echo "$BANNER_TEXT" > /etc/issue.net +cat > /etc/motd <<'EOF' +**** NOTICE: Use of this system constitutes consent to monitoring. **** +EOF + +# --------------------------------------------------------------------------- +# AC-11 / AC-12 Session Lock / Termination +# --------------------------------------------------------------------------- +banner "AC-11 / AC-12: Session timeout" + +cat > /etc/profile.d/nist-timeout.sh <<'EOF' +# AC-11/12: Idle session timeout – 15 minutes +if [ -z "${TMOUT:-}" ]; then + readonly TMOUT=900 + export TMOUT +fi +EOF +chmod 644 /etc/profile.d/nist-timeout.sh + +############################################################################### +# 2. AUDIT AND ACCOUNTABILITY (AU) +############################################################################### +banner "AU-2 / AU-3 / AU-12: Audit configuration" + +# --------------------------------------------------------------------------- +# AU-2/3/12 Audit Events – comprehensive rules for CIS/Wazuh +# --------------------------------------------------------------------------- +cat > /etc/audit/rules.d/nist-800-53.rules <<'AUDITRULES' +## ---- Remove any existing rules ---- +-D + +## ---- Buffer size ---- +-b 8192 + +## ---- Failure mode: 1 = printk, 2 = panic ---- +-f 1 + +## ---- Time changes (AU-8 / CIS 4.1.3.4 / Wazuh 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 (AC-2 / CIS 4.1.3.7 / Wazuh 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 (CIS 4.1.3.5 / Wazuh 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 (CIS 4.1.3.10 / Wazuh 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 (CIS 4.1.3.11 / Wazuh 35740) ---- +-w /var/run/utmp -p wa -k session + +## ---- Sudo configuration changes (CIS 4.1.3.1 / Wazuh 35731) ---- +-w /etc/sudoers -p wa -k scope +-w /etc/sudoers.d -p wa -k scope + +## ---- Sudo log file (CIS 4.1.3.3 / Wazuh 35733) ---- +-w /var/log/sudo.log -p wa -k actions + +## ---- Actions as another user (CIS 4.1.3.2 / Wazuh 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 (CIS 4.1.3.6 / Wazuh 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 (CIS 4.1.3.8 / Wazuh 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 + +## ---- Successful file system mounts (CIS 4.1.3.9 / Wazuh 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 (CIS 4.1.3.12 / Wazuh 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 (CIS 4.1.3.13 / Wazuh 35743) ---- +-w /etc/apparmor/ -p wa -k MAC-policy +-w /etc/apparmor.d/ -p wa -k MAC-policy + +## ---- chcon command (CIS 4.1.3.14 / Wazuh 35744) ---- +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=4294967295 -k perm_chng + +## ---- setfacl command (CIS 4.1.3.15 / Wazuh 35745) ---- +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=4294967295 -k perm_chng + +## ---- chacl command (CIS 4.1.3.16 / Wazuh 35746) ---- +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=4294967295 -k perm_chng + +## ---- usermod command (CIS 4.1.3.17 / Wazuh 35747) ---- +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=4294967295 -k usermod + +## ---- Kernel module loading (CIS 4.1.3.18 / Wazuh 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 the audit configuration immutable (requires reboot to change) ---- +-e 2 +AUDITRULES + +# --------------------------------------------------------------------------- +# AU-4 Audit Log Storage (CIS 4.1.2.x / Wazuh 35728-35730) +# --------------------------------------------------------------------------- +banner "AU-4: Audit log retention" + +# Edit auditd.conf inline +AUDITD_CONF="/etc/audit/auditd.conf" +cp "$AUDITD_CONF" "${AUDITD_CONF}.bak" + +sed -i 's/^max_log_file_action.*/max_log_file_action = keep_logs/' "$AUDITD_CONF" +sed -i 's/^space_left_action.*/space_left_action = email/' "$AUDITD_CONF" +sed -i 's/^admin_space_left_action.*/admin_space_left_action = halt/' "$AUDITD_CONF" + +# Add space thresholds if not present +grep -q '^space_left ' "$AUDITD_CONF" || echo "space_left = 75" >> "$AUDITD_CONF" +grep -q '^admin_space_left ' "$AUDITD_CONF" || echo "admin_space_left = 50" >> "$AUDITD_CONF" + +# Ensure max_log_file is set +grep -q '^max_log_file ' "$AUDITD_CONF" || sed -i '1i max_log_file = 50' "$AUDITD_CONF" + +# Set permissions on audit config files (CIS 4.1.4.5 / Wazuh 35752) +find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) -exec chmod 640 {} \; +chown -R root:root /etc/audit/ + +# Set permissions on audit tools (CIS 4.1.4.9 / Wazuh 35755) +chmod 755 /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules 2>/dev/null || true + +systemctl enable auditd +systemctl restart auditd || augenrules --load + +# --------------------------------------------------------------------------- +# AU-8 Timestamps / NTP (chrony) +# --------------------------------------------------------------------------- +banner "AU-8: NTP time synchronization (chrony)" + +cat > /etc/chrony/chrony.conf <<'EOF' +# NIST 800-53 AU-8: Authoritative time source +pool ntp.ubuntu.com iburst maxsources 4 +pool time.nist.gov iburst maxsources 2 + +keyfile /etc/chrony/chrony.keys +driftfile /var/lib/chrony/chrony.drift +logdir /var/log/chrony +maxupdateskew 100.0 +rtcsync +makestep 1 3 +EOF + +systemctl enable chrony +systemctl restart chrony + +# Disable systemd-timesyncd (Wazuh 35589 wants one or the other) +systemctl stop systemd-timesyncd 2>/dev/null || true +systemctl disable systemd-timesyncd 2>/dev/null || true +systemctl mask systemd-timesyncd 2>/dev/null || true + +############################################################################### +# 3. IDENTIFICATION AND AUTHENTICATION (IA) +############################################################################### + +# --------------------------------------------------------------------------- +# IA-5 Password Quality (CIS 5.4.x / Wazuh 35681, 35683) +# --------------------------------------------------------------------------- +banner "IA-5: Password quality (pam_pwquality)" + +cat > /etc/security/pwquality.conf <<'EOF' +# IA-5(1): Password complexity (CIS 5.4.1) +minlen = 15 +dcredit = -1 +ucredit = -1 +lcredit = -1 +ocredit = -1 +minclass = 4 +difok = 8 +maxrepeat = 3 +maxsequence = 3 +maxclassrepeat = 4 +gecoscheck = 1 +dictcheck = 1 +enforcing = 1 +EOF + +# Password aging (IA-5 / CIS 5.5.1 / Wazuh 35694, 35695) +sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS\t60/' /etc/login.defs +sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS\t1/' /etc/login.defs +sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE\t14/' /etc/login.defs + +# Set INACTIVE period for new accounts (CIS 5.5.3 / Wazuh 35698) +useradd -D -f 30 + +# Apply aging to existing admin user +if id "$ADMIN_USER" &>/dev/null; then + chage -M 60 -m 1 -W 14 -I 30 "$ADMIN_USER" +fi + +# --------------------------------------------------------------------------- +# IA-5 PAM configuration (CIS 5.3.x / Wazuh 35672, 35673, 35675, 35687-35690) +# --------------------------------------------------------------------------- +banner "IA-5: PAM hardening" + +# Remove nullok from all PAM files (Wazuh 35690) +sed -i 's/ nullok//g' /etc/pam.d/common-auth +sed -i 's/ nullok//g' /etc/pam.d/common-password + +# pam_pwhistory: remember last 24 passwords (Wazuh 35675, 35687, 35688, 35689) +sed -i '/pam_pwhistory/d' /etc/pam.d/common-password +sed -i '/^password.*pam_unix.so/i password required pam_pwhistory.so remember=24 use_authtok enforce_for_root retry=3' \ + /etc/pam.d/common-password + +# Ensure pam_unix is configured properly (Wazuh 35672) +# pam_unix should be present in common-auth, common-account, common-password, common-session +# Verify — these should already exist from the default Ubuntu PAM config + +# Create opasswd file with correct permissions (Wazuh 35770) +touch /etc/security/opasswd /etc/security/opasswd.old +chmod 600 /etc/security/opasswd /etc/security/opasswd.old +chown root:root /etc/security/opasswd /etc/security/opasswd.old + +############################################################################### +# 4. SYSTEM AND COMMUNICATIONS PROTECTION (SC) +############################################################################### + +# --------------------------------------------------------------------------- +# SC-5 / SC-7 Firewall – iptables with persistent rules +# --------------------------------------------------------------------------- +banner "SC-5 / SC-7: iptables firewall" + +# Flush existing rules +iptables -F +iptables -X +iptables -Z +ip6tables -F +ip6tables -X +ip6tables -Z + +# ---- DEFAULT POLICIES: deny all ---- +iptables -P INPUT DROP +iptables -P FORWARD DROP +iptables -P OUTPUT DROP + +ip6tables -P INPUT DROP +ip6tables -P FORWARD DROP +ip6tables -P OUTPUT DROP + +# ---- LOOPBACK (CIS 3.5.3.3.1 / Wazuh 35623) ---- +iptables -A INPUT -i lo -j ACCEPT +iptables -A OUTPUT -o lo -j ACCEPT +iptables -A INPUT ! -i lo -s 127.0.0.0/8 -j DROP + +ip6tables -A INPUT -i lo -j ACCEPT +ip6tables -A OUTPUT -o lo -j ACCEPT +ip6tables -A INPUT ! -i lo -s ::1 -j DROP + +# ---- CONNECTION TRACKING ---- +iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +iptables -A INPUT -m conntrack --ctstate INVALID -j DROP + +ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +ip6tables -A INPUT -m conntrack --ctstate INVALID -j DROP + +# ---- ICMP CONTROL ---- +# Allow essential ICMP types, drop the rest +iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT +iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT +iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT +# Rate-limit inbound pings +iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 4 -j ACCEPT +iptables -A INPUT -p icmp -j DROP + +iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT +iptables -A OUTPUT -p icmp --icmp-type destination-unreachable -j ACCEPT +iptables -A OUTPUT -p icmp --icmp-type time-exceeded -j ACCEPT + +ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT +ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT +ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT +ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -m limit --limit 1/s --limit-burst 4 -j ACCEPT +ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT +ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT +ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT +ip6tables -A INPUT -p icmpv6 -j DROP + +ip6tables -A OUTPUT -p icmpv6 -j ACCEPT + +# ---- INBOUND SERVICES ---- +# SSH – rate limited: max 4 new connections per 60 seconds per source IP +iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ + -m recent --set --name SSH --rsource +iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ + -m recent --update --seconds 60 --hitcount 4 --name SSH --rsource -j DROP +iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT + +ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ + -m recent --set --name SSH --rsource +ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \ + -m recent --update --seconds 60 --hitcount 4 --name SSH --rsource -j DROP +ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT + +# ---- OUTBOUND SERVICES ---- +# DNS +iptables -A OUTPUT -p udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT +iptables -A OUTPUT -p tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT + +# HTTP/HTTPS (apt updates, downloads) +iptables -A OUTPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT +iptables -A OUTPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT + +# NTP +iptables -A OUTPUT -p udp --dport 123 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p udp --dport 123 -m conntrack --ctstate NEW -j ACCEPT + +# Wazuh agent (remove if not using Wazuh) +iptables -A OUTPUT -p tcp --dport 1514 -m conntrack --ctstate NEW -j ACCEPT +iptables -A OUTPUT -p tcp --dport 1515 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p tcp --dport 1514 -m conntrack --ctstate NEW -j ACCEPT +ip6tables -A OUTPUT -p tcp --dport 1515 -m conntrack --ctstate NEW -j ACCEPT + +# ---- LOGGING dropped packets ---- +iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPT_INPUT_DROP: " --log-level 4 +iptables -A FORWARD -m limit --limit 5/min -j LOG --log-prefix "IPT_FORWARD_DROP: " --log-level 4 +iptables -A OUTPUT -m limit --limit 5/min -j LOG --log-prefix "IPT_OUTPUT_DROP: " --log-level 4 + +ip6tables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IP6T_INPUT_DROP: " --log-level 4 +ip6tables -A FORWARD -m limit --limit 5/min -j LOG --log-prefix "IP6T_FORWARD_DROP: " --log-level 4 +ip6tables -A OUTPUT -m limit --limit 5/min -j LOG --log-prefix "IP6T_OUTPUT_DROP: " --log-level 4 + +# ---- DOCKER-USER chain (pre-create for Docker compatibility) ---- +iptables -N DOCKER-USER 2>/dev/null || true +iptables -A DOCKER-USER -j RETURN 2>/dev/null || true + +# ---- SAVE RULES ---- +mkdir -p /etc/iptables +iptables-save > /etc/iptables/rules.v4 +ip6tables-save > /etc/iptables/rules.v6 + +# Enable persistence +systemctl enable netfilter-persistent + +echo "iptables rules saved and persistent." +echo "Current INPUT policy: $(iptables -L INPUT | head -1)" +echo "Current OUTPUT policy: $(iptables -L OUTPUT | head -1)" + +# --------------------------------------------------------------------------- +# SC-8 / SC-13 SSH hardening +# --------------------------------------------------------------------------- +banner "SC-8 / SC-13: SSH hardening" + +cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak + +cat > /etc/ssh/sshd_config.d/nist-hardening.conf <= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe && mv /etc/ssh/moduli.safe /etc/ssh/moduli + +# Test and restart (Ubuntu 24.04 uses 'ssh' not 'sshd') +sshd -t && systemctl restart ssh + +# Set permissions on SSH host key files (CIS 5.2.1-5.2.2 / Wazuh 35641, 35642) +find /etc/ssh -name 'ssh_host_*_key' -exec chmod 600 {} \; +find /etc/ssh -name 'ssh_host_*_key.pub' -exec chmod 644 {} \; +chown root:root /etc/ssh/ssh_host_* +chmod 600 /etc/ssh/sshd_config + +# --------------------------------------------------------------------------- +# SC-28 Filesystem mount hardening +# --------------------------------------------------------------------------- +banner "SC-28 / MP-2: Filesystem mount hardening" + +# Harden /dev/shm +if grep -q '/dev/shm' /etc/fstab; then + sed -i 's|^\(.*\s/dev/shm\s.*\)defaults\(.*\)|\1defaults,noexec,nosuid,nodev\2|' /etc/fstab +else + echo "tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0" >> /etc/fstab +fi + +############################################################################### +# 5. KERNEL HARDENING (SC / SI) +############################################################################### +banner "SC-3 / SC-39 / SI-16: Kernel parameter hardening" + +cat > /etc/sysctl.d/99-nist-hardening.conf <<'SYSCTL' +# ----- Network (SC-5 / SC-7) ----- +net.ipv4.ip_forward = 0 +net.ipv6.conf.all.forwarding = 0 + +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv6.conf.all.accept_source_route = 0 +net.ipv6.conf.default.accept_source_route = 0 + +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv6.conf.all.accept_redirects = 0 +net.ipv6.conf.default.accept_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.default.secure_redirects = 0 + +net.ipv4.tcp_syncookies = 1 + +net.ipv4.conf.all.log_martians = 1 +net.ipv4.conf.default.log_martians = 1 + +net.ipv4.icmp_echo_ignore_broadcasts = 1 +net.ipv4.icmp_ignore_bogus_error_responses = 1 + +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.default.rp_filter = 1 + +net.ipv6.conf.all.accept_ra = 0 +net.ipv6.conf.default.accept_ra = 0 + +net.ipv4.tcp_timestamps = 0 +net.ipv4.tcp_max_syn_backlog = 4096 + +# ----- Kernel (SC-3 / SI-16) ----- +kernel.kptr_restrict = 2 +kernel.dmesg_restrict = 1 +kernel.perf_event_paranoid = 3 +kernel.randomize_va_space = 2 +kernel.yama.ptrace_scope = 2 +fs.suid_dumpable = 0 + +kernel.unprivileged_bpf_disabled = 1 +net.core.bpf_jit_harden = 2 +kernel.unprivileged_userns_clone = 0 + +# Additional CIS hardened parameters +fs.protected_fifos = 2 +fs.protected_hardlinks = 1 +fs.protected_regular = 2 +fs.protected_symlinks = 1 +SYSCTL + +sysctl --system + +# Disable core dumps via limits +cat > /etc/security/limits.d/nist-coredump.conf <<'EOF' +* hard core 0 +EOF + +# Systemd core dump disable +mkdir -p /etc/systemd/coredump.conf.d +cat > /etc/systemd/coredump.conf.d/disable.conf <<'EOF' +[Coredump] +Storage=none +ProcessSizeMax=0 +EOF + +############################################################################### +# 6. CONFIGURATION MANAGEMENT (CM) +############################################################################### + +# --------------------------------------------------------------------------- +# CM-7 Disable unnecessary services and kernel modules +# --------------------------------------------------------------------------- +banner "CM-7: Disable unnecessary services and protocols" + +DISABLE_SVCS=( + avahi-daemon cups bluetooth isc-dhcp-server rpcbind + nfs-server vsftpd apache2 nginx ModemManager rsync +) +for svc in "${DISABLE_SVCS[@]}"; do + if systemctl list-unit-files "${svc}.service" &>/dev/null; then + systemctl disable --now "$svc" 2>/dev/null || true + systemctl mask "$svc" 2>/dev/null || true + fi +done + +# Disable uncommon filesystems (CIS 1.1.1.x / Wazuh 35509) +cat > /etc/modprobe.d/cis-filesystems.conf <<'EOF' +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 +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 + +blacklist cramfs +blacklist freevxfs +blacklist jffs2 +blacklist hfs +blacklist hfsplus +blacklist squashfs +blacklist udf +blacklist afs +blacklist ceph +blacklist cifs +blacklist exfat +blacklist fat +blacklist fscache +blacklist fuse +blacklist gfs2 +blacklist nfs_common +blacklist nfsd +blacklist smbfs_common +EOF + +# Disable uncommon network protocols (CIS 3.1.x / Wazuh 35604-35607) +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 + +# Disable USB storage (MP-7) +cat > /etc/modprobe.d/cis-usb-storage.conf <<'EOF' +install usb-storage /bin/false +blacklist usb-storage +EOF + +# --------------------------------------------------------------------------- +# CM-6 File permissions and access control +# --------------------------------------------------------------------------- +banner "CM-6: Critical file permissions" + +chmod 600 /etc/shadow /etc/gshadow +chmod 644 /etc/passwd /etc/group +chmod 600 /boot/grub/grub.cfg 2>/dev/null || true +chmod 700 /root +chmod 600 /etc/crontab +chmod 700 /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.monthly /etc/cron.weekly +chmod 600 /etc/ssh/sshd_config + +# Restrict cron and at to authorized users (CIS 5.1.8-5.1.9 / Wazuh 35600, 35601) +touch /etc/cron.allow /etc/at.allow +chmod 640 /etc/cron.allow /etc/at.allow +chown root:root /etc/cron.allow /etc/at.allow +echo "root" > /etc/cron.allow +echo "$ADMIN_USER" >> /etc/cron.allow +echo "root" > /etc/at.allow +echo "$ADMIN_USER" >> /etc/at.allow + +# Remove cron.deny and at.deny (CIS wants allow-files only) +rm -f /etc/cron.deny /etc/at.deny + +# Fix log file permissions (CIS 4.2.3 / Wazuh 35722) +find /var/log -type f -exec chmod g-wx,o-rwx {} + +find /var/log -type d -exec chmod g-wx,o-rwx {} + + +# Restrict su to sudo group (AC-6 / Wazuh 35668) +# Remove existing pam_wheel lines and add the correct one +sed -i '/pam_wheel.so/d' /etc/pam.d/su +echo "auth required pam_wheel.so use_uid group=sudo" >> /etc/pam.d/su + +# --------------------------------------------------------------------------- +# Sudo logging (CIS 5.3.4 / Wazuh 35664) +# --------------------------------------------------------------------------- +banner "CM-6: Sudo logging" + +cat > /etc/sudoers.d/nist-logging <<'EOF' +Defaults logfile="/var/log/sudo.log" +Defaults log_input,log_output +Defaults iolog_dir="/var/log/sudo-io" +EOF +chmod 440 /etc/sudoers.d/nist-logging +mkdir -p /var/log/sudo-io + +############################################################################### +# 7. SYSTEM AND INFORMATION INTEGRITY (SI) +############################################################################### + +# --------------------------------------------------------------------------- +# SI-2 Automatic security updates +# --------------------------------------------------------------------------- +banner "SI-2: Automatic security updates" + +cat > /etc/apt/apt.conf.d/50unattended-upgrades <<'EOF' +Unattended-Upgrade::Allowed-Origins { + "${distro_id}:${distro_codename}-security"; + "${distro_id}ESMApps:${distro_codename}-apps-security"; + "${distro_id}ESM:${distro_codename}-infra-security"; +}; +Unattended-Upgrade::AutoFixInterruptedDpkg "true"; +Unattended-Upgrade::Remove-Unused-Dependencies "true"; +Unattended-Upgrade::Automatic-Reboot "false"; +Unattended-Upgrade::Mail "root"; +EOF + +cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF' +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +APT::Periodic::Download-Upgradeable-Packages "1"; +APT::Periodic::AutocleanInterval "7"; +EOF + +# --------------------------------------------------------------------------- +# SI-7 AIDE file integrity monitoring +# --------------------------------------------------------------------------- +banner "SI-7: AIDE file integrity database" + +# Configure AIDE to also cover audit tools (CIS 4.1.4.11 / Wazuh 35760) +cat > /etc/aide/aide.conf.d/99_nist_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 + +aideinit -y -f 2>/dev/null || true + +cat > /etc/cron.daily/aide-check <<'AIDECRON' +#!/bin/bash +/usr/bin/aide --check --config=/etc/aide/aide.conf | /usr/bin/mail -s "AIDE report: $(hostname)" root 2>/dev/null +AIDECRON +chmod 700 /etc/cron.daily/aide-check + +############################################################################### +# 8. LOGGING (AU / Wazuh 35708, 35719, 35720) +############################################################################### +banner "AU: Logging configuration" + +# Journald rotation (CIS 4.2.1.3 / Wazuh 35708) +mkdir -p /etc/systemd/journald.conf.d +cat > /etc/systemd/journald.conf.d/nist.conf <<'EOF' +[Journal] +Compress=yes +Storage=persistent +ForwardToSyslog=yes +SystemMaxUse=500M +SystemKeepFree=1G +SystemMaxFileSize=50M +MaxRetentionSec=1month +EOF + +systemctl restart systemd-journald + +# Rsyslog file creation mode (CIS 4.2.2.4 / Wazuh 35719) +cat > /etc/rsyslog.d/50-nist.conf <<'EOF' +# CIS 4.2.2.4: Ensure rsyslog log file creation mode is configured +$FileCreateMode 0640 +$DirCreateMode 0750 +$Umask 0027 +EOF + +# Remote logging placeholder (CIS 4.2.2.6 / Wazuh 35720) +# Uncomment and edit if shipping to a remote syslog server +# If using Wazuh, this is handled by the Wazuh agent instead +cat > /etc/rsyslog.d/99-remote.conf <<'EOF' +# CIS 4.2.2.6: Forward logs to remote host +# Uncomment the line below and set your syslog/SIEM server +# *.* action(type="omfwd" target="your-siem.example.com" port="514" protocol="tcp") +EOF + +systemctl restart rsyslog + +############################################################################### +# 9. AppArmor ENFORCEMENT (AC-3 / AC-6) +############################################################################### +banner "AC-3 / AC-6: AppArmor enforcement" + +systemctl enable apparmor +aa-enforce /etc/apparmor.d/* 2>/dev/null || true + +# Ensure AppArmor is in bootloader config (CIS 1.3.1.2 / Wazuh 35537) +GRUB_DEFAULT="/etc/default/grub" +if ! grep -q 'apparmor=1' "$GRUB_DEFAULT"; then + sed -i 's/^GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 apparmor=1 security=apparmor"/' "$GRUB_DEFAULT" +fi + +############################################################################### +# 10. GRUB / BOOT HARDENING (SI-7 / AC-3) +############################################################################### +banner "SI-7 / AC-3: GRUB boot hardening" + +# Enable auditing for processes prior to auditd (CIS 4.1.1.3 / Wazuh 35725) +# Enable audit_backlog_limit (CIS 4.1.1.4 / Wazuh 35726) +if ! grep -q 'audit=1' "$GRUB_DEFAULT"; then + sed -i 's/^GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 audit=1 audit_backlog_limit=8192"/' "$GRUB_DEFAULT" +fi + +# Disable recovery mode +sed -i 's/^#\?GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY="true"/' "$GRUB_DEFAULT" + +update-grub 2>/dev/null || true + +# GRUB password (CIS 1.4.1 / Wazuh 35540) +# ----------------------------------------------------------------------- +# IMPORTANT: Generate your own hash: grub-mkpasswd-pbkdf2 +# Then replace below and uncomment the block. +# ----------------------------------------------------------------------- +# cat > /etc/grub.d/40_custom <<'GRUBPW' +# #!/bin/sh +# exec tail -n +3 $0 +# set superusers="grubadmin" +# password_pbkdf2 grubadmin +# GRUBPW +# chmod 755 /etc/grub.d/40_custom +# update-grub + +############################################################################### +# 11. PROXMOX TEMPLATE PREPARATION +############################################################################### +banner "Proxmox template preparation" + +# Enable QEMU guest agent +systemctl enable qemu-guest-agent + +# Cloud-init config +cat > /etc/cloud/cloud.cfg.d/99_proxmox.cfg <<'EOF' +datasource_list: [NoCloud, ConfigDrive] +EOF + +# Machine-id regenerated on clone +truncate -s 0 /etc/machine-id +rm -f /var/lib/dbus/machine-id +ln -s /etc/machine-id /var/lib/dbus/machine-id 2>/dev/null || true + +# Clear SSH host keys (regenerated on first boot) +rm -f /etc/ssh/ssh_host_* + +# Firstboot service to regenerate keys and AIDE DB +cat > /etc/systemd/system/firstboot-harden.service <<'SVC' +[Unit] +Description=First-boot tasks for hardened template +ConditionPathExists=!/var/lib/firstboot-done +After=network-online.target + +[Service] +Type=oneshot +ExecStart=/bin/bash -c '\ + dpkg-reconfigure openssh-server && \ + systemctl restart ssh && \ + find /etc/ssh -name "ssh_host_*_key" -exec chmod 600 {} \; && \ + find /etc/ssh -name "ssh_host_*_key.pub" -exec chmod 644 {} \; && \ + chown root:root /etc/ssh/ssh_host_* && \ + aideinit -y -f && \ + touch /var/lib/firstboot-done' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +SVC +systemctl enable firstboot-harden.service + +# Rebuild initramfs with module blacklists +update-initramfs -u + +# Clean up +apt-get autoremove -y +apt-get clean +rm -rf /tmp/* /var/tmp/* +find /var/log -type f -exec truncate -s 0 {} \; +history -c + +############################################################################### +# SUMMARY +############################################################################### +banner "NIST 800-53 Hardening v2 Complete" + +cat < ║ +╚═══════════════════════════════════════════════════════════════════╝ +SUMMARY + +echo "" +echo "Full log: $LOG" +echo "Reboot recommended: shutdown -r now"