Every server I deploy to starts with the same routine: kill password login, lock the door, and only let keys in. This is a case study in what I did, why I did it, and why the discipline matters — the actual commands I ran, the gotchas I hit, and the one rule that kept me from locking myself out.
Public-facing SSH with PasswordAuthentication yes is a liability. Bots scan the entire internet around the clock looking for open port 22, then brute-force weak credentials. A single weak password on any user account is all it takes for an attacker to walk into your server.
- ›Password guessing is automated — botnets hammer port 22 continuously, testing default and weak passwords
- ›Passwords are replayable — anything typed can be intercepted, phished, or reused from a breach elsewhere
- ›Passwords create support overhead — every teammate needs one, every reset is a chore, every shared credential is a leak risk
- ›Port 22 is a beacon — the default port tells attackers exactly where to aim
An SSH key pair replaces the shared secret with asymmetric cryptography. The private key never leaves your machine. The public key lives in ~/.ssh/authorized_keys on the server. A challenge-response exchange proves you hold the private key — no secret ever travels over the wire. No password to guess, nothing to intercept.
- ›Nothing to brute-force — a 256-bit Ed25519 key is not guessable
- ›Nothing travels in transit — the private key never leaves your machine
- ›Per-person accountability — each key maps to exactly one person and can be revoked independently
- ›Optional passphrase — even if the key file is stolen, it stays locked
Step 1: Generate a key pair on my machine
# Generate an Ed25519 key pair
ssh-keygen -t ed25519 -C "your_email@example.com"
# Verify both files exist
ls -l ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pubPress Enter to accept the default path, and set a passphrase if you want the key itself protected. This creates id_ed25519 (private key — never share it) and id_ed25519.pub (public key — this is what goes on the server).
Step 2: Push the public key to the first server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server_ipNo ssh-copy-id available? Do it manually:
cat ~/.ssh/id_ed25519.pub | ssh user@server_ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"This uses the password once to install the key — then ssh user@server_ip logs straight in with no prompt.
Step 3: Add every teammate the same way
For each additional person, ask for the contents of their id_ed25519.pub, then append it — one line per key in authorized_keys.
ssh user@server_ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo 'PASTE_THEIR_PUBLIC_KEY_HERE' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Step 4: Disable password authentication — without touching the main config
Instead of editing /etc/ssh/sshd_config directly, add a drop-in rule file. Cleaner, and it survives package upgrades:
sudo nano /etc/ssh/sshd_config.d/99-disable-password-auth.confPasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-passwordPermitRootLogin prohibit-password keeps root reachable by key but never by password. Verify the Include line exists at the top of /etc/ssh/sshd_config — modern OpenSSH auto-loads everything in /etc/ssh/sshd_config.d/*.conf.
Step 5: Change the SSH port
Port 2222Pick any unused port above 1024 — obscurity alone is not security, but it kills the overwhelming majority of automated scans. Open the port in the firewall before restarting:
sudo ufw allow 2222/tcpCloud hosts (AWS, DigitalOcean, etc.) also need the port opened in their security group — the server firewall alone is not enough.
Step 6: Restart SSH — and the Ubuntu 22.04 socket gotcha
On Ubuntu 22.04+, SSH is often socket-activated via ssh.socket, which listens independently of sshd_config. A plain restart changed nothing for me — the socket kept port 22. Check your mode first:
systemctl status ssh.socketIf it is active and enabled, override the socket to free port 22 before listening on the new one:
sudo systemctl edit ssh.socket[Socket]
ListenStream=
ListenStream=2222sudo systemctl restart ssh.socket
sudo systemctl restart sshThe one rule that saved me
Every SSH change follows the same discipline: the current session stays open until the new login works from a second terminal. Whatever the config says on paper, the old session is the lifeline. Test key login on the new port first — only then close it.
# Test in a NEW terminal, from your machine
ssh -p 2222 user@server_ip- ›Keep the current session open — it is your escape hatch
- ›Test key login on the new port from a different terminal
- ›Only close the old session once the new login is confirmed
- ›Remove the old port from the firewall last:
sudo ufw delete allow 22/tcp
- 01Key pair generated on the local machine
- 02Public key copied to the server with ssh-copy-id
- 03Key-based login confirmed in a second terminal
- 04PasswordAuthentication set to no via drop-in config
- 05SSH port changed in config and socket override
- 06Firewall and cloud security group updated for the new port
- 07New connection tested before closing the old session
Verify the effective config — not what you think you wrote:
sudo sshd -T | grep -Ei "passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication"
sudo ss -tlnp | grep sshdThe first command should report passwordauthentication no, kbdinteractiveauthentication no, pubkeyauthentication yes. The second shows sshd listening on the new port.
| Reality | Before | After |
|---|---|---|
| Auth method | Password typed over the wire | Key-only challenge-response |
| Attacker access | Port 22 bots brute-forcing credentials | Nothing to guess; port 22 closed |
| Login experience | Typed password every time | No prompt — the key proves identity |
| Accountability | Shared or weak passwords | One key per person, revocable |
| Lockout risk | Forgotten password | Lost key — solved with a spare backup key |
This is not SysAdmin paranoia. SSH is the front door to every server — the single most attacked service on the internet. A port-22 server with password auth enabled is a bet that no credential in your fleet is weak. Key-only auth is not more inconvenient; it removes an entire class of password attacks and makes access auditable. Five minutes of setup, one habit of discipline, and it is the highest-ROI security fix I know.
Have servers to lock down? I deploy and harden production infrastructure end-to-end — get in touch and we'll secure your stack.
