How to Harden Your Linux Server: A Security Checklist

6 min read
Intermediate Security Linux Hardening Firewall

Every Linux server connected to the internet is under constant attack. Automated bots scan for open ports, try default passwords, and exploit known vulnerabilities 24/7. If you spin up a new VPS and check the auth logs after an hour, you will already see hundreds of brute-force attempts.

Hardening is the process of reducing your server's attack surface — closing doors you do not need open, locking the ones that stay open, and monitoring for anything unusual. This guide is a practical checklist you can follow for any production Linux server.

1. Keep the System Updated

The single most important security measure. Most breaches exploit known vulnerabilities that already have patches available.

# Update package lists and upgrade everything
sudo apt update && sudo apt upgrade -y

# Enable automatic security updates (Ubuntu/Debian)
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Verify automatic updates are working:

cat /etc/apt/apt.conf.d/20auto-upgrades
# Should contain:
# APT::Periodic::Update-Package-Lists "1";
# APT::Periodic::Unattended-Upgrade "1";

For CentOS/RHEL:

sudo yum update -y
sudo yum install dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer

2. SSH Hardening

SSH is the front door to your server. Lock it down tight.

Disable Root Login

# /etc/ssh/sshd_config
PermitRootLogin no

Create a regular user and use sudo instead:

adduser sam
usermod -aG sudo sam

Disable Password Authentication

Use SSH keys instead of passwords. Passwords can be brute-forced; keys cannot.

# /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes

Make sure you have your SSH key copied to the server before doing this, or you will lock yourself out.

Change the Default Port

Moving off port 22 eliminates 99% of automated brute-force bots:

# /etc/ssh/sshd_config
Port 2222

Limit Users

Only allow specific users to SSH:

# /etc/ssh/sshd_config
AllowUsers sam deploy

Apply Changes

sudo sshd -t                # Test config (catch errors before restart)
sudo systemctl restart sshd

Warning: Always keep an existing SSH session open when changing SSH config. If the new config has an error, you will not be able to reconnect.

3. Firewall Setup

Only expose ports you actually need. Block everything else.

UFW (Ubuntu/Debian — Easiest)

sudo apt install ufw

# Default: deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (use your custom port if changed)
sudo ufw allow 2222/tcp

# Allow web traffic
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable the firewall
sudo ufw enable

# Check status
sudo ufw status verbose

iptables (More Control)

# Flush existing rules
sudo iptables -F

# Default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT

# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow SSH
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT

# Allow HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Save rules (persist across reboot)
sudo apt install iptables-persistent
sudo netfilter-persistent save

Use our Port Scanner to verify only the intended ports are accessible from outside.

4. Install Fail2Ban

Fail2ban monitors log files for repeated authentication failures and automatically bans offending IPs.

sudo apt install fail2ban
sudo systemctl enable --now fail2ban

Create a local config (do not edit the main file):

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local:

[DEFAULT]
bantime = 3600          # Ban for 1 hour
findtime = 600          # Look at last 10 minutes
maxretry = 3            # Ban after 3 failures
banaction = ufw         # Use UFW (or iptables)

[sshd]
enabled = true
port = 2222             # Your SSH port
logpath = /var/log/auth.log
maxretry = 3
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd    # Check banned IPs

5. User Management

Principle of Least Privilege

Every user should have only the permissions they need. Do not give everyone sudo access.

# Create a user without sudo
adduser deployer

# Create a user with sudo
adduser admin
usermod -aG sudo admin

# Remove sudo from a user
deluser sam sudo

Check Who Has Sudo

grep -E '^sudo:|^wheel:' /etc/group

Check for Users with Empty Passwords

sudo awk -F: '($2 == "" || $2 == "!") {print $1}' /etc/shadow

Disable Unused Accounts

# Lock an account (cannot log in)
sudo usermod -L olduser

# Set shell to nologin
sudo usermod -s /usr/sbin/nologin serviceaccount

6. File Permissions

Misconfigured permissions are a common attack vector.

Critical Files

# SSH config
chmod 600 /etc/ssh/sshd_config

# SSH keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

# System files
chmod 644 /etc/passwd
chown root:shadow /etc/shadow && chmod 640 /etc/shadow

# Web files
chmod 755 /var/www/html/           # Directories
find /var/www/html -type f -exec chmod 644 {} \;   # Files

Find World-Writable Files

# Find files anyone can write to (potential security risk)
find / -perm -o+w -type f 2>/dev/null | grep -v '/proc\|/sys\|/dev'

Find SUID/SGID Binaries

# These run with elevated privileges — review them
find / -perm -4000 -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null

Use our Chmod Calculator to quickly determine the right permission values.

7. Disable Unnecessary Services

Every running service is a potential attack vector. Disable what you do not need.

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

# Common services to disable if not needed:
sudo systemctl disable --now cups          # Printing
sudo systemctl disable --now avahi-daemon  # mDNS
sudo systemctl disable --now bluetooth    # Bluetooth
sudo systemctl disable --now rpcbind      # NFS

8. Network Security

Disable IPv6 (If Not Used)

# /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
sudo sysctl -p

Prevent IP Spoofing

# /etc/sysctl.conf
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

Disable ICMP Redirects

# /etc/sysctl.conf
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

Enable SYN Flood Protection

# /etc/sysctl.conf
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2

Apply all changes:

sudo sysctl -p

9. Logging and Monitoring

Check Auth Logs

# Recent login attempts
journalctl -u sshd --since "1 hour ago"

# Failed SSH logins
grep "Failed password" /var/log/auth.log | tail -20

# Successful logins
grep "Accepted" /var/log/auth.log | tail -20

# Currently logged in users
who
w
last -10

Monitor Disk Space

df -h

Running out of disk space can crash services and prevent logging (hiding attacker activity).

Set Up Log Rotation

Ensure logs do not fill up the disk:

# Check logrotate config
cat /etc/logrotate.d/rsyslog

10. SSL/TLS for Everything

Every service exposed to the internet should use TLS:

# Install certbot for free SSL
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com

# Auto-renewal
sudo certbot renew --dry-run

Test your SSL configuration with our SSL Server Test and verify HTTPS redirects with the HTTPS Redirect Tester.

Quick Hardening Checklist

□ System updated, auto-updates enabled
□ SSH: root login disabled
□ SSH: password auth disabled, keys only
□ SSH: port changed from 22
□ SSH: specific users allowed
□ Firewall: default deny, only needed ports open
□ Fail2ban: installed and configured
□ Users: no unnecessary sudo access
□ Users: no empty passwords
□ Permissions: critical files locked down
□ Services: unused services disabled
□ Network: sysctl hardened
□ Logging: auth logs monitored
□ SSL: TLS on all public services
□ Backups: running and tested

Related Tools

Need a VPS? Vultr (free credit), DigitalOcean ($200 free credit), or RackNerd (cheap annual deals).