Deploying a Linux server without proper security hardening is like leaving your front door wide open. Automated scanners, botnets, and opportunistic attackers continuously probe the internet for vulnerable systems. A freshly installed server can receive its first attack within minutes of going online.
This checklist covers the essential security measures every production Linux server needs, organized from most critical to supplementary. Work through it systematically and your server will be significantly harder to compromise.
Phase 1: Immediate Actions (First 30 Minutes)
1.1 Update Everything
The single most important security measure. Unpatched software is the number one attack vector.
# Debian/Ubuntu
sudo apt update && sudo apt upgrade -y
# RHEL/AlmaLinux/Rocky
sudo dnf update -y
# Enable automatic security updates
# Debian/Ubuntu
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades
# RHEL/AlmaLinux
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic.timer
1.2 SSH Hardening
SSH is the primary access point to your server and the most attacked service. Lock it down immediately.
# /etc/ssh/sshd_config - Critical changes:
# Disable root login
PermitRootLogin no
# Use key-based authentication only
PasswordAuthentication no
PubkeyAuthentication yes
# Change default port (optional but reduces noise)
Port 2222
# Limit access to specific users
AllowUsers deploy admin
# Disable empty passwords
PermitEmptyPasswords no
# Set idle timeout (5 minutes)
ClientAliveInterval 300
ClientAliveCountMax 0
# Disable X11 forwarding
X11Forwarding no
# Disable TCP forwarding (if not needed)
AllowTcpForwarding no
# Use strong key exchange and ciphers
KexAlgorithms 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
# Restart SSH (keep your current session open!)
sudo systemctl restart sshd
# Test new connection in a SEPARATE terminal before closing current one
1.3 Configure Firewall
Only allow traffic you explicitly need. Default policy: deny everything, allow specific ports.
# Using UFW (Ubuntu/Debian)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp # SSH (custom port)
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
# Using firewalld (RHEL/AlmaLinux)
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 --reload
📚 Recommended Reading
Comprehensive security guides for Linux administrators:
- Linux Security Hardening — €14.90 — Complete hardening guide for production servers
- Linux Security Essentials — €9.90 — Foundational security knowledge
- Linux Firewall Configuration — €12.90 — Master iptables, nftables, and firewalld
Phase 2: User and Access Management
2.1 Create a Non-Root Admin User
# Create admin user
sudo adduser deploy
sudo usermod -aG sudo deploy
# Set up SSH key for the new user
sudo mkdir -p /home/deploy/.ssh
sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
2.2 Password Policy
# Install password quality checker
sudo apt install libpam-pwquality
# /etc/security/pwquality.conf
minlen = 14
dcredit = -1 # At least 1 digit
ucredit = -1 # At least 1 uppercase
lcredit = -1 # At least 1 lowercase
ocredit = -1 # At least 1 special character
maxrepeat = 3 # Max 3 consecutive identical characters
# Set password aging policy
sudo chage -M 90 -m 7 -W 14 deploy
2.3 Sudo Configuration
# Use visudo for safe editing
sudo visudo
# Add timestamp timeout (re-enter password every 5 min)
Defaults timestamp_timeout=5
# Log all sudo commands
Defaults logfile="/var/log/sudo.log"
# Restrict specific users to specific commands
deploy ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart php*
Phase 3: File System Security
3.1 Critical File Permissions
# Secure critical system files
sudo chmod 600 /etc/shadow
sudo chmod 600 /etc/gshadow
sudo chmod 644 /etc/passwd
sudo chmod 644 /etc/group
sudo chmod 700 /root
sudo chmod 600 /boot/grub/grub.cfg
# Find world-writable files (potential security risk)
find / -type f -perm -o+w -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null
# Find files with SUID/SGID bits set
find / -type f \( -perm -4000 -o -perm -2000 \) -not -path "/proc/*" 2>/dev/null
# Find files with no owner
find / -nouser -o -nogroup 2>/dev/null | head -20
3.2 Mount Point Hardening
# /etc/fstab - Add security options to mount points
# /tmp with noexec, nosuid, nodev
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=2G 0 0
# /var/tmp linked to /tmp or similarly restricted
# /home with nosuid
/dev/mapper/vg-home /home ext4 defaults,nosuid,nodev 0 2
Phase 4: Kernel and Network Hardening
4.1 Sysctl Hardening
# /etc/sysctl.d/99-security.conf
# Disable IP forwarding (unless this is a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Don't send ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Enable SYN cookies (protect against SYN flood)
net.ipv4.tcp_syncookies = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
# Ignore broadcast ping requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Restrict kernel pointer exposure
kernel.kptr_restrict = 2
# Restrict dmesg access
kernel.dmesg_restrict = 1
# Disable core dumps
fs.suid_dumpable = 0
# Apply changes
sudo sysctl -p /etc/sysctl.d/99-security.conf
Phase 5: Intrusion Detection and Monitoring
5.1 Install Fail2Ban
# Install
sudo apt install fail2ban
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
destemail = admin@example.com
action = %(action_mwl)s
[sshd]
enabled = true
port = 2222
maxretry = 3
bantime = 86400
[nginx-http-auth]
enabled = true
port = http,https
[nginx-botsearch]
enabled = true
port = http,https
# Start and check status
sudo systemctl enable --now fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd
5.2 File Integrity Monitoring with AIDE
# Install AIDE
sudo apt install aide
# Initialize database
sudo aideinit
# Move new database into place
sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Run a check
sudo aide --check
# Add to cron for daily checks
0 5 * * * /usr/bin/aide --check | mail -s "AIDE Report $(hostname)" admin@example.com
📚 Advanced Security Topics
Go deeper into Linux security:
- Linux Security Auditing — €14.90 — Professional auditing techniques and frameworks
- SELinux & AppArmor Guide — €16.90 — Mandatory Access Control deep dive
- Linux System Hardening — €13.90 — End-to-end system hardening
- Cybersecurity Fundamentals — €24.90 — Broad security foundation
Phase 6: Application Security
6.1 Web Server Hardening (Nginx)
# /etc/nginx/nginx.conf or server block
# Hide version number
server_tokens off;
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Limit request size
client_max_body_size 10m;
# Limit request rate
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req zone=general burst=20 nodelay;
6.2 Database Security
# PostgreSQL - pg_hba.conf
# Use md5 or scram-sha-256 authentication
local all all scram-sha-256
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# Never allow remote connections without SSL
hostssl all all 0.0.0.0/0 scram-sha-256
Security Hardening Checklist Summary
| Category | Action | Priority |
|---|---|---|
| Updates | Enable automatic security updates | Critical |
| SSH | Disable root login, use key-based auth only | Critical |
| Firewall | Default deny, allow only needed ports | Critical |
| Users | Non-root admin user, strong password policy | High |
| Fail2Ban | Brute force protection for SSH and web | High |
| Kernel | Sysctl network and kernel hardening | High |
| Files | Correct permissions, mount options | Medium |
| AIDE | File integrity monitoring | Medium |
| Web Server | Security headers, rate limiting, hide version | Medium |
| Audit | Regular security audits and log review | Medium |
Conclusion
Security hardening is not a one-time task — it is an ongoing process. Start with the Critical items in Phase 1 (they take 30 minutes), then work through the remaining phases over the following days. Schedule monthly security reviews to verify your configurations remain intact and update your practices as new threats emerge.
The goal is not to make your server impenetrable (nothing is), but to make it hard enough that attackers move on to easier targets. By following this checklist, you raise the bar significantly above the vast majority of internet-facing servers.