A default SSH install is fine on a LAN. Expose it to the internet and within hours your auth log fills up with brute-force attempts from compromised IoT devices and botnets. Most won’t get in — but they’re noisy, they consume resources, and a single weak password somewhere on the system is all it takes.
These eight sshd_config changes close the brute-force vector entirely. None of them is exotic; all of them are in the default /etc/ssh/sshd_config waiting to be uncommented or changed.
Prerequisite: you’ve already enabled SSH (guide) and set up SSH keys (guide). Don’t disable password auth before keys work, or you’ll lock yourself out.
Tested on: Ubuntu Server 22.04 LTS, 24.04 LTS. Settings work identically on Debian, Raspberry Pi OS, and most modern Linux distributions.

Where the file lives
sudo nano /etc/ssh/sshd_config
After every change, restart sshd:
# Ubuntu 22.04 and earlier
sudo systemctl restart ssh
# Ubuntu 24.04 and 26.04 (any setting except Port or ListenAddress)
sudo systemctl restart ssh.service
On 24.04 and 26.04 SSH is socket-activated: ssh.socket owns the listening port and ssh.service is the daemon that reads the rest of sshd_config. The one exception is the port. Port and ListenAddress are read into the socket by a systemd generator, so they only change after sudo systemctl daemon-reload and sudo systemctl restart ssh.socket (step 6 below has the full sequence).
Test the new config from a SECOND terminal session before disconnecting your existing one. If you’ve broken something (typo, wrong directive, etc.), you can fix it via the existing session before you lock yourself out.
1. Disable password authentication
The single biggest hardening step. With this off, brute-force attempts fail at the protocol level regardless of the password’s strength.
PasswordAuthentication no
Critical: make sure SSH key auth works for every account that needs SSH access BEFORE you enable this. Once it’s set, you can only log in with a key.
Companion: ChallengeResponseAuthentication no and KbdInteractiveAuthentication no — closes the alternate prompt-based methods.
2. Disable root login
Even if root has a strong key, brute-force scanners always try root first. Disabling it removes the most common username from the attack surface.
PermitRootLogin no
If you absolutely need root SSH for automation, use prohibit-password (allows key-only root login) instead of no:
PermitRootLogin prohibit-password
Better: SSH in as a regular user, then sudo for root tasks.
3. Restrict to specific users
Only the users in this list can SSH in. Everyone else is blocked at the protocol level, regardless of password or key.
AllowUsers your-username
For multiple users:
AllowUsers user1 user2 deploy
Or restrict by group (preferred for larger setups):
AllowGroups ssh-users
Then sudo usermod -aG ssh-users user1 to grant access.
4. Reduce MaxAuthTries
Default is 6. Each session, an attacker gets 6 tries before being disconnected. Drop it to 3 — still enough for typo recovery, half the brute-force throughput.
MaxAuthTries 3
5. Set LoginGraceTime to 30 seconds
Default is 120 seconds — the time SSH allows an unauthenticated session to live before kicking it. Two minutes is generous for an attacker holding the connection open while they brute-force in parallel. Drop it.
LoginGraceTime 30
6. Change the port (optional)
Mild security-by-obscurity. Doesn’t stop a determined attacker (any decent scanner finds non-default ports) but cuts most of the noise from drive-by scanners that only try port 22.
Port 28122
Pick something memorable but non-standard. Avoid common alternatives like 222, 2222, 22222: scanners try those too. A number between 1024 and 32767 that nothing else on the box uses is fine (higher numbers fall in Linux’s ephemeral range, which outgoing connections use); 28122 here is only an example.
Do it in this order, so a mistake never leaves you with no way in:
# 1. Check the edited config parses
sudo sshd -t
# 2. Open the new port BEFORE moving sshd to it (keep the port 22 rule for now)
sudo ufw allow 28122/tcp
# 3. Apply the new port
# Ubuntu 22.04:
sudo systemctl restart ssh
# Ubuntu 24.04 and 26.04 (socket activation):
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
# 4. Confirm sshd is listening on the new port
sudo ss -tlnp | grep 28122
Your existing session stays connected through the restart. Now open a SECOND terminal and connect with ssh -p 28122 user@host. Only once that login works, remove the old rule:
sudo ufw delete allow ssh # or: sudo ufw delete allow 22/tcp, whichever you added
If you only restart ssh.service on 24.04 or 26.04, sshd keeps listening on 22. Delete the port 22 rule at that point and your next connection is locked out.
The case against changing the port: it’s not real security, and it adds complexity to every connection. A reasonable take is to leave it on 22 if you’ve done the other hardening properly. Either is defensible.
7. Set idle timeout
Drop connections whose client has gone away (laptop closed, network dropped), so dead sessions don’t sit on the server indefinitely.
ClientAliveInterval 300
ClientAliveCountMax 3
ClientAliveInterval 300 makes sshd send a check through the encrypted channel after 5 minutes with no data from the client; ClientAliveCountMax 3 disconnects after three unanswered checks, so an unresponsive client is dropped after about 15 minutes. Two things older guides get wrong: ClientAliveCountMax 0 does not mean “disconnect immediately”, it disables the disconnect altogether (OpenSSH 8.2 and later, which covers every supported Ubuntu); and these settings never log out an idle user whose SSH client is still running, because the client answers every check. To log out idle interactive shells, set a shell timeout instead, for example readonly TMOUT=900 in a file under /etc/profile.d/ (bash, 15 minutes).
8. Disable port forwarding (if you don’t need it)
SSH supports tunnelling by default. If you’re not using it, turn it off — it’s another attack surface.
AllowTcpForwarding no
AllowAgentForwarding no
GatewayPorts no
If you DO use SSH tunnels (see the SSH Tunneling guide), leave these on.
Optional: 2FA / MFA on SSH
For high-value targets, layer a TOTP code on top of SSH keys. Install libpam-google-authenticator, configure it for your user, and add to sshd_config:
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
Now login requires both your key AND a TOTP code. Overkill for a homelab, mandatory for anything internet-exposed with sensitive data.
Detailed setup: out of scope for this post — search “google authenticator ssh ubuntu” for the canonical guide.
Verify the config before reloading
OpenSSH ships a config tester:
sudo sshd -t
No output means the config is valid. Errors are printed — fix them before reloading.
Then check what sshd will actually enforce, because the file you edited is not the only one read. Ubuntu’s sshd_config starts with Include /etc/ssh/sshd_config.d/*.conf, and for most settings sshd uses the first value it finds, so a drop-in beats your edit. Cloud images often include /etc/ssh/sshd_config.d/50-cloud-init.conf, and if that file says PasswordAuthentication yes it silently undoes step 1:
sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin|kbdinteractiveauthentication|port|maxauthtries'
ls /etc/ssh/sshd_config.d/
If sshd -T still says passwordauthentication yes, fix or remove the drop-in that sets it (or put your settings in an earlier-sorting drop-in such as /etc/ssh/sshd_config.d/00-hardening.conf), then restart and check again.
A complete hardened sshd_config snippet
For reference, the full set of changes from the defaults:
# Authentication
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
# Access control
AllowUsers your-username
MaxAuthTries 3
LoginGraceTime 30
# Connection (see step 6 before changing the port)
Port 28122
ClientAliveInterval 300
ClientAliveCountMax 3
# Forwarding (disable if not used)
AllowTcpForwarding no
AllowAgentForwarding no
GatewayPorts no
Run sudo sshd -t, apply it (on 24.04 and 26.04 a port change needs daemon-reload plus restart ssh.socket, as in step 6), test from a second session, and check sshd -T shows what you expect.
What this does NOT cover
Hardening at the OS layer:
- Fail2ban / SSHGuard — ban IPs that fail repeatedly. Even with key-only auth, this cleans up logs. Covered in Fail2ban for SSH on Ubuntu.
- Firewall rules limiting source IPs — if your only SSH client comes from one office IP or one VPN range, drop everything else at the firewall.
- Mesh VPNs (Tailscale, NetBird, WireGuard) — the safest option. Don’t expose SSH to the internet at all; access via VPN only.
Each of those deserves its own post. Linked from this one as they ship.
Frequently asked questions
How do I undo this if I lock myself out?
If you have physical access (or KVM/console access via your hosting provider), boot the server, edit /etc/ssh/sshd_config from the local terminal, restart sshd. Don’t enable hardening if you don’t have a fallback access path.
Why not just put SSH behind a VPN?
Best practice for production. The hardening here is about making SSH-on-the-internet acceptable. SSH-via-VPN is strictly safer and is what I’d recommend for any serious deployment. Tailscale, NetBird, or WireGuard all work.
Will this break automated tools that SSH in?
Possibly. Anything using password auth will break (intentional). Anything using a key but the wrong username (after AllowUsers) will break. Test your automations after each change.
Should I disable SSH protocol 1?
It’s been disabled by default since OpenSSH 7.0 (2015). Modern Ubuntu doesn’t support it. Don’t worry about it.
Your Homelab Is Your Rehearsal Space
Most of the hardening steps on this page correspond to CIS Benchmark controls for Ubuntu SSH: disabling root login, restricting users, reducing MaxAuthTries and LoginGraceTime, setting client-alive timeouts. These are not homelab preferences. They are the same settings that auditors check on production servers during compliance reviews.
The difference is that in a homelab you can break things, lock yourself out, and recover with physical access to the machine. In production, that mistake costs an outage. Practise here. Get the muscle memory for testing config changes from a second terminal before disconnecting your active session. That habit alone is worth more than any certification.
Companion guides: Enable SSH on Ubuntu Server, SSH Key Authentication on Ubuntu, Fail2ban for SSH on Ubuntu, SSH Tunneling, Common SSH Errors.
The Complete SSH Series
Getting connected
- Enable SSH on Ubuntu Server
- Enable SSH on Raspberry Pi
- SSH Essentials: Remote Administration
- SSH from Windows to Ubuntu
Authentication and hardening
- SSH Key Authentication
- SSH Server Hardening (you are here)
- Fail2ban for SSH
File transfer and tunnelling
Reference
Before you expose any of this: the seven steps that decide whether a home lab survives contact with the internet, in the order they matter, are in How to Secure a Homelab Network in 2026.
RTFM Apparel · printed in the UK

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.





