Linux Security Hardening: A Complete Checklist with Commands

A default Linux installation is not secure. It’s designed to work, not to be hardened. Attackers know exactly what a default Ubuntu, CentOS, or Debian server looks like — and they exploit the predictable configuration every day. This guide gives you every command you need to harden a Linux server from scratch.

1. Update Everything First

# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y

# RHEL/CentOS/Rocky Linux
sudo dnf update -y && sudo dnf autoremove -y

# Enable automatic security updates
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

# RHEL equivalent
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer

2. Create a Non-Root Admin User

# Create new admin user
sudo useradd -m -s /bin/bash -G sudo secadmin

# Set strong password
sudo passwd secadmin

# Or better: use SSH keys and disable password auth
sudo mkdir -p /home/secadmin/.ssh
sudo chmod 700 /home/secadmin/.ssh
sudo nano /home/secadmin/.ssh/authorized_keys
# Paste your public key here
sudo chmod 600 /home/secadmin/.ssh/authorized_keys
sudo chown -R secadmin:secadmin /home/secadmin/.ssh

3. Harden SSH Configuration

SSH is the #1 attack surface on internet-facing Linux servers. These settings dramatically reduce exposure:

# Edit SSH config
sudo nano /etc/ssh/sshd_config

# Paste these settings (adjust port to something non-standard)
Port 2222
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
UseDNS no
Banner /etc/ssh/banner.txt

# Allow only specific users
AllowUsers secadmin

# Restart SSH
sudo systemctl restart sshd

# Test config before logging out!
ssh -p 2222 secadmin@server-ip

4. Configure UFW Firewall

# Install and configure UFW (Uncomplicated Firewall)
sudo apt install ufw

# Default deny everything
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow only what you need
sudo ufw allow 2222/tcp    # Your SSH port
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS

# Rate limit SSH to prevent brute force
sudo ufw limit 2222/tcp

# Enable firewall
sudo ufw enable
sudo ufw status verbose

# For RHEL with firewalld:
sudo systemctl start firewalld
sudo firewall-cmd --set-default-zone=drop
sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --add-port=443/tcp --permanent
sudo firewall-cmd --reload

5. Install and Configure Fail2ban

# Install fail2ban
sudo apt install fail2ban

# Create local config (never edit the main config)
sudo nano /etc/fail2ban/jail.local

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
backend = systemd

[sshd]
enabled = true
port = 2222
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400

# Start fail2ban
sudo systemctl enable --now fail2ban

# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd

# View banned IPs
sudo fail2ban-client get sshd banned

6. Kernel Hardening with sysctl

# Edit /etc/sysctl.d/99-security.conf
sudo nano /etc/sysctl.d/99-security.conf

# Paste all of the following:

# Disable IP forwarding (unless this is a router)
net.ipv4.ip_forward = 0

# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Ignore ICMP broadcast requests (Smurf attack protection)
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Enable reverse path filtering (spoofed packets)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

# Restrict dmesg to root
kernel.dmesg_restrict = 1

# Disable kexec (prevents kernel replacement)
kernel.kexec_load_disabled = 1

# Apply immediately
sudo sysctl -p /etc/sysctl.d/99-security.conf

7. Configure AppArmor / SELinux

# Ubuntu: AppArmor (usually pre-installed)
sudo systemctl status apparmor
sudo aa-status

# Enable AppArmor profiles
sudo aa-enforce /etc/apparmor.d/*

# Install additional profiles
sudo apt install apparmor-profiles apparmor-utils

# RHEL: Check SELinux status
sestatus
getenforce

# Set to enforcing mode
sudo setenforce 1
sudo sed -i 's/SELINUX=permissive/SELINUX=enforcing/' /etc/selinux/config

# Check SELinux audit log for denials
sudo ausearch -m avc -ts recent | audit2why

8. File System Security

# Find world-writable files (attackers love these)
sudo find / -xdev -type f -perm -0002 -print

# Find SUID/SGID binaries (potential privilege escalation)
sudo find / -xdev -perm /4000 -type f -print 2>/dev/null
sudo find / -xdev -perm /2000 -type f -print 2>/dev/null

# Remove unnecessary SUID bits
sudo chmod u-s /usr/bin/at    # Example
sudo chmod u-s /usr/bin/chfn

# Secure /tmp mount (prevent execution of code from /tmp)
# Add to /etc/fstab:
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0

# Apply immediately
sudo mount -o remount /tmp

# Set proper permissions on sensitive files
sudo chmod 640 /etc/shadow
sudo chmod 644 /etc/passwd
sudo chmod 600 /etc/ssh/sshd_config
sudo chattr +i /etc/resolv.conf    # Make immutable

9. User Account Security

# Find all accounts with UID 0 (root-level)
awk -F: '($3 == 0) { print $1 }' /etc/passwd

# Lock unused accounts
sudo usermod -L daemon
sudo usermod -L bin
sudo usermod -L sync

# Set password aging policies
sudo nano /etc/login.defs
PASS_MAX_DAYS 90
PASS_MIN_DAYS 1
PASS_WARN_AGE 14

# Apply to existing users
sudo chage -M 90 -m 1 -W 14 username

# Check users with empty passwords
sudo awk -F: '($2 == "" ) { print $1 }' /etc/shadow

# Configure PAM password quality
sudo apt install libpam-pwquality
sudo nano /etc/pam.d/common-password
# Add: minlen=14 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1

10. Audit Logging with auditd

# Install auditd
sudo apt install auditd audispd-plugins

# Configure rules /etc/audit/rules.d/audit.rules
-D   # Delete all existing rules
-b 8192  # Buffer size

# Monitor authentication
-w /var/log/auth.log -p wa -k authentication

# Monitor /etc/passwd and /etc/shadow changes
-w /etc/passwd -p wa -k user_modification
-w /etc/shadow -p wa -k user_modification
-w /etc/sudoers -p wa -k sudo_changes

# Monitor privileged commands
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands

# Monitor network configuration changes
-w /etc/hosts -p wa -k network_config
-w /etc/network/ -p wa -k network_config

# Reload and start
sudo service auditd restart

# Search audit logs
sudo ausearch -k authentication | aureport
sudo aureport --summary -i
sudo ausearch -ua root -ts today | tail -50

11. Disable Unnecessary Services

# List all running services
sudo systemctl list-units --type=service --state=running

# Disable services you don't need (examples)
sudo systemctl disable --now avahi-daemon   # mDNS
sudo systemctl disable --now cups           # Printing
sudo systemctl disable --now bluetooth      # Bluetooth
sudo systemctl disable --now nfs-server     # NFS
sudo systemctl disable --now rpcbind        # RPC

# Check for listening services
ss -tulnp
netstat -tulnp

# Remove unneeded packages entirely
sudo apt purge telnet rsh-client rsh-server xinetd nis tftp

12. Install a Rootkit Scanner

# Install rkhunter and chkrootkit
sudo apt install rkhunter chkrootkit

# Run initial scan
sudo rkhunter --update
sudo rkhunter --check --sk

# View results
sudo cat /var/log/rkhunter.log | grep -E "WARNING|FOUND"

# Run chkrootkit
sudo chkrootkit

# Schedule weekly scans via cron
echo "0 2 * * 0 root /usr/bin/rkhunter --check --sk --report-warnings-only" | sudo tee /etc/cron.d/rkhunter

Security Hardening Score

Run Lynis to get an overall security score and prioritized recommendations:

# Run Lynis audit
sudo apt install lynis
sudo lynis audit system

# Check score
sudo cat /var/log/lynis.log | grep "Hardening index"

# View recommendations
sudo cat /var/log/lynis-report.dat | grep "^suggestion"

A freshly hardened server typically scores 75-85 on Lynis. A stock default Ubuntu server scores around 55-60. Keep running Lynis monthly to catch regressions. A properly hardened Linux server will block the overwhelming majority of automated attacks that constantly scan the internet for vulnerable hosts.