sshd_config shell

your SSH server is open to the world. let's fix that.

You’ve been using SSH to connect to servers. Good. But the server side — the SSH daemon — is running with default settings. Default settings that allow password authentication, permit root login, and listen on port 22 where every bot on the internet is already knocking.

Your /var/log/auth.log is full of failed login attempts. Thousands of them. Every day. Bots from every corner of the internet trying root, admin, test, ubuntu, pi — every common username with every common password. Your server is rejecting them, but it’s spending resources doing it. And if someone on your team has a weak password, it’s only a matter of time.

sshd_config is the SSH server’s configuration file. Every setting that controls who can connect, how they authenticate, and what they’re allowed to do lives here. A few changes turn your SSH server from “default and hoping for the best” to “hardened and letting in only what you explicitly allow.”

Unless you’re running Windows then wtf none of this applies to you. But hey, come to the dark side, go install WSL2 and you can follow along. We’ll wait. Impatiently.

If you’re lazy like me (all sysadmins are!) then click here for the sshd_config cheat sheet.


Where is it

/etc/ssh/sshd_config

Edit with sudo:

sudo nano /etc/ssh/sshd_config

After making changes, test the config and restart:

sudo sshd -t                        # Test for syntax errors
sudo systemctl restart sshd         # Apply changes

Always test before restarting. A broken sshd_config means you can’t SSH back in after a restart. If you’re remote, that means you’re locked out.

Pro tip: Keep an existing SSH session open while you restart. If the new config is broken, your existing session stays connected and you can fix it.


Disable password authentication

PasswordAuthentication no

This is the single most impactful hardening change. Disabling passwords means the only way in is with an SSH key. Those thousands of brute-force bots in your auth log? They’re all trying passwords. With this setting, every single one of them fails instantly — they can’t even attempt a password.

Before enabling this, make sure:

  1. You have an SSH key pair set up
  2. Your public key is in ~/.ssh/authorized_keys on the server
  3. You’ve tested that key-based login works

If you disable passwords before setting up keys, you lock yourself out. Don’t do that.

Also disable challenge-response

ChallengeResponseAuthentication no
KbdInteractiveAuthentication no

Some systems use challenge-response as a backdoor to password auth. Disable both to be sure.


Disable root login

PermitRootLogin no

Nobody should SSH in as root. Log in as your user, then sudo what you need. This way, the audit log shows who actually logged in — “owner ran sudo rm” is more useful than “root ran rm.”

If you need root for automated processes (like rsync backups):

PermitRootLogin prohibit-password

Allows root login only with SSH keys, never with a password. A reasonable middle ground.


Change the SSH port

Port 2222

Default is 22. Moving to a non-standard port doesn’t add real security (security through obscurity), but it eliminates 99% of automated brute-force noise because bots overwhelmingly target port 22.

Your auth log goes from thousands of failed attempts per day to near zero. It’s not security — it’s noise reduction.

After changing, connect with:

ssh -p 2222 user@server

Or add it to your ~/.ssh/config so you don’t have to remember:

Host myserver
    HostName server.example.com
    Port 2222

Restrict which users can log in

AllowUsers owner deploy

Only owner and deploy can SSH in. Everyone else is rejected immediately, even with valid credentials. This is a whitelist — explicit is better than implicit.

Restrict by group

AllowGroups ssh-users

Only members of the ssh-users group can connect. Add users to the group:

sudo usermod -aG ssh-users owner

Limit authentication attempts

MaxAuthTries 3

Disconnect after 3 failed authentication attempts. Default is 6. Reduces the window for brute-force attacks per connection.


Set idle timeout

ClientAliveInterval 300
ClientAliveCountMax 2

Sends a keepalive every 300 seconds (5 minutes). If 2 keepalives get no response, the session is disconnected. Total timeout: 10 minutes of inactivity.

Prevents ghost sessions from accumulating on the server — users who closed their laptop without disconnecting.


Disable unnecessary features

X11Forwarding no

Unless you’re forwarding GUI applications over SSH (rare), turn this off. Reduces attack surface.

AllowTcpForwarding no

Disables SSH tunneling and port forwarding. Only set this if your users don’t need tunneling — some legitimate use cases depend on it.

PermitEmptyPasswords no

Should already be the default, but make it explicit. Accounts with empty passwords cannot SSH in.


Use SSH protocol 2 only

Protocol 2

On modern systems, this is already the default and the option may not even exist in the config. Protocol 1 has known vulnerabilities. If your system still supports it, force protocol 2.


A hardened sshd_config template

# Listening
Port 2222
Protocol 2
AddressFamily inet

# Authentication
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
MaxAuthTries 3
PubkeyAuthentication yes
AuthenticationMethods publickey

# Access control
AllowUsers owner deploy

# Session
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
PermitEmptyPasswords no

# Logging
LogLevel VERBOSE

LogLevel VERBOSE logs more detail about authentication — useful for security auditing and investigating unauthorized access attempts.


The settings that actually matter

Setting Recommended Why
PasswordAuthentication no Force key-based auth. Stops brute-force.
PermitRootLogin no Audit trail. Principle of least privilege.
Port Non-standard Noise reduction (not real security).
AllowUsers Explicit list Whitelist > blacklist.
MaxAuthTries 3 Limit brute-force window.
ClientAliveInterval 300 Clean up idle sessions.
X11Forwarding no Reduce attack surface.
LogLevel VERBOSE Better audit logs.

“But I—”

No.

“Password auth is fine, I use strong passwords.” Your password is strong. Is everyone else’s on the server? SSH keys are mathematically stronger than any password and can’t be brute-forced over the network. One weak password on one account compromises the server. Key-only auth eliminates this class of attack entirely.

“Fail2ban handles brute-force.” Fail2ban is great — it bans IPs after too many failed attempts. But it’s reactive. Disabling password auth is proactive. Why ban attackers after they’ve tried 5 passwords when you can make passwords impossible? Use both, but disabling passwords is the priority.

“Changing the port is security through obscurity.” Correct. It’s not real security. But it drops your auth log noise from 10,000 entries per day to near zero, making the log actually useful for spotting real threats. It’s not a security measure — it’s a signal-to-noise optimization.

“I need password auth for some users.” Then use Match blocks:

Match User deploy
    PasswordAuthentication no
Match User contractor
    PasswordAuthentication yes
    MaxAuthTries 2

Per-user settings. The contractor gets password auth with strict limits. Everyone else uses keys. SSH is flexible enough to handle this.


sshd_config cheat sheet

You made it. Or you skipped straight here. Either way, no judgment. Copy and paste these. Pin them. Tattoo them on your forearm. Whatever works.

What you’re doing Setting
Disable password auth PasswordAuthentication no
Disable root login PermitRootLogin no
Change port Port 2222
Restrict users AllowUsers owner deploy
Limit auth attempts MaxAuthTries 3
Idle timeout (10 min) ClientAliveInterval 300 + ClientAliveCountMax 2
Disable X11 forwarding X11Forwarding no
Verbose logging LogLevel VERBOSE
Test config sudo sshd -t
Restart SSH sudo systemctl restart sshd

The three changes that matter most: PasswordAuthentication no, PermitRootLogin no, and AllowUsers yourname. Everything else is refinement.

Back to the top, you overachiever.