Server Hardening Quickstart (SSH, Passkeys, Fail2ban)
Type 2+ characters.
Practical hardening steps for an internet-facing Linux box (tested on Debian/Ubuntu). Adjust paths and package managers as needed.
Before you start
- Replace
adminuserwith your actual sudo-capable account name. - Use your server IP/hostname anywhere
SERVER_IPappears. - In the Fail2ban config, update
destemail/senderor switch toaction = %(action_)sif you do not want email. - In UFW, only allow the ports you truly need (e.g., drop 80/443 if not serving web traffic; add others if required).
- Then run the steps below in order on the target server.
1) Patch and add a sudo user
sudo apt update && sudo apt upgrade -y
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
sudo adduser adminuser
sudo usermod -aG sudo adminuser
- Enable automatic security updates; use the new user for SSH, not
root.
2) Configure SSH for keys + passkeys
Generate a FIDO2-backed key on your laptop (requires a hardware key with resident credential support):
ssh-keygen -t ed25519-sk -O resident -O verify-required -C "adminuser@server-fido2"
- If your token lacks resident storage, drop
-O resident. - Copy the public key to the server:
ssh-copy-id adminuser@SERVER_IP
Harden /etc/ssh/sshd_config (append or edit):
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
AllowUsers adminuser
MaxAuthTries 3
MaxStartups 10:30:60
ClientAliveInterval 300
ClientAliveCountMax 2
LogLevel VERBOSE
Then reload:
sudo systemctl reload sshd
3) Firewall: allow only what you use
sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # or: sudo ufw allow 22/tcp
sudo ufw allow 80/tcp 443/tcp # if serving web traffic
sudo ufw enable
sudo ufw status
4) Fail2ban for SSH
sudo apt install fail2ban -y
cat <<'EOF' | sudo tee /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 4
backend = systemd
destemail = [email protected]
sender = fail2ban@server
mta = sendmail
[sshd]
enabled = true
filter = sshd
port = ssh
logpath = %(sshd_log)s
maxretry = 4
action = %(action_mwl)s
EOF
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
- Uses systemd journal, emails ban reports (
action_mwl); adjust email settings or switch toaction = %(action_)sif no mailer.
5) Additional hardening knobs
sudo apt install auditd lynisfor baseline auditing checks.sudo sysctl -w net.ipv4.conf.all.rp_filter=1 net.ipv4.tcp_syncookies=1 net.ipv4.conf.all.accept_source_route=0and persist via/etc/sysctl.d/99-hardening.conf.- Disable unused services:
systemctl list-unit-files --type=service --state=enabledthensudo systemctl disable --now name.
6) Quick verification
- New session uses passkey:
ssh -v adminuser@SERVER_IPshould showdebug1: Offering public key: ...sk. - Root login refused:
ssh root@SERVER_IPshould fail. - Firewall:
sudo ufw statusshows only required ports. - Fail2ban jails:
sudo fail2ban-client statuslistssshdwith active bans.
Troubleshooting
- If passkey prompts fail, ensure your FIDO2 token is inserted/unlocked and
libfido2is installed on the client. - Locked out by firewall? Use console/serial access and run
ufw disabletemporarily.