LinuxXXVI · SSHKeys
SSH keys and known_hosts - the key-based authentication model
What you'll learn
- Generate SSH key pairs with ssh-keygen
- Deploy public keys to authorized_keys
- Manage known_hosts and respond to fingerprint changes
- Recognise insecure key configurations
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
SSH key-based authentication is the secure default. This
lesson covers how keys work, how to generate and deploy them,
and how to manage the trust store (known_hosts).
How SSH keys work
SSH uses asymmetric cryptography. Each user has a keypair:
- Private key (
~/.ssh/id_ed25519): kept secret. Used to sign authentication challenges. - Public key (
~/.ssh/id_ed25519.pub): distributed to servers. Used to verify signatures.
When the client connects:
- Client proves it possesses the private key (via a signature).
- Server verifies the signature using the user’s public
key in
~/.ssh/authorized_keys.
The private key never leaves the client.
Generate a key
ssh-keygen -t ed25519 -C "user@host"
Output:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Your identification has been saved in /home/user/.ssh/id_ed25519
Your public key has been saved in /home/user/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:abc123... user@host
The passphrase protects the private key. A passphrase is mandatory in production.
Key types
| Type | Strength | Notes |
|---|---|---|
ed25519 | Strong, modern | Default in modern OpenSSH; small keys, fast |
rsa | Good with >= 3072 bits | Older; widely compatible |
ecdsa | Has NIST curve concerns | Not recommended |
dsa | Deprecated | Do not use |
Always use ed25519 or rsa with at least 3072 bits.
Deploy the public key
Three methods:
ssh-copy-id:
ssh-copy-id user@server
The simplest. Prompts for the password, copies the public
key to ~/.ssh/authorized_keys, and sets correct
permissions.
Manual:
# On the client
cat ~/.ssh/id_ed25519.pub
# On the server
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "<paste key here>" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Configuration management: Ansible, Puppet, etc. can deploy keys at scale.
Permissions
The SSH server is strict about file permissions:
| File | Required mode |
|---|---|
~/.ssh | 700 |
~/.ssh/authorized_keys | 600 |
~/.ssh/id_* (private) | 600 |
~/.ssh/known_hosts | 644 (read-only OK) |
Wrong permissions cause silent auth failures. Test with:
ssh -v user@server 2>&1 | grep -i permission
known_hosts
After the first connection to a host, the server’s host
key fingerprint is stored in ~/.ssh/known_hosts:
server.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
If the host key changes (server reinstall, MITM), ssh
refuses to connect and prints REMOTE HOST IDENTIFICATION HAS CHANGED.
For automation, use known_hosts with hashed hostnames
(ssh-keyscan -H) to avoid storing plaintext hostnames.
Responding to a host key change
This warning is the only man-in-the-middle defence SSH has. The
common reflex - delete the entry, re-scan, reconnect - throws
that defence away, because ssh-keyscan verifies nothing. It
trusts whatever answers on port 22, which is precisely what an
interceptor is doing. Re-scanning during a maintenance window
that plausibly changed the key is how an attacker gets trusted
permanently.
The rule: obtain the new fingerprint out of band first, then
update known_hosts.
# ON THE SERVER, via console or out-of-band management,
# before touching any client:
for k in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf "$k"; done
# ON EACH CLIENT:
ssh-keygen -R server.example.com # remove the stale entry
ssh-keyscan -t ed25519 server.example.com > /tmp/new_hk
ssh-keygen -lf /tmp/new_hk # compare with the recorded fingerprint
# Only after the fingerprints match, character for character:
cat /tmp/new_hk >> ~/.ssh/known_hosts
If you cannot reach the server out of band to read its fingerprint, you cannot safely re-trust it. Treat the change as an incident until proven otherwise.
SSHFP and DNSSEC
For high-security environments, publish the host key fingerprint in DNS as SSHFP records. SSH can verify against the DNS record:
ssh -o "VerifyHostKeyDNS=yes" user@server
DNSSEC must be valid for this to be trustworthy.
Manage multiple keys
ssh-agent (covered in the next lesson) holds decrypted
private keys. The ~/.ssh/config file selects which key to
use:
Host server1
HostName server1.example.com
User admin
IdentityFile ~/.ssh/server1_key
Host github.com
User git
IdentityFile ~/.ssh/github_key
Host * matches defaults for all hosts.
Insecure key configurations to avoid
- No passphrase: a stolen key file gives the attacker access. Always passphrase-protect.
- Old algorithms: DSA, ECDSA with NIST P-256 (prefer Ed25519).
- Wide-open authorized_keys:
from="*"command="*"is effectively no restriction. Always constrain. - Keys in version control: never commit private keys. Even public keys in repo can leak info about who has access.
- Shared keys per user: every user should have their own keypair. Shared keys destroy accountability.
Knowledge check
Knowledge check · 3 questions
Q1. What is the right ssh-keygen command for a modern key?
Q2. The private SSH key can be safely committed to version control for backup.
Q3. Which of the following are required file permissions for SSH? Select all that apply.
Passing score: 75%. Answers are checked in this browser.