A default Linux installation is not secure enough for production. I've seen servers compromised within hours of going live because basic hardening steps were skipped. This checklist covers twelve essential hardening measures that will significantly reduce your attack surface and protect your server from common threats.
Work through these steps in order. Each one builds on the last.
Step 1: Harden SSH Configuration
SSH is usually the first service attackers probe. Tighten it down before anything else.
Edit /etc/ssh/sshd_config and make these changes:
# Disable root login
PermitRootLogin no
# Use key authentication only
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Change default port (optional but helpful)
Port 2222
# Limit users who can SSH
AllowUsers deploy admin
# Disable X11 forwarding if not needed
X11Forwarding no
# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
Restart SSH after changes:
sudo systemctl restart sshd
Before you close your current session, open a new terminal and verify you can still connect. I've locked myself out more than once by testing the restart command in my only active session.
Step 2: Configure a Host-Based Firewall
Use iptables, nftables, or ufw depending on your distribution. For most Ubuntu/Debian systems, ufw provides the simplest interface.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp # Your SSH port
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
For CentOS/RHEL with firewalld:
sudo firewall-cmd --set-default-zone=drop
sudo firewall-cmd --zone=public --add-service=ssh --permanent
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
sudo firewall-cmd --reload
Only open ports you actually need. Every open port is a potential entry point.
Step 3: Install and Configure Fail2Ban
Fail2Ban monitors log files and blocks IPs that show malicious behavior—repeated failed login attempts, for example.
sudo apt install fail2ban # Debian/Ubuntu
sudo yum install fail2ban # CentOS/RHEL
Create a local configuration file:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
Start the service:
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Check banned IPs:
sudo fail2ban-client status sshd
In support tickets I handled, servers without fail2ban averaged dozens of brute-force attempts per hour. With it enabled, repeat offenders get blocked automatically.
Step 4: Enforce Proper User Permissions
Never run services as root. Create dedicated service accounts with minimal privileges.
sudo useradd -r -s /bin/false serviceuser
The -r flag creates a system account, and -s /bin/false prevents interactive login.
For human users, enforce sudo instead of direct root access:
sudo usermod -aG sudo username # Debian/Ubuntu
sudo usermod -aG wheel username # CentOS/RHEL
Set proper file permissions:
sudo chmod 700 /root
sudo chmod 600 /etc/ssh/sshd_config
sudo chmod 644 /etc/passwd
sudo chmod 600 /etc/shadow
Run a quick audit:
sudo find / -type f -perm -4000 -ls 2>/dev/null
This lists all setuid binaries. Review the output and remove setuid from any binaries that don't need it.
Step 5: Tune Kernel Parameters for Security
Edit /etc/sysctl.conf or create a new file in /etc/sysctl.d/:
# Disable IP forwarding if not routing
net.ipv4.ip_forward = 0
# Enable SYN cookies for SYN flood protection
net.ipv4.tcp_syncookies = 1
# Disable ICMP redirect acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Enable reverse path filtering
net.ipv4.conf.all.rp_filter = 1
# Ignore ICMP ping requests (optional)
net.ipv4.icmp_echo_ignore_all = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
Apply changes:
sudo sysctl -p
These parameters mitigate common network-based attacks and improve stack resilience.
Step 6: Enable SELinux or AppArmor
Mandatory access control adds a critical security layer. SELinux ships with RHEL/CentOS; AppArmor comes with Ubuntu/Debian.
For SELinux, verify it's enforcing:
getenforce
If it returns Permissive or Disabled, edit /etc/selinux/config:
SELINUX=enforcing
Reboot for the change to take effect.
For AppArmor on Ubuntu:
sudo systemctl status apparmor
sudo aa-status
Most distributions ship with profiles for common services. Load them:
sudo aa-enforce /etc/apparmor.d/*
Don't disable SELinux or AppArmor to "fix" an application issue. Write a custom policy instead.
Step 7: Set Up Audit Logging with auditd
The auditd daemon records security-relevant events: file access, system calls, authentication attempts.
sudo apt install auditd # Debian/Ubuntu
sudo yum install audit # CentOS/RHEL
Enable and start:
sudo systemctl enable auditd
sudo systemctl start auditd
Add rules to monitor sensitive files. Edit /etc/audit/rules.d/audit.rules:
# Monitor changes to passwd and group files
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
# Monitor sudo usage
-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
# Monitor authentication logs
-w /var/log/auth.log -p wa -k auth
Reload rules:
sudo augenrules --load
Query the audit log:
sudo ausearch -k identity
Audit logs are your forensic evidence if something goes wrong.
Step 8: Keep Packages Up to Date
Outdated software is the easiest way in. Automate updates where possible.
For Debian/Ubuntu, enable unattended upgrades:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
For CentOS/RHEL, use yum-cron or dnf-automatic:
sudo yum install yum-cron
sudo systemctl enable yum-cron
sudo systemctl start yum-cron
Edit /etc/yum/yum-cron.conf and set:
apply_updates = yes
Manually check for updates weekly:
sudo apt update && sudo apt upgrade # Debian/Ubuntu
sudo yum update # CentOS/RHEL
Subscribe to your distribution's security mailing list so you know about critical patches before they hit the news.
Step 9: Remove Unnecessary Services
Every running service expands your attack surface. List all active services:
sudo systemctl list-units --type=service --state=running
Disable anything you don't need:
sudo systemctl disable cups # Printing service
sudo systemctl stop cups
sudo systemctl disable avahi-daemon # Zero-config networking
sudo systemctl stop avahi-daemon
Common services to review: rpcbind, postfix (if you're not sending mail), bluetooth, ModemManager.
Check open ports:
sudo ss -tulnp
If you see a listening port you don't recognize, track down which process owns it and decide if it's needed.
Step 10: Implement File Integrity Monitoring
File integrity monitoring tools detect unauthorized changes to critical system files. Two popular options are AIDE and Tripwire.
Install AIDE:
sudo apt install aide # Debian/Ubuntu
sudo yum install aide # CentOS/RHEL
Initialize the database:
sudo aideinit
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Run a check:
sudo aide --check
Schedule daily checks with cron:
sudo crontab -e
Add:
0 3 * * * /usr/bin/aide --check | mail -s "AIDE Report" [email protected]
After legitimate system changes, update the AIDE database:
sudo aide --update
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Without file integrity monitoring, rootkits and backdoors can sit undetected for months.
Step 11: Restrict Root Access
Root should be used as little as possible. Disable root login entirely if you can.
Lock the root account:
sudo passwd -l root
If you need root access, use sudo instead of su. Configure sudo to require a password even for users in the sudo group.
Edit /etc/sudoers with visudo:
Defaults timestamp_timeout=0
This forces password re-entry for every sudo command.
Log all sudo commands:
Defaults logfile=/var/log/sudo.log
Review the sudo log regularly:
sudo tail -f /var/log/sudo.log
So what if you accidentally lock yourself out?
Most cloud providers offer console access through their web panel. Physical servers usually have out-of-band management (IPMI, iLO, iDRAC). Test your recovery method before you need it.
Step 12: Deploy Intrusion Detection
Intrusion detection systems monitor network traffic and system activity for signs of compromise. The two main types are network-based (NIDS) and host-based (HIDS).
For host-based detection, install OSSEC:
wget -q -O - https://updates.atomicorp.com/installers/atomic | sudo bash
sudo yum install ossec-hids # CentOS/RHEL
For Debian/Ubuntu, download from the OSSEC website and build from source, or use the Wazuh fork which provides packages.
Configure OSSEC to monitor logs, check file integrity, and detect rootkits:
sudo /var/ossec/bin/ossec-control start
For network intrusion detection, Suricata and Snort are the standard choices. They require more resources and expertise to tune properly, so start with host-based detection if you're new to IDS.
Review IDS alerts daily. Most will be false positives at first, but patterns emerge quickly.
Start with SSH and Firewall
If you only have time for two steps today, lock down SSH and enable your firewall. Those two measures stop the majority of automated attacks. Then work through the rest of the checklist over the next week. A hardened server won't prevent every attack, but it forces attackers to work much harder and leaves clear evidence when they try.
