Improved debian hardening

This commit is contained in:
2025-12-19 20:12:03 +01:00
parent 3baa6a0fd2
commit e5749c224f

View File

@@ -3,7 +3,6 @@ set -euo pipefail
# Debian Hardening Script # Debian Hardening Script
# Adapted from the Arch Linux hardening script (arch-hardening.sh) # Adapted from the Arch Linux hardening script (arch-hardening.sh)
# Run as root or via sudo
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
@@ -22,11 +21,33 @@ log_error() {
# Update system # Update system
log "Updating packages..." log "Updating packages..."
sudo apt-get update sudo apt-get update
sudo apt-get upgrade -y sudo apt-get dist-upgrade -y
# [DEB-0280] Ensure libpam-tmpdir is installed and enabled
sudo apt-get install -y libpam-tmpdir
if ! grep -q "pam_tmpdir.so" /etc/pam.d/common-session 2>/dev/null; then
sudo sed -i "/pam_unix.so/ a session optional pam_tmpdir.so" /etc/pam.d/common-session || true
fi
# [DEB-0810] Ensure apt-listbugs is installed
log "Installing apt-listbugs..."
sudo apt-get install -y apt-listbugs
# [DEB-0811] Ensure apt-listchanges is installed
log "Installing apt-listchanges..."
sudo apt-get install -y apt-listchanges
# [DEB-0831] Ensure needrestart is installed
log "Installing needrestart..."
sudo apt-get install -y needrestart
# [DEB-0880] Ensure fail2ban is installed
log "Installing fail2ban..."
sudo apt-get install -y fail2ban
# [STRG-1846] Disable drivers like firewire # [STRG-1846] Disable drivers like firewire
log "Disabling FireWire kernel modules..." log "Disabling FireWire kernel modules..."
sudo tee /etc/modprobe.d/firewire-disable.conf > /dev/null <<EOL sudo tee /etc/modprobe.d/90-firewire-disable.conf > /dev/null <<EOL
# Disable FireWire kernel modules to prevent unauthorized DMA access # Disable FireWire kernel modules to prevent unauthorized DMA access
blacklist firewire-core blacklist firewire-core
install firewire-core /bin/false install firewire-core /bin/false
@@ -37,19 +58,10 @@ install firewire-sbp2 /bin/false
EOL EOL
# [LOGG-2154] Ensure system log is configured to send logs to a remote log server # [LOGG-2154] Ensure system log is configured to send logs to a remote log server
# Debian uses rsyslog by default; install rsyslog to match standard Debian setups
log "Installing rsyslog..." log "Installing rsyslog..."
sudo apt-get install -y rsyslog sudo apt-get install -y rsyslog
sudo systemctl enable --now rsyslog sudo systemctl enable --now rsyslog
# [NETW-2706] Ensure DNSSEC validation is enabled (systemd-resolved)
log "Enabling DNSSEC in systemd-resolved..."
if [ -f /etc/systemd/resolved.conf ]; then
sudo sed -i '/^DNSSEC=/d' /etc/systemd/resolved.conf || true
echo 'DNSSEC=yes' | sudo tee -a /etc/systemd/resolved.conf > /dev/null
sudo systemctl restart systemd-resolved
fi
# [USB-3000] Ensure USBGUARD is installed and configured # [USB-3000] Ensure USBGUARD is installed and configured
log "Installing usbguard..." log "Installing usbguard..."
sudo apt-get install -y usbguard sudo apt-get install -y usbguard
@@ -59,19 +71,13 @@ if command -v usbguard >/dev/null 2>&1; then
sudo systemctl enable --now usbguard sudo systemctl enable --now usbguard
fi fi
# [NETW-3032] Ensure arpwatch is installed and enabled # [NETW-3032] Checking for ARP monitoring software
log "Installing arpwatch..." log "Installing Arpon..."
sudo apt-get install -y arpwatch sudo apt-get install -y arpon
iface=$(ip -o link show | awk -F': ' '{print $2}' | sed 's/@.*$//' | grep -Ev '^(lo|virbr|vmbr)' | head -n1 || true)
if [ -n "$iface" ]; then
sudo systemctl enable --now "arpwatch@${iface}.service" || true
else
log_warn "No suitable network interface found for arpwatch; service not enabled."
fi
# [NETW-3200] Disable unused network protocols # [NETW-3200] Disable unused network protocols
log "Disabling unused network protocol modules..." log "Disabling unused network protocol modules..."
sudo tee /etc/modprobe.d/network-protocols-disable.conf > /dev/null <<EOL sudo tee /etc/modprobe.d/90-network-protocols-disable.conf > /dev/null <<EOL
# Disable unnecessary network protocols to reduce attack surface # Disable unnecessary network protocols to reduce attack surface
blacklist dccp blacklist dccp
install dccp /bin/false install dccp /bin/false
@@ -86,49 +92,96 @@ blacklist tipc
install tipc /bin/false install tipc /bin/false
EOL EOL
# [MALW-3276] Ensure rkhunter is installed # [MALW-3276] Install rkhunter and update properties
log "Installing rkhunter..." # log "Installing and configuring rkhunter (MALW-3276)..."
sudo apt-get install -y rkhunter # sudo apt-get install -y rkhunter || true
sudo rkhunter --propupd || true # if command -v rkhunter >/dev/null 2>&1; then
# Add common whitelists # sudo rkhunter --update || true
sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/egrep" /etc/rkhunter.conf || true # sudo rkhunter --propupd || true
sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/fgrep" /etc/rkhunter.conf || true # # Add common whitelists (reduce false positives)
sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/ldd" /etc/rkhunter.conf || true # sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/egrep" /etc/rkhunter.conf || true
sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/rkhunter" /etc/rkhunter.conf || true # sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/fgrep" /etc/rkhunter.conf || true
# sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/ldd" /etc/rkhunter.conf || true
# sudo sed -i "$ a SCRIPTWHITELIST=/usr/bin/rkhunter" /etc/rkhunter.conf || true
# fi
# [MALW-3282] Ensure ClamAV is installed # [MALW-3282] Check for clamscan
log "Installing ClamAV..." # log "Checking for ClamAV installation..."
sudo apt-get install -y clamav clamav-freshclam # if ! command -v clamscan >/dev/null 2>&1; then
sudo freshclam || true # log "Installing ClamAV..."
sudo systemctl enable --now clamav-freshclam || true # sudo apt-get install -y clamdscan
sleep 2 # sudo freshclam || true
sudo systemctl enable --now clamav-daemon || true # else
# log "Clamscan is already installed."
# fi
# [MALW-3284] Check for clamd
# log "Checking for clamd..."
# if ! command -v clamd >/dev/null 2>&1; then
# log "Installing clamd..."
# sudo apt-get install -y clamav clamav-daemon
# sudo systemctl enable --now clamav-daemon || true
# else
# log "Clamd is already installed."
# fi
# [FINT-4350] Install a file integrity tool # [FINT-4350] Install a file integrity tool
log "Installing AIDE..." log "Installing AIDE..."
sudo apt-get install -y aide sudo apt-get install -y aide
sudo aide --init || true
if [ -f /var/lib/aide/aide.db.new.gz ]; then if [ -f /var/lib/aide/aide.db.new.gz ]; then
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz || true sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz || true
fi fi
sudo systemctl enable --now aidecheck.timer || true sudo systemctl enable --now aidecheck.timer || true
# [FIRE-45XX] Firewall configuration: ensure nftables is installed and iptables removed if present # [FIRE-45XX] Firewall configuration: ensure nftables is installed and iptables removed if present
log "Installing nftables and removing iptables (if installed)..." log "Disabling iptables to prevent conflicts with nftables (default on debian since Buster)..."
if dpkg -s iptables >/dev/null 2>&1; then sudo tee /etc/modprobe.d/90-ip_tables-disable.conf > /dev/null <<EOL
sudo apt-get remove -y iptables || true
fi
sudo apt-get install -y nftables
sudo tee /etc/modprobe.d/ip_tables-disable.conf > /dev/null <<EOL
# Disable ip_tables to prevent conflicts with nftables # Disable ip_tables to prevent conflicts with nftables
blacklist ip_tables blacklist ip_tables
install ip_tables /bin/false install ip_tables /bin/false
EOL EOL
# [TOOL-5190] Ensure IDS/IPS tools are installed (snort) # [BOOT-5122] Check for GRUB boot password
log "Installing snort..." log "Setting GRUB boot password..."
sudo apt-get install -y snort GRUB_PW_FILE="/etc/grub.d/40_custom"
sudo systemctl enable --now snort || true if ! grep -q "set superusers=" "$GRUB_PW_FILE" 2>/dev/null; then
read -s -p "Enter GRUB superuser name: " GRUB_USER
echo
read -s -p "Enter GRUB superuser password: " GRUB_PASS
echo
GRUB_PASS_HASH=$(grub-mkpasswd-pbkdf2 <<< "$GRUB_PASS" | awk -F' ' '/PBKDF2 hash of your password is/ {print $7}')
sudo tee -a "$GRUB_PW_FILE" > /dev/null <<EOL
set superusers="$GRUB_USER"
password_pbkdf2 $GRUB_USER $GRUB_PASS_HASH
EOL
sudo update-grub || true
else
log "GRUB superuser already configured; skipping."
fi
# Fix ^GRUB_CMDLINE_LINUX=.*audit=1 is not present in /etc/default/grub
if ! grep -q 'audit=1' /etc/default/grub 2>/dev/null; then
log "Enabling audit=1 in GRUB_CMDLINE_LINUX..."
sudo sed -i 's|^GRUB_CMDLINE_LINUX="\(.*\)"|GRUB_CMDLINE_LINUX="\1 audit=1"|' /etc/default/grub
sudo update-grub || true
else
log "audit=1 already present in GRUB_CMDLINE_LINUX; skipping."
fi
# ^GRUB_CMDLINE_LINUX=.*audit_backlog_limit=8192 is not present in /etc/default/grub
if ! grep -q 'audit_backlog_limit=8192' /etc/default/grub 2>/dev/null; then
log "Enabling audit_backlog_limit=8192 in GRUB_CMDLINE_LINUX..."
sudo sed -i 's|^GRUB_CMDLINE_LINUX="\(.*\)"|GRUB_CMDLINE_LINUX="\1 audit_backlog_limit=8192"|' /etc/default/grub
sudo update-grub || true
else
log "audit_backlog_limit=8192 already present in GRUB_CMDLINE_LINUX; skipping."
fi
# [TOOL-5190] Ensure IDS/IPS tools are installed (suricata)
# log "Installing suricata..."
# sudo apt-get install -y suricata
# sudo systemctl enable --now suricata || true
# [KRNL-5820] Disable core dumps # [KRNL-5820] Disable core dumps
log "Disabling core dumps via systemd and limits..." log "Disabling core dumps via systemd and limits..."
@@ -177,14 +230,22 @@ net.core.bpf_jit_harden = 2
net.ipv4.conf.all.forwarding = 0 net.ipv4.conf.all.forwarding = 0
net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.log_martians = 1 net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.log_martians = 1 net.ipv4.conf.default.log_martians = 1
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv6.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0 net.ipv6.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
EOL EOL
sudo sysctl --system sudo sysctl --system
@@ -195,24 +256,34 @@ if ! grep -q 'TMOUT' /etc/profile 2>/dev/null; then
echo 'export TMOUT' | sudo tee -a /etc/profile > /dev/null echo 'export TMOUT' | sudo tee -a /etc/profile > /dev/null
fi fi
# [MACF-6290] Enable AppArmor # [MACF-6290] Enable MAC framework (AppArmor)
log "Installing and enabling AppArmor..." log "Enabling AppArmor..."
sudo apt-get install -y apparmor apparmor-utils sudo apt-get install -y apparmor apparmor-utils || true
sudo systemctl enable --now apparmor || true sudo systemctl enable --now apparmor || true
# [FILE-6344] Restricting process details to users via /proc mount options # [FILE-6344] Restricting process details to users via /proc mount options
log "Configuring /proc to hide process info..." log "Configuring /proc to hide process info..."
if ! grep -q '^proc\s\+/proc\s\+proc\s\+' /etc/fstab; then if ! grep -q '^proc\s\+/proc\s\+proc\s\+' /etc/fstab; then
echo '# /proc' | sudo tee -a /etc/fstab > /dev/null echo '# /proc' | sudo tee -a /etc/fstab > /dev/null
echo 'proc /proc proc defaults,hidepid=2,gid=wheel 0 0' | sudo tee -a /etc/fstab > /dev/null echo 'proc /proc proc defaults,hidepid=2,gid=sudo 0 0' | sudo tee -a /etc/fstab > /dev/null
else else
sudo sed -i 's|^proc[[:space:]]\+/proc[[:space:]]\+proc[[:space:]]\+.*$|proc /proc proc defaults,hidepid=1,gid=wheel 0 0|' /etc/fstab sudo sed -i 's|^proc[[:space:]]\+/proc[[:space:]]\+proc[[:space:]]\+.*$|proc /proc proc defaults,hidepid=2,gid=sudo 0 0|' /etc/fstab
fi fi
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo mount -o remount /proc sudo mount -o remount /proc
# [FILE-6374] Check /dev/shm and /tmp mount options # [FILE-6374] Check /dev, /dev/shm and /tmp mount options
log "Ensuring /dev/shm and /tmp have secure mount options..." log "Ensuring /dev, /dev/shm and /tmp have secure mount options..."
if ! grep -q '^devtmpfs\s\+/dev\s\+devtmpfs\s\+' /etc/fstab; then
echo '# /dev' | sudo tee -a /etc/fstab > /dev/null
echo 'devtmpfs /dev devtmpfs rw,nosuid,noexec,relatime,size=10%,mode=755 0 0' | sudo tee -a /etc/fstab > /dev/null
else
sudo sed -i 's|^devtmpfs[[:space:]]\+/dev[[:space:]]\+devtmpfs[[:space:]]\+.*$|devtmpfs /dev devtmpfs rw,nosuid,noexec,relatime,size=10%,mode=755 0 0|' /etc/fstab
fi
sudo systemctl daemon-reload
sudo mount -o remount /dev
if ! grep -q '^tmpfs\s\+/dev/shm\s\+tmpfs\s\+' /etc/fstab; then if ! grep -q '^tmpfs\s\+/dev/shm\s\+tmpfs\s\+' /etc/fstab; then
echo '# /dev/shm' | sudo tee -a /etc/fstab > /dev/null echo '# /dev/shm' | sudo tee -a /etc/fstab > /dev/null
echo 'tmpfs /dev/shm tmpfs rw,nosuid,nodev,noexec 0 0' | sudo tee -a /etc/fstab > /dev/null echo 'tmpfs /dev/shm tmpfs rw,nosuid,nodev,noexec 0 0' | sudo tee -a /etc/fstab > /dev/null
@@ -233,11 +304,14 @@ sudo mount -o remount /tmp
# [FILE-6430] Disable mounting of some filesystems # [FILE-6430] Disable mounting of some filesystems
log "Disabling unnecessary filesystem modules..." log "Disabling unnecessary filesystem modules..."
sudo tee /etc/modprobe.d/fs_blacklist.conf > /dev/null <<EOL sudo tee /etc/modprobe.d/90-fs_blacklist.conf > /dev/null <<EOL
# Blacklist unnecessary filesystem modules to reduce attack surface # Blacklist unnecessary filesystem modules to reduce attack surface
blacklist cramfs blacklist cramfs
install cramfs /bin/false install cramfs /bin/false
blacklist freevxfs
install freevxfs /bin/false
blacklist hfs blacklist hfs
install hfs /bin/false install hfs /bin/false
@@ -256,6 +330,7 @@ EOL
# [BANN-7126] Add legal banner to /etc/issue # [BANN-7126] Add legal banner to /etc/issue
log "Adding legal banner to /etc/issue..." log "Adding legal banner to /etc/issue..."
sudo systemctl disable --now pvebanner || true
sudo tee /etc/issue > /dev/null <<EOL sudo tee /etc/issue > /dev/null <<EOL
******************************************************************** ********************************************************************
* WARNING - UNAUTHORIZED ACCESS * * WARNING - UNAUTHORIZED ACCESS *
@@ -272,41 +347,102 @@ sudo tee /etc/issue > /dev/null <<EOL
\n - \l \n - \l
EOL EOL
# [BANN-7130] Check issue.net banner file contents
log "Checking /etc/issue.net banner file contents..."
if ! sudo grep -q "WARNING - UNAUTHORIZED ACCESS" /etc/issue.net; then
log "Adding legal banner to /etc/issue.net..."
sudo tee /etc/issue.net > /dev/null <<EOL
********************************************************************
* WARNING - UNAUTHORIZED ACCESS *
* *
* Unauthorized access to this computer system is strictly *
* prohibited. Individuals accessing, using, or modifying this *
* system without explicit authorization will be subject to legal *
* action and prosecuted to the fullest extent of the law. *
* *
* Authorized users should have no expectation of privacy. All *
* activity on this system is monitored, recorded, and may be used *
* as evidence in criminal or civil proceedings. *
********************************************************************
\n - \l
EOL
fi
# [HRDN-7220] Check if one or more compilers are installed
# Disallow apt to extract /usr/bin/as by making a dpkg config
log "Checking if as is present and excluding it from installation..."
sudo tee /etc/dpkg/dpkg.cfg.d/01-exclude-as > /dev/null <<'EOL'
# Exclude as from being installed
path-exclude /usr/bin/as
path-exclude /usr/bin/x86_64-linux-gnu-as
EOL
# [HRDN-7222] Restricting compiler access to root user only # [HRDN-7222] Restricting compiler access to root user only
# Correcting from chown->chmod to restrict access # Correcting from chown->chmod to restrict access
log "Restricting compiler binaries..." log "Restricting compiler binaries..."
for bin in /usr/bin/as /usr/bin/gcc /usr/bin/g++ /usr/bin/cc /usr/bin/c++ /usr/bin/ld /usr/bin/lld /usr/bin/clang; do for bin in /usr/bin/as /usr/bin/x86_64-linux-gnu-as; do
if [ -f "$bin" ]; then if [ -f "$bin" ]; then
sudo chmod 700 "$bin" || true sudo chmod 700 "$bin" || true
fi fi
done done
# [PKGS-7320] Install package auditing tools # [PKGS-7320] Install package auditing tools
log "Installing arch-audit equivalent packages..." log "Installing package auditing..."
# Debian doesn't have arch-audit; install debsecan for security audits
sudo apt-get install -y debsecan || true sudo apt-get install -y debsecan || true
# [PKGS-7370] Checking for debsums utility
if ! dpkg -l | grep -q debsums; then
sudo apt-get install -y debsums || true
fi
# [SSH-7408] Check SSH specific defined options
log "Checking SSH specific defined options..."
set_sshd_option() {
local opt="$1"
local val="$2"
# If the option exists (possibly commented), replace the whole line; otherwise append
if sudo grep -Eq "^[[:space:]]*#?[[:space:]]*${opt}[[:space:]]+" /etc/ssh/sshd_config 2>/dev/null; then
sudo sed -ri "s|^[[:space:]]*#?[[:space:]]*${opt}[[:space:]]+.*|${opt} ${val}|" /etc/ssh/sshd_config
else
echo "${opt} ${val}" | sudo tee -a /etc/ssh/sshd_config > /dev/null
fi
}
set_sshd_option "PermitRootLogin" "no" # Need to setup SSH key and local admin
set_sshd_option "PasswordAuthentication" "no" # Need to setup SSH key and local admin
set_sshd_option "ChallengeResponseAuthentication" "no"
set_sshd_option "AllowTcpForwarding" "no"
set_sshd_option "AllowAgentForwarding" "no"
set_sshd_option "X11Forwarding" "no"
set_sshd_option "TCPKeepAlive" "no"
set_sshd_option "ClientAliveCountMax" "2"
set_sshd_option "MaxAuthTries" "3"
set_sshd_option "MaxSessions" "2"
set_sshd_option "LogLevel" "VERBOSE"
sudo systemctl restart ssh || true
# [PKGS-7420] Detect toolkit to automatically download and apply upgrades
log "Installing unattended-upgrades..."
sudo apt-get install -y unattended-upgrades || true
sudo dpkg-reconfigure -f noninteractive unattended-upgrades || true
sudo tee /etc/apt/apt.conf.d/20auto-upgrades > /dev/null <<'EOL'
APT::Periodic::Unattended-Upgrade "1";
EOL
sudo systemctl enable --now unattended-upgrades || true
# [FILE-7524] Ensuring file permissions # [FILE-7524] Ensuring file permissions
log "Enforcing file permissions for SSH & cron..." log "Enforcing file permissions for SSH & cron..."
sudo chmod -R 640 /var/log/ || true
sudo chmod 600 /etc/crontab || true
sudo chmod 700 /etc/cron.* || true
sudo chmod -R 700 /etc/cron.d/ || true
sudo chmod 600 /etc/ssh/sshd_config || true sudo chmod 600 /etc/ssh/sshd_config || true
sudo chmod 700 /etc/cron.hourly || true
# [CRYP-8004] Presence of hardware RNG and software PRNG # [CRYP-8004] Presence of hardware RNG tools
log "Installing rng-tools and haveged..." log "Installing rng-tools..."
sudo apt-get install -y rng-tools haveged || true sudo apt-get install -y rng-tools-debian || true
sudo systemctl enable --now rng-tools haveged || true sudo systemctl enable --now rng-tools-debian || true
# [CRYP-8006] Ensure MemoryOverwriteRequest-bit set (UEFI) using system-shutdown script
sudo tee /usr/lib/systemd/system-shutdown/mor-bit-wipe.sh > /dev/null <<'EOL'
#!/bin/bash
MOR_VAR_PATH="/sys/firmware/efi/efivars/MemoryOverwriteRequestControl-e20939be-32d4-41be-a150-897f85d49829"
if [ -e "$MOR_VAR_PATH" ]; then
printf "\x07\x00\x00\x00\x01" | dd of="$MOR_VAR_PATH" bs=5 count=1 conv=notrunc >/dev/null 2>&1 || true
echo "$(date) - Successfully set MOR-bit for next boot memory wipe." >> /var/log/mor-wipe.log || true
fi
exit 0
EOL
sudo chmod +x /usr/lib/systemd/system-shutdown/mor-bit-wipe.sh || true
# [AUTH-*] Password and PAM related settings # [AUTH-*] Password and PAM related settings
log "Configuring password hashing and pam pwquality..." log "Configuring password hashing and pam pwquality..."
@@ -315,37 +451,50 @@ sudo sed -i 's/^#SHA_CRYPT_MIN_ROUNDS .*/SHA_CRYPT_MIN_ROUNDS 5000/' /etc/login.
sudo sed -i 's/^#SHA_CRYPT_MAX_ROUNDS .*/SHA_CRYPT_MAX_ROUNDS 5000000/' /etc/login.defs || true sudo sed -i 's/^#SHA_CRYPT_MAX_ROUNDS .*/SHA_CRYPT_MAX_ROUNDS 5000000/' /etc/login.defs || true
sudo apt-get install -y libpam-pwquality || true sudo apt-get install -y libpam-pwquality || true
sudo tee /etc/security/pwquality.conf > /dev/null <<EOL set_pwq() {
# PAM pwquality configuration file local key="$1"; local val="$2"
retry = 3 if sudo grep -Eq "^[[:space:]]*${key}[[:space:]]*=" /etc/security/pwquality.conf 2>/dev/null; then
difok = 6 sudo sed -ri "s|^[[:space:]]*${key}[[:space:]]*=.*|${key} = ${val}|" /etc/security/pwquality.conf
minlen = 14 else
dcredit = -1 sudo sh -c "printf '%s = %s\n' '${key}' '${val}' >> /etc/security/pwquality.conf"
ucredit = -1 fi
ocredit = -1 }
lcredit = -1
EOL set_pwq "retry" 3
set_pwq "difok" 6
set_pwq "minlen" 14
set_pwq "dcredit" -1
set_pwq "ucredit" -1
set_pwq "ocredit" -1
set_pwq "lcredit" -1
set_pwq "minclass" 4
set_pwq "usercheck" 1
set_pwq "enforcing" 1
# Add pam_pwquality to /etc/pam.d/common-password if not present # Add pam_pwquality to /etc/pam.d/common-password if not present
if ! grep -q "pam_pwquality.so" /etc/pam.d/common-password 2>/dev/null; then if ! grep -q "pam_pwquality.so" /etc/pam.d/common-password 2>/dev/null; then
sudo sed -i "/pam_unix.so/ i password requisite pam_pwquality.so retry=3" /etc/pam.d/common-password || true sudo sed -i "/pam_unix.so/ i password requisite pam_pwquality.so retry=3" /etc/pam.d/common-password || true
fi fi
# [AUTH-9286] Password change days # [AUTH-9286] Password aging
sudo sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 7/' /etc/login.defs || true sudo sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 7/' /etc/login.defs || true
sudo sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs || true sudo sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs || true
# [AUTH-9328] Default umask # [AUTH-9328] Default umask
sudo sed -i 's/^UMASK.*/UMASK 027/' /etc/login.defs || true if sudo grep -Eq '^[[:space:]]*#?[[:space:]]*UMASK\b' /etc/login.defs 2>/dev/null; then
if grep -qE '^[[:space:]]*#?[[:space:]]*umask' /etc/bash.bashrc 2>/dev/null; then sudo sed -ri "s|^[[:space:]]*#?[[:space:]]*UMASK[[:space:]]+.*|UMASK 077|" /etc/login.defs || true
sudo sed -i 's/^[[:space:]]*#\?[[:space:]]*umask.*/umask 027/' /etc/bash.bashrc || true
else else
echo 'umask 027' | sudo tee -a /etc/bash.bashrc > /dev/null echo 'UMASK 077' | sudo tee -a /etc/login.defs > /dev/null
fi
if grep -qE '^[[:space:]]*#?[[:space:]]*umask' /etc/bash.bashrc 2>/dev/null; then
sudo sed -i 's/^[[:space:]]*#\?[[:space:]]*umask.*/umask 077/' /etc/bash.bashrc || true
else
echo 'umask 077' | sudo tee -a /etc/bash.bashrc > /dev/null
fi fi
if grep -qE '^[[:space:]]*#?[[:space:]]*umask' /etc/profile 2>/dev/null; then if grep -qE '^[[:space:]]*#?[[:space:]]*umask' /etc/profile 2>/dev/null; then
sudo sed -i 's/^[[:space:]]*#\?[[:space:]]*umask.*/umask 027/' /etc/profile || true sudo sed -i 's/^[[:space:]]*#\?[[:space:]]*umask.*/umask 077/' /etc/profile || true
else else
echo 'umask 027' | sudo tee -a /etc/profile > /dev/null echo 'umask 077' | sudo tee -a /etc/profile > /dev/null
fi fi
# [AUTH-9408] Logging of failed login attempts is enabled # [AUTH-9408] Logging of failed login attempts is enabled
@@ -367,21 +516,130 @@ sudo systemctl enable --now sysstat || true
sudo apt-get install -y auditd || true sudo apt-get install -y auditd || true
sudo systemctl enable --now auditd || true sudo systemctl enable --now auditd || true
# Fix ^space_left_action[[:space:]]*=[[:space:]]*email is not present in /etc/audit/auditd.conf
if ! grep -q '^space_left_action[[:space:]]*=[[:space:]]*email' /etc/audit/auditd.conf 2>/dev/null; then
log "Configuring auditd to email on low disk space..."
sudo sed -i 's/^space_left_action[[:space:]]*=.*/space_left_action = email/' /etc/audit/auditd.conf || true
# Set admin email for auditd notifications
ADMIN_EMAIL="root@localhost" # Change as needed
if grep -q '^admin_space_left_email[[:space:]]*=' /etc/audit/auditd.conf 2>/dev/null; then
sudo sed -i "s|^admin_space_left_email[[:space:]]*=.*|admin_space_left_email = ${ADMIN_EMAIL}|" /etc/audit/auditd.conf || true
else
echo "admin_space_left_email = ${ADMIN_EMAIL}" | sudo tee -a /etc/audit/auditd.conf > /dev/null
fi
fi
# Fix ^admin_space_left_action[[:space:]]*=[[:space:]]*halt is not present in /etc/audit/auditd.conf
if ! grep -q '^admin_space_left_action[[:space:]]*=[[:space:]]*halt' /etc/audit/auditd.conf 2>/dev/null; then
log "Configuring auditd to halt on critical disk space..."
sudo sed -i 's/^admin_space_left_action[[:space:]]*=.*/admin_space_left_action = halt/' /etc/audit/auditd.conf || true
fi
# Fix ^max_log_file_action[[:space:]]*=[[:space:]]*keep_logs is not present in /etc/audit/auditd.conf
if ! grep -q '^max_log_file_action[[:space:]]*=[[:space:]]*keep_logs' /etc/audit/auditd.conf 2>/dev/null; then
log "Configuring auditd to keep logs on max log file size..."
sudo sed -i 's/^max_log_file_action[[:space:]]*=.*/max_log_file_action = keep_logs/' /etc/audit/auditd.conf || true
fi
# [ACCT-9630] Configure auditd rules # [ACCT-9630] Configure auditd rules
sudo tee /etc/audit/rules.d/10-harden.rules > /dev/null <<EOL sudo tee /etc/audit/rules.d/10-harden.rules > /dev/null <<EOL
# Monitor attempts to change system time -a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
-w /etc/localtime -p wa -k time-change -a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change
-a always,exit -F arch=b64 -S clock_settime -k time-change
-a always,exit -F arch=b32 -S clock_settime -k time-change
# Monitor attempts to change user/group info -w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k user-info -w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k user-info -w /etc/gshadow -p wa -k identity
-w /etc/group -p wa -k user-info -w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k user-info -w /etc/security/opasswd -p wa -k identity
-a exit,always -F arch=b64 -S sethostname -S setdomainname -k system-locale
-a exit,always -F arch=b32 -S sethostname -S setdomainname -k system-locale
-w /etc/issue -p wa -k system-locale
-w /etc/issue.net -p wa -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/network -p wa -k system-locale
-w /etc/apparmor/ -p wa -k MAC-policy
-w /etc/apparmor.d/ -p wa -k MAC-policy
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
-w /var/log/tallylog -p wa -k logins
-w /var/run/utmp -p wa -k session
-w /var/log/wtmp -p wa -k session
-w /var/log/btmp -p wa -k session
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access
-a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access
-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access
-a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access
-a always,exit -F path=/usr/bin/chage -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/chfn -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/chsh -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/expiry -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/ssh-agent -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/fusermount3 -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/dotlockfile -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/lockfile -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/procmail -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/lib/openssh/ssh-keysign -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/lib/dbus-1.0/dbus-daemon-launch-helper -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/libexec/proxmox-mail-forward -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/libexec/pam-tmpdir/pam-tmpdir-helper -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/sbin/mount.cifs -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/sbin/mount.nfs -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/sbin/postdrop -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/sbin/postqueue -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-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
-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete
-a always,exit -F arch=b32 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
-w /var/log/auth.log -p wa -k sudoaction
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules
# Make the configuration immutable (must be the last line) # Make the configuration immutable (must be the last line)
-e 2 -e 2
EOL EOL
# Fix ^Storage=persistent is not present in /etc/systemd/journald.conf
if ! grep -q '^Storage=persistent' /etc/systemd/journald.conf 2>/dev/null; then
log "Configuring journald to use persistent storage..."
sudo sed -i 's/^#Storage=.*/Storage=persistent/' /etc/systemd/journald.conf || true
sudo systemctl restart systemd-journald || true
fi
log "Debian hardening completed. Review the log above for applied steps and check for any package/service variations on your system." log "Debian hardening completed. Review the log above for applied steps and check for any package/service variations on your system."
exit 0 exit 0