SSH Configuration
After SSH Key Authentication, a single connection can already require a long command like ssh -i ~/.ssh/id_work [email protected], especially once several servers and several keys are in play. There's also a second, still-open question: which authentication methods the server itself allows, and whether root can log in directly at all. These are two separate concerns — convenience on the client side, and policy on the server side — and they're controlled by two entirely different configuration files.
Two Independent Configuration Files
SSH has two completely separate levels of configuration:
| File | Scope | Controls | |
|---|---|---|---|
| Client | ~/.ssh/config |
per user | which server to connect to, with which parameters |
| Server | /etc/ssh/sshd_config |
system-wide (root) | who can log in to this server, and how |
Keeping these two apart matters: a change in the client configuration only affects how ssh behaves on your own machine; a change in the server configuration applies to everyone connecting to that server.
Client Configuration: ~/.ssh/config
If the file doesn't exist yet, create it:
Each server gets its own Host block:
Host prod-web
HostName 192.168.1.50
User alice
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host staging-db
HostName 10.20.0.15
User deploy
IdentityFile ~/.ssh/id_work
Now a short alias replaces the full command:
This is functionally identical to running ssh -p 2222 -i ~/.ssh/id_ed25519 [email protected].
The Most Common Directives
Host— defines which alias(es) the block applies to; multiple patterns are allowed (for example,Host *.internal);HostName— the real address or domain name;User— the remote username;Port— a port other than the default 22;IdentityFile— which private key file to use;ProxyJump— connect through an intermediate (bastion/jump) server, for example:
This is a common pattern in architectures where an internal network is only reachable through a single, tightly controlled bastion server: running ssh internal-app automatically connects first to bastion-server, then to the internal 10.20.0.30, without doing that two-step manually.
Settings Shared by Every Host
A Host * block applies settings to every server:
ServerAliveInterval 60— the client sends a keep-alive request to the server every 60 seconds; this prevents a long-idle connection from being silently dropped by NAT or a firewall;ServerAliveCountMax 3— how many missed replies are tolerated before the connection is considered dead.
Note
Put the Host * block at the end of the file. OpenSSH reads the configuration top to bottom and takes the first value it finds for each parameter — so if Host * comes earlier, values in more specific blocks that follow it get ignored.
Server Configuration: /etc/ssh/sshd_config
This file controls the behavior of the sshd service and can only be edited with root privileges.
Key Directives
Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers alice deploy
Port— which port the service listens on; choosing a non-default value is covered in more depth in SSH Hardening;PermitRootLogin no— prevents therootuser from logging in directly over SSH; the administrator has to log in as a regular user first and then usesudo— a direct application of the least-privilege principle from Root and sudo;PasswordAuthentication no— allows key-based login only, disabling password attempts entirely;PubkeyAuthentication yes— confirms key authentication is enabled (usually already the default);AllowUsers— only the listed users are allowed to log in over SSH; every other system account — even one with a correct password or key — is rejected outright.
Danger
Always confirm that key authentication genuinely works before turning on PasswordAuthentication no. Getting this wrong risks being locked out of the server entirely — especially on a remote machine, where the only recovery path might be a hosting provider's console or rescue mode.
Warning
On Ubuntu 22.04+ (and most current distributions) the very top of /etc/ssh/sshd_config contains an Include /etc/ssh/sshd_config.d/*.conf line, and cloud images ship a drop-in there such as 50-cloud-init.conf. Because sshd_config takes the first value it finds for each setting and the include sits at the top, a directive in a drop-in file overrides the same directive you edit lower down in the main file. This is the frequent "I set PasswordAuthentication no but passwords still work" trap. Always confirm the effective value with sudo sshd -T | grep -i <directive> (used in the scenario below) rather than trusting the main file alone — and put lasting changes in their own drop-in, e.g. /etc/ssh/sshd_config.d/99-hardening.conf.
Safely Changing the Configuration
After any change to sshd_config, the syntax must be validated before reloading the service:
No output means success. If there's an error, it's reported with the exact line number. Only then reload the service:
reload is preferred over restart — it re-reads the new configuration without dropping existing connections. This matters especially for remote changes: if the new configuration turns out to be broken and the service is fully restarted, you could cut yourself off from the server; reload keeps the current session alive.
Warning
When changing sshd_config on a remote server, test a new connection in a separate terminal, without closing the current SSH session. If the new connection fails, the existing session is still there to undo the change. Editing, closing the terminal, and only then testing is the most common way to lock yourself out of a server.
Practical Scenario: Verifying an Access Plan Before Disabling Root Login
Problem: a new server currently only allows login as root; this needs to move to a safer setup.
Step 1 — check the current effective values:
sshd -T prints all effective settings — both defaults and anything read from the file — which is more reliable than reading the file directly, since some values already differ from their documented defaults on a given install.
Step 2 — set up a regular user and key authentication first: following SSH Key Authentication, authorized_keys is prepared for alice, and login is verified with ssh alice@server in a new terminal.
Step 3 — confirm sudo access:
Step 4 — change the configuration:
Step 5 — reload while keeping the old session open, then test in a new terminal:
In a new terminal:
Login as alice@server continues to work.
Step 6 — result and documentation: confirm root can no longer log in directly, and that alice can still log in with a key and run administrative tasks through sudo. Record the date and reason for the change (for instance, aligning with a security policy).
Common Mistakes
Running restart Directly, Without sshd -t First
Restarting the service with a syntax error in the configuration can leave it unable to start at all — in which case SSH access to the server is gone entirely.
Relying on a Single Open Session for a Risky Change
As noted above, closing the current session before testing a change removes any way to correct a broken configuration.
Writing the Wrong IdentityFile Path in ~/.ssh/config
If the path doesn't exist, or points to the wrong file (a .pub file, for example), ssh silently falls back to trying other default keys — which can be confusing to debug. Use the -v flag to see which key is actually being used.
Forgetting an Existing User When Updating AllowUsers
Replacing the whole AllowUsers value when adding a new user, instead of appending to it, can lock out users who were previously allowed.
Exercises
- Create a
Hostblock in~/.ssh/configfor your lab server (alias,HostName,User,IdentityFile) and connect usingssh <alias>. - From the output of
sudo sshd -T | less, find the values ofPermitRootLogin,PasswordAuthentication, andX11Forwarding, and write down the practical meaning of each. - Repeat the scenario above: set up key authentication for a regular user, and only then enable
PermitRootLogin no. - Deliberately introduce a syntax error into
sshd_config(for example,Port abc), observe howsudo sshd -treports it, then fix it.
Verification criterion: for exercise 3, a direct ssh root@server attempt must return Permission denied, while ssh alice@server must still succeed and sudo -l must list available commands — if either check fails, the change was not applied correctly.