Linux Server Hardening Checklist 2026: A Complete Security Guide
A practical, step-by-step Linux server hardening guide covering SSH lockdown, firewall rules, kernel tuning, audit logging, automatic patching, and CIS benchmark alignment for Ubuntu and RHEL.
Why Server Hardening Matters
A default Linux installation is designed for ease of use, not security. Default configurations include SSH root login enabled, no firewall rules, unnecessary services running, weak password policies, and no audit logging. Each of these is an attack vector. Hardening closes these gaps systematically.
Pre-Hardening: Document Your Current State
# System information
uname -a
cat /etc/os-release
# Running services
systemctl list-units --type=service --state=running
# Open ports
ss -tulnp
# Current users with login access
cat /etc/passwd | grep -v nologin | grep -v false
# Installed packages count
dpkg -l | wc -l # Debian/Ubuntu
rpm -qa | wc -l # RHEL/CentOSStep 1: System Updates and Automatic Patching
Apply All Updates
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# RHEL/Rocky/Alma
sudo dnf update -yEnable Automatic Security Updates
Ubuntu:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
# Verify
cat /etc/apt/apt.conf.d/20auto-upgradesRHEL/Rocky:
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timerStep 2: SSH Hardening
SSH is the most attacked service on any Linux server.
Edit /etc/ssh/sshd_config:
# Disable root login
PermitRootLogin no
# Disable password auth (use keys only)
PasswordAuthentication no
PubkeyAuthentication yes
# Change default port
Port 2222
# Limit to specific users
AllowUsers deployer admin
# Idle timeout (5 min)
ClientAliveInterval 300
ClientAliveCountMax 0
# Disable forwarding
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
# Strong ciphers only
KexAlgorithms curve25519-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
# Limit attempts
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 30Set Up Key-Based Authentication
# Generate Ed25519 key (local machine)
ssh-keygen -t ed25519 -C "admin@server"
# Copy to server
ssh-copy-id -p 2222 admin@your-server
# Test key login, THEN disable password auth
ssh -p 2222 admin@your-server
sudo systemctl restart sshdInstall Fail2Ban
sudo apt install fail2ban # Ubuntu
sudo dnf install fail2ban # RHELCreate /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600sudo systemctl enable --now fail2banStep 3: Firewall Configuration
UFW (Ubuntu)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# Allow SNMP from monitoring server only
sudo ufw allow from 10.0.0.100 to any port 161 proto udp comment 'SNMP'
sudo ufw enable
sudo ufw status verbosefirewalld (RHEL)
sudo firewall-cmd --set-default-zone=drop
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reloadiptables Rate Limiting
# Rate limit SSH (max 4 connections per minute per IP)
sudo iptables -A INPUT -p tcp --dport 2222 -m recent --set --name SSH
sudo iptables -A INPUT -p tcp --dport 2222 -m recent --update \
--seconds 60 --hitcount 4 --name SSH -j DROPStep 4: User Account Security
Password Policy
Edit /etc/security/pwquality.conf:
minlen = 14
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
maxrepeat = 3
Account Lockout
Add to /etc/pam.d/common-auth:
auth required pam_faillock.so preauth deny=5 unlock_time=900
auth required pam_faillock.so authfail deny=5 unlock_time=900
Sudo Hardening
sudo visudo
# Add:
Defaults timestamp_timeout=5
Defaults logfile="/var/log/sudo.log"Remove Unused Accounts
# Lock unused accounts
sudo usermod -L olduser
sudo usermod -s /usr/sbin/nologin olduserStep 5: Kernel Hardening
Create /etc/sysctl.d/99-hardening.conf:
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Disable IP forwarding (unless router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Ignore ICMP broadcasts
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Log martian packets
net.ipv4.conf.all.log_martians = 1
# Restrict kernel pointer access
kernel.kptr_restrict = 2
# Restrict dmesg
kernel.dmesg_restrict = 1
# Disable core dumps
fs.suid_dumpable = 0
# ASLR enabled
kernel.randomize_va_space = 2
# Restrict ptrace
kernel.yama.ptrace_scope = 2sudo sysctl --systemStep 6: Audit Logging
Configure auditd
sudo apt install auditd audispd-pluginsAdd rules to /etc/audit/rules.d/hardening.rules:
# Monitor authentication files
-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
# Monitor SSH config
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Monitor cron
-w /etc/crontab -p wa -k cron
-w /var/spool/cron/ -p wa -k cron
# Monitor privilege escalation
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -k privilege_escalation
# Monitor kernel module loading
-a always,exit -F arch=b64 -S init_module -S finit_module -k kernel_modules
# Make config immutable (requires reboot to change)
-e 2sudo systemctl enable --now auditd
sudo augenrules --loadStep 7: Disable Unnecessary Services
# Common services to disable on servers
sudo systemctl disable --now avahi-daemon # mDNS
sudo systemctl disable --now cups # Printing
sudo systemctl disable --now bluetooth # Bluetooth
sudo systemctl disable --now ModemManager # Modem
sudo systemctl disable --now rpcbind # NFS (if not used)
# Verify
systemctl list-units --type=service --state=runningStep 8: File System Security
Secure Mount Options in /etc/fstab
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
tmpfs /var/tmp tmpfs defaults,noexec,nosuid,nodev 0 0
File Permissions
sudo chmod 600 /etc/shadow
sudo chmod 600 /etc/gshadow
sudo chmod 644 /etc/passwd
sudo chmod 700 /root
# Find SUID binaries (potential privilege escalation)
find / -type f -perm -4000 2>/dev/nullStep 9: Intrusion Detection
AIDE (File Integrity Monitor)
sudo apt install aide
sudo aide --init
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Add to daily cron
echo "0 3 * * * /usr/bin/aide --check" | sudo crontab -Lynis (Security Auditor)
sudo apt install lynis
sudo lynis audit system
# Review the hardening index score
# Aim for 80+ on a production serverStep 10: Monthly Verification Script
#!/bin/bash
# hardening-check.sh
echo "=== SSH Config ==="
grep -E "^(PermitRootLogin|PasswordAuthentication|Port)" /etc/ssh/sshd_config
echo -e "\n=== Firewall Status ==="
sudo ufw status numbered 2>/dev/null || sudo firewall-cmd --list-all
echo -e "\n=== Failed Logins (7 Days) ==="
sudo journalctl -u sshd --since "7 days ago" | grep "Failed" | wc -l
echo -e "\n=== Users With Login Shell ==="
grep -v 'nologin\|false' /etc/passwd
echo -e "\n=== Listening Ports ==="
ss -tulnp
echo -e "\n=== Pending Updates ==="
apt list --upgradable 2>/dev/null | grep -i security
echo -e "\n=== Fail2Ban ==="
sudo fail2ban-client status sshd 2>/dev/null
echo -e "\n=== Audit Rules ==="
sudo auditctl -l | wc -lCIS Benchmark Alignment
| CIS Control | Guide Section | |-------------|---------------| | 1.1 Filesystem Configuration | Step 8 | | 2.1 Special Purpose Services | Step 7 | | 3.1-3.5 Network Configuration | Steps 3, 5 | | 4.1-4.2 Logging and Auditing | Step 6 | | 5.1 SSH Configuration | Step 2 | | 5.2-5.4 User Accounts | Step 4 | | 6.1 File Permissions | Step 8 |
Frequently Asked Questions
Should I change the SSH port?
Changing the port from 22 to a non-standard port reduces automated scanner noise by 90%+. It's not true security — any targeted attacker will find the port — but it dramatically reduces brute-force attempts and log noise, making real attacks easier to spot.
How often should I run hardening checks?
Run automated checks monthly. Review audit logs weekly. Apply security patches as soon as they're released — automatic security updates handle this. Run Lynis quarterly for a comprehensive audit.
Does kernel hardening affect performance?
Most kernel hardening parameters have negligible performance impact. The exception is ptrace_scope, which can break debugging tools. Use ptrace_scope = 1 on development servers instead of 2.
What about containers and cloud servers?
Container hosts need additional hardening — Docker daemon configuration, runtime security, and namespace restrictions. Cloud VMs should follow this guide plus cloud-specific controls like security groups, IAM roles, and encrypted volumes.