Skip to main content

Linux Server Hardening Checklist — 20 Steps to Secure Your Server

· 7 min read
Goel Academy
DevOps & Cloud Learning Hub

You just deployed a server on the internet — here are the 20 things you must do before going to sleep. Every minute an unhardened server is exposed, automated scanners are probing it. Shodan indexes new IPs within hours. This isn't theoretical — this is Tuesday.

Step 1: Update Everything

Before anything else, patch the system. Known vulnerabilities are the easiest attack vector.

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

# RHEL/AlmaLinux/Rocky
sudo dnf update -y && sudo dnf autoremove -y

# Check for kernel updates requiring reboot
needs-restarting -r 2>/dev/null || echo "Check if reboot needed"

Step 2: Create a Non-Root User with sudo

Never operate as root. Create a dedicated admin account.

# Create user with home directory and bash shell
useradd -m -s /bin/bash deployer
echo "deployer ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/deployer
chmod 440 /etc/sudoers.d/deployer

# Set up SSH key authentication for the new user
mkdir -p /home/deployer/.ssh
cp ~/.ssh/authorized_keys /home/deployer/.ssh/
chown -R deployer:deployer /home/deployer/.ssh
chmod 700 /home/deployer/.ssh
chmod 600 /home/deployer/.ssh/authorized_keys

Step 3: Harden SSH Configuration

SSH is the primary attack surface. Lock it down aggressively.

# Back up original config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Apply hardened settings
sudo tee /etc/ssh/sshd_config.d/hardened.conf << 'EOF'
# Authentication
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
MaxSessions 3

# Network
Port 22
AddressFamily inet
ListenAddress 0.0.0.0
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2

# Security
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
PermitUserEnvironment no

# Restrict to specific users
AllowUsers deployer

# Use strong crypto only
KexAlgorithms sway25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
EOF

# Validate config before restarting
sudo sshd -t && sudo systemctl restart sshd

Step 4: Configure a Firewall

Only allow what's explicitly needed. Deny everything else.

# Using ufw (Ubuntu/Debian)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw enable
sudo ufw status verbose

Step 5: Install and Configure fail2ban

Automated brute-force protection. Bans IPs after repeated failures.

sudo apt install fail2ban -y

# Create a local config (never edit jail.conf directly)
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
banaction = ufw

[sshd]
enabled = true
port = 22
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400
EOF

sudo systemctl enable fail2ban
sudo systemctl restart fail2ban

# Check status and banned IPs
sudo fail2ban-client status sshd

Step 6: Enable Automatic Security Updates

Unattended upgrades ensure critical patches are applied without human intervention.

# Debian/Ubuntu
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades

# Verify it's enabled
cat /etc/apt/apt.conf.d/20auto-upgrades
# Should show:
# APT::Periodic::Update-Package-Lists "1";
# APT::Periodic::Unattended-Upgrade "1";

Step 7: Remove Unnecessary Services

Every running service is an attack surface. Remove what you don't need.

# List all listening services
ss -tulnp

# List enabled services
systemctl list-unit-files --state=enabled --type=service

# Disable and stop services you don't need
sudo systemctl disable --now cups snapd avahi-daemon bluetooth
sudo apt purge telnet rsh-client rsh-redone-client 2>/dev/null

Step 8: Configure Audit Logging

The Linux audit system records security-relevant events. Essential for incident response.

sudo apt install auditd -y

# Add audit rules for critical files
sudo tee /etc/audit/rules.d/hardening.rules << 'EOF'
# Monitor password and group changes
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers

# Monitor SSH configuration
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /etc/ssh/sshd_config.d/ -p wa -k sshd_config

# Monitor cron
-w /etc/crontab -p wa -k cron
-w /var/spool/cron/ -p wa -k cron

# Log all commands run as root
-a always,exit -F arch=b64 -F euid=0 -S execve -k root_commands
EOF

sudo systemctl restart auditd

# Search audit logs
sudo ausearch -k identity --start today

Step 9: Kernel Hardening via sysctl

Harden the network stack and memory behavior at the kernel level.

sudo tee /etc/sysctl.d/99-security.conf << 'EOF'
# 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 reverse path filtering (anti-spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Enable SYN cookies (SYN flood protection)
net.ipv4.tcp_syncookies = 1

# Disable core dumps for setuid programs
fs.suid_dumpable = 0

# Randomize address space layout (ASLR)
kernel.randomize_va_space = 2

# Restrict access to kernel pointers
kernel.kptr_restrict = 2

# Restrict dmesg access to root
kernel.dmesg_restrict = 1
EOF

sudo sysctl --system

Step 10: Set File Permissions on Critical Files

# Restrict access to sensitive files
sudo chmod 600 /etc/shadow /etc/gshadow
sudo chmod 644 /etc/passwd /etc/group
sudo chmod 700 /root
sudo chmod 600 /boot/grub/grub.cfg

# Find world-writable files (potential security risk)
find / -xdev -type f -perm -0002 -exec ls -l {} \; 2>/dev/null

# Find files with no owner
find / -xdev -nouser -o -nogroup 2>/dev/null

Steps 11-15: Quick Wins

# Step 11: Disable USB storage (if not needed)
echo "blacklist usb-storage" | sudo tee /etc/modprobe.d/blacklist-usb.conf

# Step 12: Set login banner
sudo tee /etc/issue.net << 'EOF'
Authorized access only. All activity is monitored and logged.
EOF

# Step 13: Configure password policy
sudo apt install libpam-pwquality -y
# In /etc/security/pwquality.conf: minlen=14, dcredit=-1, ucredit=-1

# Step 14: Set account lockout
# In /etc/pam.d/common-auth, add:
# auth required pam_tally2.so deny=5 onerr=fail unlock_time=900

# Step 15: Restrict cron to authorized users only
echo "deployer" | sudo tee /etc/cron.allow
sudo chmod 600 /etc/cron.allow

Steps 16-20: Advanced Hardening

Step 16: Enable Process Accounting

sudo apt install acct -y
sudo systemctl enable acct

# View login history
lastlog

# View command accounting
lastcomm deployer

Step 17: Configure Logrotate for Audit Logs

sudo tee /etc/logrotate.d/audit << 'EOF'
/var/log/audit/audit.log {
weekly
rotate 52
compress
delaycompress
notifempty
create 0600 root root
postrotate
/usr/sbin/service auditd restart
endscript
}
EOF

Step 18: Disable IPv6 (If Not Used)

# Add to /etc/sysctl.d/99-security.conf
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1

Step 19: Set GRUB Password

# Generate a hashed password
sudo grub-mkpasswd-pbkdf2
# Copy the hash and add to /etc/grub.d/40_custom:
# set superusers="admin"
# password_pbkdf2 admin <hash>
sudo update-grub

Step 20: Run a Security Audit with Lynis

# Install Lynis
sudo apt install lynis -y

# Run a full system audit
sudo lynis audit system

# Check the hardening index (aim for 80+)
grep "hardening_index" /var/log/lynis-report.dat

Lynis scores your system on a 0-100 hardening index and provides specific recommendations. Run it after applying all the steps above — you should score 80+.

Verification Checklist

After applying all 20 steps, verify your work:

# Quick verification script
echo "=== SSH Config ==="
sshd -T | grep -E "permitrootlogin|passwordauthentication|pubkeyauthentication"

echo "=== Firewall ==="
sudo ufw status | head -20

echo "=== Fail2ban ==="
sudo fail2ban-client status

echo "=== Listening Services ==="
ss -tulnp | grep LISTEN

echo "=== Kernel Hardening ==="
sysctl net.ipv4.conf.all.rp_filter net.ipv4.tcp_syncookies kernel.randomize_va_space

echo "=== File Permissions ==="
stat -c '%a %n' /etc/shadow /etc/passwd /etc/ssh/sshd_config

Now that your server is hardened, you need to know how to troubleshoot it when things go wrong — next we cover strace, lsof, tcpdump, and systematic debugging methodology.