Does Changing Port 22 Make You Safe?
Thousands of failed logins in a public host's sshd log are almost never aimed at you. Most of it is background noise: scanners walk the address space in order and try one generic dictionary of names, root, admin, ubuntu, postgres. The question worth answering is not how to make those lines disappear, but which kind of attack you are actually facing, and whether the change you are about to make can stop that kind.
A skeleton table first; the sections below fill it in:
| What you face | How it looks in the log | What actually stops it |
|---|---|---|
| Indiscriminate scanning | Usernames are all dictionary words like root, admin, ubuntu; source IPs almost never repeat | Very little. Key-only auth, a quieter log, and a port change to cut noise |
| Reused leaked credentials | A username that really exists on your host; sources clustered in a few IPs or one netblock | Key-only auth. Password complexity will not save you here |
| Someone who already holds a key or a session | Only Accepted publickey, no failures at all | Hardening does not stop this. Key management, auditing and least privilege do |
First, decide what you are actually defending against
The username distribution in your log is the cheapest clue you will get. Words like root, admin, ubuntu, postgres, docker, deploy and git come from a generic dictionary; whoever is knocking does not know you and is merely working through the address space. A real account name from your host, a company name or an internal service name means something else entirely: the other side holds a list that is connected to you, usually harvested from someone else's breach. That is the case worth handling on its own.
One line tells you the shape of it, no need to read the log by hand:
journalctl -u ssh --since -24h | grep -oE 'Failed password for (invalid user )?[^ ]+' | sort | uniq -c | sort -rn | headOne term to fix while we are here: this is not credential stuffing. Stuffing means taking account and password pairs leaked elsewhere and replaying them against another service, betting you reused them. A blind SSH brute force is guessing a generic dictionary and does not even know your username. The two are distinguishable in the log, and the distinguishing feature is the one above: whether the username is really yours.
Changing the port buys you signal, not security
Moving the port keeps out the cheap scripts that only try 22 and 2222. It does not hide the host: scanning the whole IPv4 space across all ports is routine, so "an obscure port means nobody finds me" does not hold. The genuine payoff is day-to-day operations: the log gets quiet enough that an anomaly stands out, which matters most once there are several hosts.
The cost is the part people miss, because a port change is not a file edit but a change that runs through the whole chain: the cloud security group (a layer outside the instance, which the host firewall cannot touch), the host firewall itself, SFTP and backup scripts, deployment jobs in CI, monitoring probes, Git remotes, and the ssh -p in a colleague's bookmark. Miss one and the symptom is simply that you cannot connect, and that failure looks exactly like being blocked by the network or having sshd down.
Treat it as noise reduction. Do not count it as a security boundary.
Keys only: that is the real boundary, and the key is the new attack surface
The difference between password and key auth is not which secret is harder to guess, it is a difference in mechanism. A password is a shared secret that anyone can try online, indefinitely. Key auth has the server issue a challenge, and you pass only if you can sign it with the private key. Guessing is off the table; stealing is not.
That is why key-only auth removes the bulk of the first two threats: a dictionary is useless, and so is a leaked password. What it cannot stop is the third kind, because to the server a person holding your private key is indistinguishable from you.
The trouble merely moves. Key auth shifts the risk from guessing to key custody: copies of the private key scattered across machines, an unencrypted key sitting in CI, someone else's key left on a jump host, agent forwarding that lends your local key to a machine in the middle, and a key generated years ago with an algorithm nobody would pick today. None of that shows up as a failed login, because the attacker walks in through the front door. Where keys live, which are still in use and when they rotate is covered under local encryption and interactive OTP auth.
fail2ban raises the cost of trying, it is not a wall
The mechanism is plain: fail2ban reads the log or journald, matches failed attempts against a rule, and once the count is high enough inserts a ban into the firewall. Three things must hold for it to work at all, the log must be readable, the firewall must be writable, and the addresses it blocks must not include your own people.
What it really does is raise the cost of per-attempt guessing. That mattered most while password auth was still open; with key-only auth it mostly reduces noise. What it does not do is just as clear: low-and-slow distributed attempts, a couple of tries per IP across thousands of sources, pass straight through, and so does anyone knocking with a key.
False bans are more common than missed ones: your own egress IP, your home connection, a corporate CGNAT exit where one address serves a whole building, monitoring probes, jump hosts. Write the whitelist as an answer to "who needs to reach this host", not as "my office IP".
sudo fail2ban-client status sshdFour order rules, and why the order is the point
The content of these four is not a set of commands, it is a sequence. Get the sequence wrong and you lose the whole machine, not one command.
Open the new path before closing the old one. The cloud security group sits outside the instance and is a separate layer from the host firewall. Open the new port in both, prove it with a brand-new connection, and only then close 22. Do it the other way round and the symptom is a host you cannot reach at all, with no sshd error to read.
Prove the key before disabling the password. A reloaded config takes effect immediately. The session you have open keeps working, but it is no guarantee that you can get back in, so a fresh connection must succeed with the key before password auth is switched off.
Trust the effective value, not the line you wrote. This is the one that gets missed. sshd_config uses the first value obtained for a keyword, and the drop-in directory is normally Included at the very top of the file, so a drop-in value beats the line you added further down: you think you changed it and you did not. sshd -T prints the merged result, so read that:
sudo sshd -T | grep -E '^(port|passwordauthentication|permitrootlogin)'A real example from one host: those three lines come back as port 22, passwordauthentication no, permitrootlogin yes. Password login is genuinely off, and root can still walk in with a key. Whether that combination is what you wanted is a judgement you have to make.
Confirm the way back in before you start. Cloud consoles, VNC and serial consoles bypass sshd entirely and do not depend on you being able to connect over SSH. Every hardening step is surgery on the only channel you have, so make sure the other channel works before you pick up the knife.
Who runs those four steps now
These steps are rarely typed by hand any more; usually you describe the goal, and an AI agent writes and runs the change. That moves the risk rather than removing it. A mistyped character is less likely, but whether the order is right, whether the effective value is the one you assumed, and whether you can still get back in after a lockout do not disappear because the agent is doing the typing, and the agent will not pause because it might lock you out.
So what is worth handing over is not a list of commands, it is the order, the checkpoints and the way back:
Harden SSH on this server, in this order, and show me the result of each step before continuing:
1) Open the new port in the cloud security group and the host firewall first, prove a brand-new connection works, and only then allow 22 to be closed;
2) Deploy the key next, prove key login works on a fresh connection, and only then disable password auth, without dropping my current session;
3) Then install fail2ban for SSH failures, with my egress IP and the monitoring probe's egress on the whitelist;
4) For every step show me the merged effective values via sshd -T, and keep a backup of the previous config plus a rollback path.A few things people get wrong
Thousands of failed logins mean I am being targeted? Check the usernames. All dictionary words means indiscriminate scanning; your real account name or an internal service name means the other side holds data connected to you.
An unusual port means scanners cannot find me? Full-port scanning is routine. The payoff is log signal, not invisibility.
With passwords disabled, the log goes quiet? The authentication failures go away, but connections keep arriving and mostly leave connect-and-disconnect records with no verdict. The attack surface moved from guessing passwords to stealing keys.
Installed fail2ban, so it is locked down? It handles repeats from one source. Distributed low-and-slow attempts and anyone with a key are unaffected, while banning your own people is common.
Whitelisted my IP, so it is safe? A whitelist is an availability design, not a security design. Travel, a phone hotspot or a proxy changes your egress, and a whitelisted netblock shared by many people is an open door for strangers.
Hardened once, nothing left to do? What you need is reviewability: what changed, what is in effect now, which keys are still valid, and whether the ban list has caught your own people.
What to leave behind after hardening
Being able to connect is not the same as being able to manage. Afterwards, keep a few things: the config backup and rollback path from before the change; a record of what is in effect now; visibility into bans and whitelists, who got banned, why, and whether it was one of yours; and a key inventory covering which key lives on which machine, whether it is still in use, and when it should rotate.
Do this across several hosts and the hard part stops being how to make the change and becomes which machines have been changed and whether they are still in that state. That kind of cross-checking is what an operator workbench is good at, and it is a different job from the server-side mechanisms. An SSH workbench like Termark gets you onto the host and keeps hosts and keys in one place to compare; the lifecycle that sshd and fail2ban manage is still theirs to manage. Keeping that boundary clear is what tells you whether to look at the connection, the ban list, or the config when something breaks.
References
[1] OpenSSH sshd_config manual
[4] Ubuntu Server documentation: OpenSSH
[5] NIST SP 800-63B: authenticators and passwords
Related reading
- After SSH Disconnects, Is Your Program Still Running? — who owns a task's lifecycle once the connection is gone.
- SSH Tunnels and Jump Hosts — when the connection chain grows, which layer the entry and exit point sit in.
- If you are on servers all day, try Termark — sessions, SFTP and port forwarding in one client.