Sign in
← Linux: security
11 min · medium · Lesson 3 of 5

Hardening the SSH server

How to protect the main entry point: disable passwords and root login, change the defaults, and close off the rest in sshd_config.

How to protect the main entry point: turn off passwords and root login, check that sshd actually applied the settings, and close off the extra exposure in sshd_config.

Why SSH is target number one

SSH is the main door into a server, and it's constantly being pulled at. Any public server catches a nonstop stream of password-guessing attempts over SSH from bots. If password login is open and the password is weak, sooner or later it gets cracked.

Hardening is closing off the most obvious ways in. sshd settings live in /etc/ssh/sshd_config and in files under the /etc/ssh/sshd_config.d/ directory. After an edit, sshd needs to be asked to reread the config.

Turn off password login

The main step: move to keys from the SSH keys lesson and turn off passwords entirely. Then password-guessing is pointless, there's simply no password.

PasswordAuthentication no
PubkeyAuthentication yes

Before turning off passwords, make sure key login already works, or you'll lock yourself out. Keep a second SSH session open while you check the new config.

A second path to a password: keyboard-interactive

sshd has another method, keyboard-interactive. It's enabled by default (KbdInteractiveAuthentication yes). On Debian and Ubuntu sshd runs through PAM (UsePAM yes), and then keyboard-interactive asks for the same password, just through a different route. You turn off PasswordAuthentication but leave this method on, and password login is still possible.

So both get turned off:

PasswordAuthentication no
KbdInteractiveAuthentication no

You can see what methods the server offers right from the client. Deny the key on your side, and the server lists what's left:

$ ssh -o BatchMode=yes -o PubkeyAuthentication=no deploy@server true
deploy@server: Permission denied (publickey,password,keyboard-interactive).

After hardening, only publickey remains in the parentheses.

Why an sshd_config edit sometimes has no effect

On Debian 12 and Ubuntu 22.04 there's a line near the top of /etc/ssh/sshd_config:

Include /etc/ssh/sshd_config.d/*.conf

sshd takes the first value it meets for a setting, not the last. Files from sshd_config.d/ are pulled in right at the start, before the rest of the main config, and cloud-init often puts PasswordAuthentication yes there. Then your edit further down the file is simply ignored, and silently at that.

So first check what sshd actually applied, and what's in the directory:

$ sudo sshd -T | grep -i passwordauth
passwordauthentication yes
$ ls /etc/ssh/sshd_config.d/
50-cloud-init.conf

sshd -T prints the final config after all Includes. If it disagrees with what you wrote, look for the overriding line.

A reliable approach: put your own settings in a separate file in the same directory, but also remove the conflicting line from the cloud-init file. Files are pulled in alphabetically, and 50-cloud-init.conf still comes before your 99-...:

# /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitRootLogin no

After the edit, run sudo sshd -T | grep -i passwordauth again and check the value is now no.

Disable root login

Direct root login is dangerous: it's instant all-powerful access, and the name root is known to every bot. It's better to log in as a regular user and raise privileges with sudo, as in the users lesson.

By default PermitRootLogin prohibit-password is set: root's password isn't accepted, but login by key is allowed. If root's key ever leaked, or someone left it in place "temporarily," it still works. Full denial:

PermitRootLogin no

Now not even the right key gets you in as root directly, and an attacker also has to guess the username. In the sshd log, the refusal shows up as ROOT LOGIN REFUSED.

Apply and verify

After an edit, the config gets checked for errors and the service is asked to reread it:

$ sudo sshd -t            # check the config syntax
$ sudo systemctl reload ssh

sshd -t catches a typo before the restart. Without this check, a broken config brings sshd down, and it stops accepting new connections. For example, a typo in a setting name:

$ sudo sshd -t
/etc/ssh/sshd_config.d/99-hardening.conf: line 5: Bad configuration option: PasswordAuthentcation
/etc/ssh/sshd_config.d/99-hardening.conf: terminating, 1 bad configuration options

What reload does: it sends a SIGHUP signal to the main sshd process. On that signal, sshd restarts itself with the same path and flags and rereads the config. On Debian, the ssh.service unit runs sshd -t once more before that. Without systemd (as in this lesson's containers), the same thing is done by hand, the main process's PID is in the pid file:

kill -HUP "$(cat /var/run/sshd.pid)"

Already-open SSH sessions don't drop: each one lives in its own process. New connections follow the new rules. That's why the spare session saves you: if the new config locks you out, you can undo everything through it.

What else narrows the attack surface

A few settings that close off extra exposure:

All of this works together with the principle of least privilege: exactly what's needed stays open, everything else is closed.

Changing the port and socket activation

Starting with Ubuntu 22.10, sshd comes up through socket activation: the port is listened on not by the service itself but by the ssh.socket unit. A plain reload doesn't help here, a new Port from the config only takes effect after the socket is restarted.

$ systemctl is-enabled ssh.socket   # enabled = socket activation is on
$ sudo systemctl daemon-reload
$ sudo systemctl restart ssh.socket

If ssh.socket is disabled or there's no such unit (Debian 12, Ubuntu 22.04), the port takes effect with a plain sudo systemctl reload ssh. You can always check where sshd actually listens with ss -tlnp | grep sshd.

Common mistakes

What to remember

SSH is target number one for password guessing, so passwords get turned off (PasswordAuthentication no and KbdInteractiveAuthentication no) in favor of keys, and direct root login is denied (PermitRootLogin no), with a regular user plus sudo instead.

An edit is easy to lose: the first value met wins, and Include /etc/ssh/sshd_config.d/*.conf near the top of the file is pulled in first. sshd -T shows the final config.

Before a reload, the config gets checked with sshd -t, and a spare session is kept open so you don't lock yourself out. On top of that, the circle is narrowed (AllowUsers) and attempts are capped (MaxAuthTries, fail2ban).

What you'll need for the tasks

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson linux-security-03-ssh-hardening