>_ josetilabs.com
← blog

24 Oct 2026 · seguranca, ssh, linux · 4 min

How to harden SSH: what to turn off and what to keep

Most SSH servers ship with password login enabled, root allowed in, and X11 forwarding turned on. Turning off those three and requiring a public key stops most automated attacks. The rest is tuning limits and ciphers.

What to turn off

PasswordAuthentication no removes the most attacked path. Password brute force is what fills the log with attempts. Without passwords, an attacker needs the private key, which changes the game.

PermitRootLogin no blocks direct root login. Even with a key, root cannot get in. Anyone who needs privilege logs in as a normal user and uses sudo.

X11Forwarding no disables the GUI tunnel. Almost nobody uses it, and it is extra attack surface.

AllowTcpForwarding no and AllowAgentForwarding no close TCP tunnels and agent forwarding. If you use a tunnel for a database or an internal service, keep AllowTcpForwarding yes inside a Match block for the user or host that needs it.

ClientAliveInterval 300 with ClientAliveCountMax 2 kills idle sessions after ten minutes. It avoids forgotten open terminals.

What to keep

PubkeyAuthentication yes is what remains. It is the method you want.

AuthenticationMethods publickey says only public keys count. No fallback to password.

UsePAM yes keeps PAM active. Needed for a second factor via PAM, such as Google Authenticator or Duo.

Subsystem sftp stays. Turning it off breaks clients that use SFTP and tools like rsync over SSH.

StrictModes yes checks key file permissions. Already the default, but explicit costs nothing.

LogLevel VERBOSE logs which key was used in each login. It helps audit who connected and when, without exposing sensitive data.

The artifact

Create the file /etc/ssh/sshd_config.d/99-hardening.conf. OpenSSH reads everything in sshd_config.d in alphabetical order, and the first occurrence of a directive wins.

bash
# generate the SSH hardening file (review before applying)
mkdir -p /tmp/ssh-hardening
cat > /tmp/ssh-hardening/99-hardening.conf <<'EOF'
# ssh hardening: what to turn off and what to keep

# authentication
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
PermitEmptyPasswords no
MaxAuthTries 3
LoginGraceTime 30

# session
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
AllowStreamLocalForwarding no
PermitTunnel no
ClientAliveInterval 300
ClientAliveCountMax 2
TCPKeepAlive no

# limits
MaxSessions 4
MaxStartups 10:30:100

# misc
UseDNS no
PrintMotd no
PrintLastLog yes
Banner none

# keep sftp
Subsystem sftp /usr/lib/openssh/sftp-server
EOF
echo "file generated:"
wc -l /tmp/ssh-hardening/99-hardening.conf
echo "--- contents ---"
cat /tmp/ssh-hardening/99-hardening.conf

Apply without dropping the current session:

bash
sudo sshd -t && sudo systemctl reload ssh

sshd -t validates syntax. If it passes, reload applies without closing open connections.

Terminal mostrando a política efetiva antes e depois: permitrootlogin without-password para no, passwordauthentication yes para no, x11forwarding yes para no, maxauthtries 6 para 3
Effective policy before and after, as seen with sshd -T.

How to verify

Confirm the effective policy:

bash
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|authenticationmethods|maxauthtries|logingracetime|x11forwarding|allowtcpforwarding|allowagentforwarding|clientaliveinterval|clientalivecountmax|tcpkeepalive|maxsessions|maxstartups|usedns|subsystem)'

Expected output, with the values you set:

permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
pubkeyauthentication yes
authenticationmethods publickey
maxauthtries 3
logingracetime 30
x11forwarding no
allowtcpforwarding no
allowagentforwarding no
clientaliveinterval 300
clientalivecountmax 2
tcpkeepalive no
maxsessions 4
maxstartups 10:30:100
usedns no
subsystem sftp /usr/lib/openssh/sftp-server

Open another window and test key login before closing the current one.

If it goes wrong

Permission denied (publickey) right after applying. Cause: the public key is not in ~/.ssh/authorized_keys for the user, or permissions are wrong. Fix: chmod 700 ~/.ssh and chmod 600 ~/.ssh/authorized_keys, and confirm the file holds the key. Temporarily bring password back with PasswordAuthentication yes in a Match block for your IP.

sshd -t reports Deprecated option or Bad configuration. Cause: a directive that does not exist in the installed version, such as Protocol 2 on OpenSSH 9.x, which no longer exists. Fix: remove the line; the default is already right.

SFTP stopped working. Cause: the Subsystem sftp line was removed or points to a missing path. Fix: confirm /usr/lib/openssh/sftp-server exists and keep the line.

Before and after

Directive Before (default) After
permitrootlogin without-password no
passwordauthentication yes no
authenticationmethods any publickey
maxauthtries 6 3
logingracetime 120 30
x11forwarding yes no
allowtcpforwarding yes no
allowagentforwarding yes no
clientaliveinterval 0 300
clientalivecountmax 3 2
tcpkeepalive yes no
maxsessions 10 4

FAQ

Do I need to change the SSH port? No. Changing the port reduces log noise, but it is not security. Anyone scanning finds the port in seconds. Keep 22 and invest in keys and fail2ban.

What if I lose the private key? You lose access. That is why you keep a second key somewhere else, or console access through the provider. Do not disable passwords before you have two ways in.

Does it work with ed25519 and rsa keys? Yes. SSH accepts both. Prefer ed25519 for new keys: ssh-keygen -t ed25519.

Does reload drop my session? No. systemctl reload ssh reloads configuration for new connections. Open sessions continue.

Can I restrict by user or IP? Yes. Use Match blocks at the end of the file. Example: Match User deploy followed by AllowTcpForwarding yes allows tunnels only for that user. Order matters: Match applies until the next Match or end of file.

Sources

Official sshd_config documentation: man.openbsd.org/sshd_config.

OpenSSH 9.6, sshd_config manual: https://man.openbsd.org/sshd_config OpenSSH documentation: https://www.openssh.com/manual.html

Internal links: see SSH key instead of password, SSH agent and fail2ban without locking yourself out.

Read next

// By e-mail

Posts published here also go out by e-mail. Delivery has not started yet: whoever leaves an address now joins the first batch.

I store only your e-mail, the date and the record of this consent. No cookies, no trackers, nothing shared with third parties. Details in the privacy policy.