1186 lines
48 KiB
Bash
1186 lines
48 KiB
Bash
#!/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 <<SSHEOF
|
||
# --------------- NIST 800-53 SSH Hardening ---------------
|
||
Protocol 2
|
||
|
||
# Authentication
|
||
PermitRootLogin no
|
||
PubkeyAuthentication yes
|
||
PasswordAuthentication no
|
||
PermitEmptyPasswords no
|
||
ChallengeResponseAuthentication no
|
||
UsePAM yes
|
||
MaxAuthTries 3
|
||
LoginGraceTime 60
|
||
|
||
# Access control (CIS 5.2.4 / Wazuh 35643)
|
||
AllowUsers ${ADMIN_USER}
|
||
|
||
# Session
|
||
ClientAliveInterval 300
|
||
ClientAliveCountMax 3
|
||
MaxSessions 3
|
||
MaxStartups 10:30:60
|
||
|
||
# Cryptography (SC-13)
|
||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
|
||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
|
||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
|
||
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
|
||
|
||
# Banner (AC-8)
|
||
Banner /etc/issue.net
|
||
|
||
# Disable forwarding (CIS 5.2.13 / Wazuh 35647)
|
||
DisableForwarding yes
|
||
|
||
# Disable unused auth methods (Wazuh 35648, 35649, 35650, 35660)
|
||
GSSAPIAuthentication no
|
||
HostbasedAuthentication no
|
||
IgnoreRhosts yes
|
||
PermitUserEnvironment no
|
||
|
||
# Disable X11 forwarding
|
||
X11Forwarding no
|
||
|
||
# Logging (AU-3 / CIS 5.2.5 / Wazuh 35653)
|
||
LogLevel VERBOSE
|
||
|
||
# Suppress login info (handled by MOTD)
|
||
PrintLastLog no
|
||
SSHEOF
|
||
|
||
# Remove small Diffie-Hellman moduli
|
||
awk '$5 >= 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 <HASH> 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 <HASH>
|
||
# 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
|
||
╔═══════════════════════════════════════════════════════════════════╗
|
||
║ NIST 800-53 + CIS/Wazuh Controls Applied ║
|
||
╠═══════════════════════════════════════════════════════════════════╣
|
||
║ ║
|
||
║ FIREWALL (iptables) ║
|
||
║ Default deny in/out/forward ║
|
||
║ SSH rate-limited (4/min per source) ║
|
||
║ ICMP type-filtered with rate limiting ║
|
||
║ Outbound: DNS, HTTP/S, NTP, Wazuh only ║
|
||
║ DOCKER-USER chain pre-created ║
|
||
║ Persistent via iptables-persistent ║
|
||
║ ║
|
||
║ ACCESS CONTROL ║
|
||
║ AC-2 Account management, root locked ║
|
||
║ AC-7 Faillock (5 attempts / 15 min) ║
|
||
║ AC-8 Login banners ║
|
||
║ AC-11 Session timeout (15 min, readonly) ║
|
||
║ AC-17 SSH hardened (key-only, AllowUsers) ║
|
||
║ ║
|
||
║ AUDIT ║
|
||
║ AU-2/3 Comprehensive rules (CIS 4.1.3.1–4.1.3.18) ║
|
||
║ AU-4 Log retention: keep_logs, space alerts ║
|
||
║ AU-8 Chrony NTP, systemd-timesyncd disabled ║
|
||
║ ║
|
||
║ AUTHENTICATION ║
|
||
║ IA-5 Password quality (minlen=15, minclass=4, maxseq=3) ║
|
||
║ IA-5 PAM: faillock, pwhistory, nullok removed ║
|
||
║ IA-5 Password aging on existing + future accounts ║
|
||
║ IA-5 opasswd permissions set ║
|
||
║ ║
|
||
║ SYSTEM PROTECTION ║
|
||
║ SC-8 SSH: modern crypto, DisableForwarding, GSSAPI off ║
|
||
║ SC-28 Mount hardening (/dev/shm) ║
|
||
║ SI-2 Unattended security upgrades ║
|
||
║ SI-7 AIDE with audit tool integrity ║
|
||
║ SI-16 ASLR, ptrace, BPF, core dumps disabled ║
|
||
║ ║
|
||
║ CONFIGURATION MANAGEMENT ║
|
||
║ CM-6 File permissions, sudo logging, su restricted ║
|
||
║ CM-7 Services disabled/masked, packages purged ║
|
||
║ CM-7 Filesystem + network modules blacklisted ║
|
||
║ CM-7 USB storage disabled ║
|
||
║ ║
|
||
║ LOGGING ║
|
||
║ Journald: persistent, rotated, size-limited ║
|
||
║ Rsyslog: file mode 0640, remote placeholder ready ║
|
||
║ Sudo: full I/O logging enabled ║
|
||
║ ║
|
||
║ BOOT ║
|
||
║ AppArmor enforced + in GRUB cmdline ║
|
||
║ audit=1 audit_backlog_limit=8192 in GRUB ║
|
||
║ Recovery mode disabled ║
|
||
║ GRUB password: MANUAL STEP (see Section 10) ║
|
||
║ ║
|
||
║ WAZUH SCA FIXES ║
|
||
║ 35509 Filesystem modules blacklisted ║
|
||
║ 35522 /var nodev (set in partition script) ║
|
||
║ 35537 AppArmor in bootloader ║
|
||
║ 35545 Apport removed ║
|
||
║ 35573 rsync removed ║
|
||
║ 35585 telnet removed ║
|
||
║ 35587 ftp removed ║
|
||
║ 35589 systemd-timesyncd masked (chrony used) ║
|
||
║ 35600 cron.allow created ║
|
||
║ 35601 at.allow created ║
|
||
║ 35604 dccp blacklisted ║
|
||
║ 35605 tipc blacklisted ║
|
||
║ 35606 rds blacklisted ║
|
||
║ 35607 sctp blacklisted ║
|
||
║ 35619 Single firewall (iptables only) ║
|
||
║ 35623 Loopback traffic configured ║
|
||
║ 35624 Default deny policy ║
|
||
║ 35641 SSH host key permissions (firstboot) ║
|
||
║ 35643 AllowUsers set ║
|
||
║ 35644 Banner configured ║
|
||
║ 35646 ClientAliveInterval configured ║
|
||
║ 35647 DisableForwarding yes ║
|
||
║ 35648 GSSAPIAuthentication no ║
|
||
║ 35649 HostbasedAuthentication no ║
|
||
║ 35650 IgnoreRhosts yes ║
|
||
║ 35652 LoginGraceTime configured ║
|
||
║ 35653 LogLevel VERBOSE ║
|
||
║ 35655 MaxAuthTries 3 ║
|
||
║ 35656 MaxSessions 3 ║
|
||
║ 35657 MaxStartups configured ║
|
||
║ 35658 PermitEmptyPasswords no ║
|
||
║ 35659 PermitRootLogin no ║
|
||
║ 35660 PermitUserEnvironment no ║
|
||
║ 35661 UsePAM yes ║
|
||
║ 35664 Sudo logfile configured ║
|
||
║ 35668 su restricted to sudo group ║
|
||
║ 35672 pam_unix enabled ║
|
||
║ 35673 pam_faillock enabled ║
|
||
║ 35675 pam_pwhistory enabled ║
|
||
║ 35681 Password complexity (minclass=4) ║
|
||
║ 35683 maxsequence=3 ║
|
||
║ 35687 Password history remember=24 ║
|
||
║ 35688 enforce_for_root on pwhistory ║
|
||
║ 35689 use_authtok on pwhistory ║
|
||
║ 35690 nullok removed ║
|
||
║ 35694 Password expiration (60 days) ║
|
||
║ 35695 Minimum password days (1) ║
|
||
║ 35698 Inactive lock (30 days) ║
|
||
║ 35703 Root umask 0077 ║
|
||
║ 35708 Journald rotation configured ║
|
||
║ 35719 Rsyslog file creation mode 0640 ║
|
||
║ 35722 Log file permissions restricted ║
|
||
║ 35725 audit=1 in GRUB ║
|
||
║ 35726 audit_backlog_limit in GRUB ║
|
||
║ 35728 max_log_file_action = keep_logs ║
|
||
║ 35729 admin_space_left_action = halt ║
|
||
║ 35730 space_left_action = email ║
|
||
║ 35731-35748 All audit rules present ║
|
||
║ 35752 Audit config file permissions ║
|
||
║ 35755 Audit tool permissions ║
|
||
║ 35760 AIDE covers audit tools ║
|
||
║ 35770 opasswd permissions ║
|
||
║ ║
|
||
║ STILL REQUIRES MANUAL ACTION: ║
|
||
║ 35540 Set GRUB password (see Section 10) ║
|
||
║ 35710 Journal-upload auth (only if using journal-upload) ║
|
||
║ 35720 Remote syslog (edit /etc/rsyslog.d/99-remote.conf ║
|
||
║ or use Wazuh agent as compensating control) ║
|
||
║ ║
|
||
╠═══════════════════════════════════════════════════════════════════╣
|
||
║ NEXT STEPS: ║
|
||
║ 1. Set GRUB password (Section 10) ║
|
||
║ 2. Verify: sudo sshd -T | grep -i allowusers ║
|
||
║ 3. Verify: sudo iptables -L -v -n ║
|
||
║ 4. Review $LOG ║
|
||
║ 5. Reboot and re-run Wazuh SCA scan ║
|
||
║ 6. Shut down → qm template <VMID> ║
|
||
╚═══════════════════════════════════════════════════════════════════╝
|
||
SUMMARY
|
||
|
||
echo ""
|
||
echo "Full log: $LOG"
|
||
echo "Reboot recommended: shutdown -r now"
|