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"
|