I've hardened enough fresh VPS instances to know the drill. Here's the checklist I actually run, in order, every time. No security theater. No 200-page CIS benchmark. Just the things that stop real attacks.
Target: Ubuntu/Debian. Time: ~15 minutes if you copy-paste. Assumes root or sudo.
apt update && apt upgrade -y
apt autoremove -y
Not optional. Most exploits target known vulnerabilities with patches already shipped. If you're not updating, nothing else matters.
# Copy your SSH key first. Do NOT skip this.
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
Password auth is how most brute-force attacks succeed. SSH keys only. Period.
~/.ssh/authorized_keys BEFORE restarting sshd. Otherwise you lock yourself out.sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd
Log in as a regular user. Use sudo. Root login over SSH is an unnecessary risk.
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
# Only open what you need:
# ufw allow 80/tcp
# ufw allow 443/tcp
ufw enable
ufw status verbose
Default deny. Open only what you need. If you're running a web server, add 80 and 443. Nothing else.
apt install -y fail2ban
systemctl enable fail2ban
systemctl start fail2ban
Default config bans after 5 failed SSH attempts for 10 minutes. That's fine for most setups. If you want tighter settings:
cat > /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 3600
findtime = 600
EOF
systemctl restart fail2ban
# Check what's listening
ss -tlnp
# Remove common unnecessary packages
apt purge -y telnetd rsh-server tftpd 2>/dev/null
apt autoremove -y
Every listening service is an attack surface. If you don't need it, remove it.
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
Accept the prompt. This installs security updates automatically. You'll still need to handle major version upgrades manually, but day-to-day security patches land without intervention.
echo 'tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0' >> /etc/fstab
mount -o remount /run/shm
Prevents certain privilege escalation attacks that use shared memory for execution.
dpkg-statoverride --update --add root su 4750
groupadd -r wheel 2>/dev/null
usermod -aG wheel $(whoami)
echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel
chmod 440 /etc/sudoers.d/wheel
Only users in the wheel group can sudo. Reduces the blast radius of a compromised user account.
# Run all of these and confirm:
ss -tlnp # Only expected ports listening
ufw status # Only expected ports allowed
fail2ban-client status sshd # Ban counter active
cat /etc/ssh/sshd_config | grep -E '(PasswordAuth|PermitRoot)' # Both "no"
lastb | head # Check for brute force attempts
journalctl -u sshd | tail # No suspicious SSH activity
This is the 15-minute checklist. It stops drive-by scans, brute force, and low-effort exploits. It does NOT cover:
Those are phase two. This checklist gets you from "embarrassingly vulnerable" to "reasonably hardened" in the time it takes to brew coffee.
Done. Ship it.