On this page

The guide SSH — From Passwords to Keys secured your side of the connection: keys instead of passwords, an agent, nicknames for the machines you reach. This guide is about the other end — the machine that accepts connections. A rented server or a Raspberry Pi exposed to the internet is asked to prove its identity thousands of times a day by automated scripts guessing passwords. Hardening is how you stop answering.

Everything here was verified on disposable Ubuntu 26.04, Fedora 44 and Arch containers running OpenSSH 10.2 through 10.5, from the inside. Nothing in this guide needs anything beyond the machine you are hardening and the client you are reading this on.

The one rule before any change

SSH has no password reset email. If you break the SSH server remotely, the way back in is whatever console your provider gives you — assume it is slow and annoying. On a Raspberry Pi, the fallback is a keyboard and a screen instead. So every change in this guide follows the same ritual:

  1. Change the configuration.
  2. Validate it with sshd -t before restarting anything.
  3. Restart the SSH service.
  4. Prove the new setup works by logging in from a new terminal, while the old session stays open.
  5. Only then close the old session.

The reason for step 4 is that an already-established session survives a restart. This was tested directly: a session was left running a twelve-second command, the service was restarted underneath it, and the command finished with its output intact. Your current connection is your safety rope. Keep holding it until the new way in is proven.

And if the step-4 login fails, the move is backward, not forward: undo the edit, sshd -t, restart, and confirm the old behavior is back — all from the session that still works. Find the cause, then try again. The user-restriction section below is the classic place this saves you: one misspelled username there refuses even key-holders.

Where the configuration lives

Server settings live in /etc/ssh/sshd_config, a plain text file. Edit it with sudo nano /etc/ssh/sshd_config. The file is mostly comments; the lines that matter start with a keyword and a value.

After any edit, validate before restarting:

$ sudo sshd -t

Silence and exit code zero mean the file parses. A typo produces an error naming the line:

/etc/ssh/sshd_config: line 146: Bad configuration option: Portr
/etc/ssh/sshd_config: terminating, 1 bad configuration options

That output came from adding Portr 22 — one extra letter. Note what the validation just prevented: a server that refuses to start. To see the configuration the server would actually run, not just check the syntax:

$ sudo sshd -T | grep -i passwordauthentication
passwordauthentication yes

If that test ever disagrees with your edit (it says yes after you set no), another file is winning. The server uses the first value it reads, and the main file can pull in extra files from /etc/ssh/sshd_config.d/ near the top. Look for an Include line in /etc/ssh/sshd_config and check that directory.

One naming note: the service is called ssh on Ubuntu and sshd on Fedora and Arch, so the restart command differs. On Arch the server is not preinstalled — sudo pacman -S openssh installs it; Fedora ships it in the base system, and Ubuntu handles it at install time rather than through its metapackages:

$ sudo systemctl restart ssh      # Ubuntu
$ sudo systemctl restart sshd     # Fedora, Arch

The configuration directives behave identically on both. What differs is the service name, and one Ubuntu-specific quirk you will meet in the port section.

Keys only — the main event

If you followed the client guide, your key is already on the server (ssh-copy-id put it there), and key login works. Verify that with your own eyes before going further: open a new terminal, log in with the key, no password. If that login fails, stop here and fix it first.

Open /etc/ssh/sshd_config and set:

PasswordAuthentication no

Then validate, restart, and test — in that order. With password authentication off, a password attempt is refused before any password is asked for, and the client shows it:

sshguide@localhost: Permission denied (publickey).

The word publickey in parentheses is the complete list of login methods the server still offers — before the change, the same attempt showed (publickey,password). And the server does not explain why a login was refused, to you or to anyone guessing. That silence is a feature.

While you are in the file, look at root. The default setting, PermitRootLogin prohibit-password, already blocks root password logins. Making it explicit closes the question permanently:

PermitRootLogin no

With no, root cannot log in over SSH at all — you log in as yourself and use sudo for admin work, exactly as Chapter 5 taught. On a localhost test rig, root password attempts were rejected under both settings, with the same message either way.

Restricting who can log in

AllowUsers turns the login policy into an explicit list. Everyone not on it is refused, whatever credentials they offer:

AllowUsers yourname

Restart, and any account not on the list is refused whatever it offers, while the allowed account keeps working. If several people maintain the machine, list them all separated by spaces, or use AllowGroups with a group (Chapter 5 showed how groups work).

The effect is small but real: accounts you never created cannot be logged into, and a script guessing admin or root passwords is wasting its time on a server that stopped listening to those names.

The port change — what it is and what it is not

Every exposed SSH server on the internet is probed on port 22 by automated scripts around the clock. Moving the SSH port makes that background noise stop. It does not make you safer, and the difference matters: a port scan sweeps all 65,535 ports in short order, so moving the port hides you from lazy scripts, not from anyone who wants in. Moving the port is log hygiene. Keys are the security. Never let a moved port be the reason key-only auth “can wait”.

With that said, if the noise bothers you, the change is one line:

Port 2222

After a restart, the server answers on 2222 and refuses connections on 22 (-p tells the client which port to knock on):

$ ssh -p 2222 [email protected]
ssh: connect to host localhost port 22: Connection refused

Two findings from live testing that most port-change instructions skip. First, listing two Port lines makes the server listen on both ports — adding Port 2222 while Port 22 remains does not close anything. Delete the old port line if you want it closed.

Second, and more important: on current Ubuntu releases, ssh runs behind systemd socket activation, and a service called ssh.socket holds port 22 open. Editing sshd_config and restarting does nothing until you switch to the plain service:

$ sudo systemctl disable --now ssh.socket
$ sudo systemctl enable --now ssh.service

After that, Port lines behave as documented. Fedora and Arch ship no socket layer, and the port change worked as described in our testing on both. If you move the port, the client has to follow: use ssh -p 2222, or put a Port 2222 line under the server’s entry in ~/.ssh/config (the client guide’s nicknames).

Reading your logs

Every connection attempt, successful or not, lands in the journal:

$ sudo journalctl -u ssh        # Ubuntu
$ sudo journalctl -u sshd       # Fedora

Failed key attempts look like this:

Connection closed by authenticating user sshguide ::1 port 52148 [preauth]

Successful restarts announce themselves too:

sshd[2246]: Server listening on 0.0.0.0 port 2222.

The listening line doubles as proof that a port change took effect. Scrolling this journal after a week online explains, better than any article, why keys beat passwords: the volume of guesses against a real server is constant and enormous.

What this guide does not cover

A firewall limits which ports are reachable before SSH is even asked — Chapter 10 touched on the landscape, and the setup differs by distro (ufw on Ubuntu, firewalld on Fedora). Automated blocking of misbehaving addresses is its own topic (fail2ban, CrowdSec), and it can wait: with key-only logins enabled, password guessing already fails at the door.

The hardened configuration, complete

For a single-admin server, the hardening file is short:

# /etc/ssh/sshd_config — the lines that matter
Port 22
PasswordAuthentication no
PermitRootLogin no
AllowUsers yourname

Validate, restart, and test from a new terminal:

$ sudo sshd -t
$ sudo systemctl restart ssh

That is the whole hardening file for most personal servers. The port line stays at 22 unless log noise bothers you; keys do the protecting.

Where you are now

Your server now answers only to named users carrying keys, refuses root logins outright, and writes down everyone who knocks. Combined with the client setup from the SSH guide, both ends of the connection are yours. The Automation chapter shows how cron turns maintenance into scheduled work, and the full sshd_config manual documents every directive this guide left out.