Linux Server Hardening
Harden Linux servers according to CIS Benchmark recommendations.
Introduction
This project documents the hardening of a Debian Linux server from a default installation to a security-baseline configuration suitable for internet-facing or internal production use. The work involved reviewing the server's default configuration, identifying weak or unnecessary settings across authentication, network exposure, and logging, applying targeted hardening changes, and verifying that legitimate services continued to function after each change. The end state is a server with a reduced attack surface, enforced least-privilege access, an active host-based firewall, and centralized monitoring through Wazuh.
What is Linux Hardening?
Linux Server Hardening is the process of reducing the attack surface of a Linux server by applying secure configurations, restricting access, removing unnecessary services, strengthening authentication, protecting network services, and improving system monitoring. This project focuses on applying practical hardening techniques to a Linux server based on widely recognized security practices, particularly the CIS Benchmarks. CIS describes its Benchmarks as consensus-based secure configuration recommendations developed by cybersecurity professionals, covering everything from filesystem and boot settings to authentication, network parameters, logging, and auditing. Rather than applying every control in the full benchmark, this project prioritizes the changes with the highest security impact for a general-purpose server: remote access, privilege management, network exposure, patching, and visibility into authentication activity.
Case work
The main objectives of this project were to take a default Debian server build and bring it in line with CIS Benchmark-aligned best practices: locking down SSH, enforcing least-privilege account management, restricting network exposure with a host-based firewall, keeping the system patched, tightening file and directory permissions, and standing up logging and auditing that feeds into Wazuh for ongoing security monitoring.
1. SSH Hardening
The default OpenSSH configuration allowed root login and password authentication, both common brute-force targets. Ed25519 keypairs were generated and deployed for authorized administrators, /etc/ssh/sshd_config was locked down, and Fail2ban was installed with an sshd jail to auto-block IPs after repeated failed logins.
# Generate an Ed25519 keypair for the admin account
ssh-keygen -t ed25519 -C "admin@server"
# Edit the daemon config
sudo nano /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
X11Forwarding no
AllowUsers admin
# Validate syntax before restarting (avoids a lockout)
sudo sshd -t
sudo systemctl restart sshd
# Install and enable Fail2ban for SSH
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban2. Least Privilege & Account Management
Default and unused accounts were audited, service accounts without an interactive need were locked down, and administrative access was moved from shared root usage to a named account scoped through sudo. Password quality and lockout policy were enforced via PAM.
# List accounts with a login shell
awk -F: '$7 ~ /sh$/ {print $1}' /etc/passwd
# Disable shell access for a non-interactive service account
sudo usermod -s /usr/sbin/nologin svc-backup
# Create a scoped sudoers rule instead of full root
sudo visudo -f /etc/sudoers.d/admin
admin ALL=(ALL) /usr/bin/apt, /usr/bin/systemctl
# Enforce password quality
sudo apt install libpam-pwquality -y
# /etc/security/pwquality.conf
minlen = 14
dcredit = -1
ucredit = -1
# Lock an account after 5 failed attempts
sudo nano /etc/pam.d/common-auth
auth required pam_faillock.so deny=5 unlock_time=9003. Host-Based Firewall
UFW (backed by nftables) was enabled with a default-deny inbound policy. Only the ports required for the server's role were explicitly opened, including a rate-limited SSH rule. All other listening sockets were reviewed and unused services disabled.
# Set default-deny policy
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow only required services
sudo ufw limit OpenSSH
sudo ufw allow 443/tcp
# Enable and check status
sudo ufw enable
sudo ufw status verbose
# Review what's actually listening
sudo ss -tulpn4. Patch & Package Management
The system was brought fully up to date and configured to apply security patches automatically going forward. Unused packages were removed to reduce the toolset available to an attacker after any future compromise.
# Update and upgrade
sudo apt update && sudo apt full-upgrade -y
# Enable automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Remove unused packages
sudo apt autoremove --purge -y5. File & Directory Permissions
Sensitive files were reviewed and corrected, world-writable directories were located, and mount options were tightened on non-essential partitions. SUID/SGID binaries were enumerated and checked against a known-good baseline.
# Lock down sensitive files
sudo chmod 600 /etc/shadow
sudo chmod 600 /etc/ssh/ssh_host_*_key
# Find world-writable directories
sudo find / -xdev -type d -perm -0002
# Harden /tmp mount options (in /etc/fstab)
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0
# Enumerate SUID/SGID binaries
sudo find / -xdev \( -perm -4000 -o -perm -2000 \) -type f6. Logging, Auditing & Wazuh Monitoring
The Wazuh agent was installed and registered against a central Wazuh manager to provide file integrity monitoring, log collection, and authentication anomaly alerting, correlated with local journal/rsyslog output.
# Install the Wazuh agent and point it at the manager
curl -so wazuh-agent.deb https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_amd64.deb
sudo WAZUH_MANAGER='manager_ip' dpkg -i ./wazuh-agent.deb
sudo systemctl daemon-reload
sudo systemctl enable --now wazuh-agent
# Confirm the agent is connected
sudo /var/ossec/bin/agent-auth -m manager_ip
sudo systemctl status wazuh-agent
# Tail auth events being forwarded
sudo tail -f /var/log/auth.logWhat was documented
For each control above, the before-and-after configuration was recorded, along with the specific command used to apply the change and the command used to verify it (for example, sshd -T | grep permitrootlogin, ufw status verbose, and confirmation of active alerts in the Wazuh dashboard). This before/after record is what makes the hardening auditable rather than a one-time, undocumented change.