SSH Key AuthenticationLesson 1 of 2
Key-based SSH login
Generate a key pair, install the public key on another system, log in without a password, and debug it when it refuses.
ssh-keygenssh-copy-id~/.ssh/authorized_keysssh-agentssh-addssh -vrestoreconchmod 700 ~/.ssh
After this lesson you can
- Explain what the private and public key are and which one goes where.
- Create an Ed25519 key pair, copy the public key to a remote account, and log in with it.
- Check ownership, permissions and SELinux labels when a key is ignored, and read ssh -v and the server log.
The idea
With password login, the secret travels to the server every time and can be guessed or intercepted. With key authentication you hold a private key that never leaves your machine, and the server holds the matching public key. To log in the client proves it has the private key, with no secret sent over the wire.
| File | Where | Treat it as |
|---|---|---|
~/.ssh/id_ed25519 | Your machine (the client) | Secret. Mode 600. Never copy it anywhere |
~/.ssh/id_ed25519.pub | Anywhere | Public. Safe to share and paste |
~/.ssh/authorized_keys | The server, in the account you log in as | A list of public keys allowed to log in as that account, one per line |
~/.ssh/known_hosts | Your machine | Host keys of servers you have connected to |
Generate with ssh-keygen -t ed25519, the modern default (compact and secure; -t rsa -b 4096 also works). You are asked where to save it (accept the default) and for a passphrase that encrypts the private key on disk. A passphrase protects a stolen key; an empty one (-N '') allows fully unattended logins, which is what automation and the exam typically use.
Installing the public key
ssh-copy-id user@host logs in once with the password, creates ~/.ssh and authorized_keys with the right modes, and appends your key. The manual equivalent on the server:
install -d -m 700 ~/.ssh
cat id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
restorecon -Rv ~/.ssh
Why a key gets ignored
sshd checks, by default (StrictModes yes), that the user’s home directory, .ssh and authorized_keys are owned by the user and not writable by others. If not, it silently skips the key and falls back to the password prompt. The recipe that works:
| Path | Mode | Owner |
|---|---|---|
/home/user | no group or other write (755 or 750) | user |
~/.ssh | 700 | user |
~/.ssh/authorized_keys | 600 (or 644) | user |
~/.ssh/id_ed25519 (client) | 600 | user |
Also: SELinux requires the ssh_home_t label on ~/.ssh contents; files copied or created in odd ways may have the wrong one, fixed by restorecon -Rv ~/.ssh.
Try it
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
ls -l ~/.ssh
Generating public/private ed25519 key pair.
Your identification has been saved in /home/alex/.ssh/id_ed25519
Your public key has been saved in /home/alex/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:q3xN0b1Y... alex@node1.lab.example
-rw-------. 1 alex alex 399 Nov 3 14:21 id_ed25519
-rw-r--r--. 1 alex alex 99 Nov 3 14:21 id_ed25519.pub
ssh-copy-id alex@node2
ssh -o PreferredAuthentications=publickey node2 hostname
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'alex@node2'"
and check to make sure that only the key(s) you wanted were added.
node2.lab.example
PreferredAuthentications=publickey forbids password fallback, so success proves the key works rather than a typed password.
Look at the result on the server
ls -ld ~/.ssh ~/.ssh/authorized_keys
cat ~/.ssh/authorized_keys
drwx------. 2 alex alex 29 Nov 3 14:22 /home/alex/.ssh
-rw-------. 1 alex alex 99 Nov 3 14:22 /home/alex/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... alex@node1.lab.example
The last field is a comment (the user@host that created the key). Anyone holding the matching private key can log in as alex on node2, so adding a line to authorized_keys is granting access: treat that file as sensitive.
A passphrase and the agent
ssh-keygen -t ed25519 -f ~/.ssh/id_work
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_work
ssh-add -l
Agent pid 5521
Identity added: /home/alex/.ssh/id_work (alex@node1.lab.example)
256 SHA256:Zk9... alex@node1.lab.example (ED25519)
Diagnosing a refusal
ssh -v node2 true 2>&1 | grep -E 'Offering|Authentications|Permission|Authenticated'
debug1: Offering public key: /home/alex/.ssh/id_ed25519 ED25519 SHA256:q3xN...
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
The server rejected the key (it offers password next). Look at the server’s side of the story:
sudo journalctl -u sshd -n 4 --no-pager
Nov 03 14:30:02 node2 sshd[7712]: Authentication refused: bad ownership or modes for directory /home/alex/.ssh
Going further
ssh-copy-id -i ~/.ssh/id_work.pub installs a specific key. Per-host settings go in ~/.ssh/config: Host node2 / User alex / IdentityFile ~/.ssh/id_work, and keep that file mode 600. In authorized_keys a key can be restricted with options in front of it: from="192.168.122.0/24",command="/usr/bin/rsync --server ...",no-pty ssh-ed25519 AAAA.... ssh-keygen -p changes a passphrase, ssh-keygen -y -f key regenerates the public key from a private one, and ssh-keyscan node2 fetches host keys for known_hosts. Revoking access means deleting the line from authorized_keys: no password reset is needed.
Practice
0 of 5 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Create a key pair
As user
alexonnode1, create an Ed25519 key pair with an empty passphrase (for the exam style task) in the default location, and list the two files created. Say which one must never leave the machine.Check your result
~/.ssh/id_ed25519(mode 600) and~/.ssh/id_ed25519.pubexist. The private key isid_ed25519.Hint
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519.-N ''sets an empty passphrase.Solution
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519 ls -l ~/.ssh ssh-keygen -lf ~/.ssh/id_ed25519.pub - Practice
Log in to node2 without a password
Install alex's public key into alex's account on
node2and prove you can runssh node2 hostnamewithout being asked for a password. Show the line that was added on node2 and its file's permissions.Check your result
ssh node2 hostnameprintsnode2.lab.exampleimmediately. On node2~/.ssh/authorized_keyscontains the key and has mode600;~/.sshhas mode700.Hint
ssh-copy-id alex@node2asks for the password once and appends the key. Add-o PreferredAuthentications=publickeywhen testing to prove the key is used.Solution
ssh-copy-id alex@node2 ssh -o PreferredAuthentications=publickey node2 hostname ssh node2 'ls -ld ~/.ssh; ls -l ~/.ssh/authorized_keys; cat ~/.ssh/authorized_keys' - Practice
Install a key by hand
Without
ssh-copy-id, install alex's public key for the accountdanaon node2 by logging in as root there (orsudo) and creating the directory and file with correct owner, mode and SELinux label. Then testssh dana@node2using alex's private key on node1.Check your result
ls -ldZ /home/dana/.sshshowsdana dana ... ssh_home_twith mode700;authorized_keysis mode600.ssh -i ~/.ssh/id_ed25519 dana@node2 idsucceeds without a password.Hint
Create
~dana/.sshas dana (install -d -m 700 -o dana -g dana), append the key,chmod 600, thenrestorecon -Rv /home/dana/.ssh.Solution
# on node2, as root sudo install -d -m 700 -o dana -g dana /home/dana/.ssh echo 'PASTE-THE-PUBLIC-KEY-LINE' | sudo tee -a /home/dana/.ssh/authorized_keys sudo chown dana:dana /home/dana/.ssh/authorized_keys sudo chmod 600 /home/dana/.ssh/authorized_keys sudo restorecon -Rv /home/dana/.ssh # on node1, as alex ssh -i ~/.ssh/id_ed25519 dana@node2 id - Practice
A key with a passphrase and an agent
Create a second key
~/.ssh/id_workwith a passphrase, install it for alex on node2, and show thatssh -i ~/.ssh/id_work node2asks for the passphrase. Then load it intossh-agentso you are asked only once, and list the loaded keys.Check your result
Without the agent the passphrase prompt appears each time. After
ssh-add,ssh-add -llists the key fingerprint and logins no longer prompt.Hint
eval "$(ssh-agent -s)"starts an agent for this shell,ssh-add ~/.ssh/id_workunlocks the key into it.Solution
ssh-keygen -t ed25519 -f ~/.ssh/id_work ssh-copy-id -i ~/.ssh/id_work.pub alex@node2 ssh -i ~/.ssh/id_work node2 hostname eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_work ssh-add -l ssh -i ~/.ssh/id_work node2 hostname - Challenge
The key is ignored: find out why
On node2 deliberately set
chmod 777 ~/.sshand thenchmod 666 ~/.ssh/authorized_keys. Try the login, read the client'sssh -voutput and the server's log to find the reason, then repair it. Name three other causes of the same symptom (wrong owner, SELinux label, home directory writable by others).Check your result
With loose modes the key is skipped and the password prompt appears.
ssh -vshowsOffering public keythenAuthentications that can continue;journalctl -u sshdon node2 showsAuthentication refused: bad ownership or modes for directory. Afterchmod 700/600the key works again.Hint
sshd has
StrictModes yesby default, which refuses keys when the home directory,.sshorauthorized_keysare writable by others.Solution
# on node2 chmod 777 ~/.ssh; chmod 666 ~/.ssh/authorized_keys # on node1 ssh -v node2 true 2>&1 | grep -E 'Offering|Authentications|Permission' # on node2 sudo journalctl -u sshd -n 5 --no-pager chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys restorecon -Rv ~/.ssh