Skip to main content

Linux server security hardening guide 2026

TL;DR

Step-by-step guide to hardening an Ubuntu or Debian server covering SSH, firewalls, fail2ban, automatic updates, and kernel-level protections.

Key facts

Topology
Ubuntu/Debian security hardening

TL;DR

A fresh Linux server is not production-ready. Default configurations expose SSH on port 22 with password authentication, run unnecessary services, and ship with permissive kernel network parameters. This guide walks through hardening an Ubuntu or Debian server from first login to a locked-down state suitable for production workloads.

SSH hardening

SSH is the first thing attackers probe. Tighten the configuration in /etc/ssh/sshd_config:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?Port .*/Port 2222/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?MaxAuthTries.*/MaxAuthTries 3/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?X11Forwarding.*/X11Forwarding no/' /etc/ssh/sshd_config

sudo systemctl restart sshd

Before restarting, confirm you have key-based access on the new port from a second terminal so you do not lock yourself out.

UFW firewall

Enable UFW and allow only the ports you actually need:

sudo apt install -y ufw
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'
sudo ufw --force enable
sudo ufw status verbose

fail2ban

fail2ban monitors log files and bans IPs that show brute-force patterns:

sudo apt install -y fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local to enable the SSH jail with your custom port:

[sshd]
enabled  = true
port     = 2222
maxretry = 5
bantime  = 3600
findtime = 600
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Automatic security updates

Unattended-upgrades ensures critical patches are applied without manual intervention:

sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades

Verify the configuration allows security updates:

grep -i "Unattended-Upgrade::Allowed-Origins" /etc/apt/apt.conf.d/50unattended-upgrades

Disable unused services

List active services and disable anything you do not need:

sudo systemctl list-units --type=service --state=running
sudo systemctl disable --now cups
sudo systemctl disable --now avahi-daemon

File permissions audit

Ensure no world-writable files exist outside of /tmp and /var/tmp:

sudo find / -xdev -type f -perm -0002 -not -path "/tmp/*" -not -path "/var/tmp/*" 2>/dev/null
sudo find / -xdev -type f -perm -4000 2>/dev/null

Review any unexpected SUID binaries and remove the bit if they are not required.

Kernel-level sysctl hardening

Apply network-level protections via /etc/sysctl.d/99-hardening.conf:

cat <<'EOF' | sudo tee /etc/sysctl.d/99-hardening.conf
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv6.conf.all.accept_redirects = 0
kernel.randomize_va_space = 2
EOF

sudo sysctl --system

SYN cookies protect against SYN flood attacks, martian logging flags packets with impossible source addresses, and ASLR (randomize_va_space = 2) hardens against memory exploitation.

Log monitoring

Centralise log review so anomalies surface quickly:

sudo apt install -y logwatch
sudo logwatch --detail high --mailto admin@example.com --range today

Schedule logwatch via cron for daily summaries. For real-time alerting, ship logs to a centralised aggregator and set thresholds on authentication failures and kernel warnings.

With Reflex

Reflex continuously monitors your servers for security drift — open ports, failed login spikes, package vulnerabilities, and configuration changes — alerting you before an attacker exploits the gap. See How it works.