Linux Server Hardening:
A Practical Checklist

A freshly provisioned VPS comes with password authentication over SSH, no firewall, and a root account waiting to be brute-forced. This checklist covers every essential layer - from updated packages to kernel hardening - so your server is locked down before it sees real traffic.

1. Keep Packages Updated Automatically

The single most effective thing you can do to improve server security is keep software up to date. Many serious breaches exploit vulnerabilities that had patches available for months. On Debian and Ubuntu, unattended-upgrades handles this automatically.

apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

To also auto-reboot after kernel updates (recommended for long-running servers), edit /etc/apt/apt.conf.d/50unattended-upgrades and uncomment the Automatic-Reboot line. Set a reboot time like "02:00" to pick a low-traffic window.

2. Harden SSH Configuration

SSH is the primary attack surface of any internet-facing server. The default configuration is far too permissive. Edit /etc/ssh/sshd_config and apply these settings:

# Disable root login entirely
PermitRootLogin no

# Require public key authentication, disable passwords
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey

# Restrict to specific users (replace 'deploy' with your username)
AllowUsers deploy

# Reduce login timeout and attempts
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 5

# Disable unused features
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no

# Keep connections alive
ClientAliveInterval 300
ClientAliveCountMax 2

After editing, validate the config before restarting: sshd -t. If it returns cleanly, restart with systemctl restart sshd. Make absolutely sure you have a working SSH key loaded in ~/.ssh/authorized_keys for your user before disabling password auth.

⚠️
Don't lock yourself out. Test your SSH key login in a second terminal before closing your current session. If you lose access to a VPS, recovery usually requires a console through the provider's web panel.

3. Configure a Firewall with UFW

Linux ships with nftables (or iptables) under the hood, but UFW (Uncomplicated Firewall) provides a human-readable interface that's hard to misconfigure. A good default policy is deny everything, then explicitly allow what you need.

# Set default policies
ufw default deny incoming
ufw default allow outgoing

# Allow SSH (adjust port if you changed it)
ufw allow 22/tcp

# Allow web traffic if this is a web server
ufw allow 80/tcp
ufw allow 443/tcp

# Enable the firewall
ufw enable

# Verify rules
ufw status verbose

If you're running additional services - a PostgreSQL database, a custom application port, or a WireGuard VPN - add only those ports. The principle of least privilege applies to network access too.

4. Deploy Fail2Ban

Fail2Ban scans log files for repeated failed authentication attempts and temporarily bans the offending IP via firewall rules. It's especially effective against SSH brute-force attacks, which are constant on any public server.

apt install fail2ban

# Create a local override (don't edit /etc/fail2ban/jail.conf directly)
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local and configure the SSH jail:

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 4
findtime = 600
bantime  = 3600

This bans an IP for one hour after four failed attempts within ten minutes. Restart with systemctl restart fail2ban and monitor with fail2ban-client status sshd.

5. Harden Kernel Parameters

The Linux kernel exposes a large number of tunable parameters via /proc/sys. Several of these have security implications and are set to insecure defaults. Create /etc/sysctl.d/99-hardening.conf:

# Prevent SYN flood attacks
net.ipv4.tcp_syncookies = 1

# Disable IP source routing and redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0

# Log martian packets (useful for detecting spoofing)
net.ipv4.conf.all.log_martians = 1

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

# Restrict access to kernel pointers in /proc
kernel.kptr_restrict = 2

# Restrict dmesg access to root
kernel.dmesg_restrict = 1

# Protect hard and symlinks from privilege escalation attacks
fs.protected_hardlinks = 1
fs.protected_symlinks = 1

Apply immediately with: sysctl -p /etc/sysctl.d/99-hardening.conf

6. Disable Unnecessary Services

Every running service that isn't needed is an unnecessary attack surface. List all enabled services and disable anything you don't recognise or require:

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

# Disable services you don't need (examples)
systemctl disable --now avahi-daemon
systemctl disable --now cups
systemctl disable --now bluetooth

Common services to investigate on a minimal server: avahi-daemon (mDNS), cups (printing), rpcbind (NFS), postfix (if you're not sending mail). Check listening ports with ss -tlnp to see what's actually exposed.

7. Audit Your System with Lynis

Lynis is an open-source security auditing tool that scans your system and generates a hardening index score along with specific recommendations. It's invaluable for catching things you've missed.

apt install lynis
lynis audit system

The output colour-codes findings as warnings (yellow) and suggestions (green). Aim for a hardening index above 70. Run Lynis periodically - a cronjob once a week is a good baseline - and treat new warnings as action items.

💡
Go further with rkhunter. Install rkhunter alongside Lynis to scan for rootkits and known suspicious file properties. Run rkhunter --check after initial setup and establish a baseline, so any future changes are flagged.

Quick Summary Checklist

  • Enable unattended-upgrades for automatic security patches
  • Disable SSH root login and password authentication
  • Restrict SSH to named users only
  • Configure UFW with default-deny and explicit allow rules
  • Install and configure Fail2Ban for the SSH jail
  • Apply kernel hardening parameters via sysctl.d
  • Disable all services not actively needed
  • Run a Lynis audit and work through the warnings
  • Test your configuration - scan with nmap -sV yourserver from an external host
Services Technologies Process Blog Get in Touch