They Found Me Immediately
Within minutes of the server going live, the auth logs started filling up. Failed login attempts for root, admin, test, and a dozen other default usernames. Automated scanners don't care who you are — they sweep entire IP ranges looking for low-hanging fruit.
Securing SSH was the first thing I did. Every change described here was validated with sshd -t before restarting the service, and I always tested from a new terminal while keeping the existing session alive as a rollback path.
Disable Root Login
PermitRootLogin no
Root is the first username every bot tries. Disabling root login removes the most obvious target. Even if an attacker somehow obtained root's key, SSH would refuse the connection. Administration goes through a regular user who escalates via sudo — with a password required. Two barriers instead of one.
Key-Based Authentication Only
PasswordAuthentication no
Brute-forcing an Ed25519 key is computationally infeasible. Brute-forcing a password is just a matter of time and patience. Disabling password authentication eliminates that entire class of attack. The server holds the public half of the key; my machine holds the private half. During authentication, the server issues a challenge that only the private key can answer. No secret is ever transmitted over the wire.
Whitelist Users
AllowUsers <permitted_user>
This is a whitelist, not a blacklist. Only explicitly listed users can SSH in. Even if an attacker creates a new user through some other vulnerability, they still can't establish an SSH session unless that user appears on this list. This is stronger than just disabling root because it blocks everyone not explicitly permitted.
Move the Port
I moved SSH off the default port. This doesn't provide real security — a full port scan finds the new port in minutes. But it cuts log noise from lazy scanners significantly, which makes actual security events easier to spot in the logs. It's hygiene, not a lock.
Idle Timeout
Inactive sessions disconnect after a few minutes of silence. The server sends keepalive checks at a regular interval, and if enough go unanswered, the session is terminated. This reduces the window for session hijacking on unattended terminals.
Order of Operations
When changing the SSH port, I added the new port to the firewall first, confirmed the connection worked on the new port from a separate terminal, and only then removed the old port.
Doing it the other way around means permanent lockout. You removed the old port, the new port isn't open yet, and your only SSH session just timed out. The server is now unreachable. This isn't hypothetical — it's one of the most common ways people brick their cloud VMs.
The lesson: never remove access before confirming the replacement works.