Why Hardening Matters
A default Linux installation is designed for broad compatibility, not maximum security. Default settings leave unnecessary services running, weak password policies in place, and valuable logging disabled. Hardening is the systematic process of reducing the attack surface.
Scope: This guide targets Ubuntu 22.04 LTS / Debian 12 on a VPS or bare-metal server.
1. Initial System Updates
Always start with a fully patched system.
apt update && apt full-upgrade -y
apt autoremove -y && apt autoclean
Enable unattended security upgrades:
apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades
2. SSH Hardening
SSH is the most common attack vector on internet-exposed servers. Lock it down hard.
# Edit /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
X11Forwarding no
AllowTcpForwarding no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
# Change the default port (optional, reduces noise in logs)
Port 2222
Reload SSH after editing:
systemctl reload sshd
3. Firewall with UFW
apt install ufw -y
# Default deny-all policy
ufw default deny incoming
ufw default allow outgoing
# Allow only what you need
ufw allow 2222/tcp # SSH (your custom port)
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw enable
ufw status verbose
4. Fail2Ban: Auto-Block Brute Force
apt install fail2ban -y
# Create local override (never edit jail.conf directly)
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = 2222
EOF
systemctl enable --now fail2ban
fail2ban-client status sshd
5. Kernel Parameter Hardening (sysctl)
cat >> /etc/sysctl.d/99-hardening.conf << 'EOF'
# Prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable IPv6 if unused
net.ipv6.conf.all.disable_ipv6 = 1
# Protect against SYN flood
net.ipv4.tcp_syncookies = 1
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
# Randomise kernel address space
kernel.randomize_va_space = 2
EOF
sysctl --system
6. Audit Logging with auditd
apt install auditd audispd-plugins -y
systemctl enable --now auditd
# Watch sensitive files
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/sudoers -p wa -k sudoers_changes
auditctl -w /var/log/auth.log -p r -k auth_log_read
# Make rules persistent
ausearch -k passwd_changes
Quick Reference Checklist
| Task | Status |
|---|---|
| System fully updated | ✅ |
| SSH: root login disabled | ✅ |
| SSH: password auth disabled | ✅ |
| Firewall (UFW) enabled | ✅ |
| Fail2Ban configured | ✅ |
| Kernel params hardened | ✅ |
| Auditd logging enabled | ✅ |
Next Steps
- Configure AppArmor or SELinux for mandatory access control.
- Deploy Wazuh or OSSEC for host-based intrusion detection.
- Set up centralised log shipping with rsyslog → SIEM.