1 of 2

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-keygen
  • ssh-copy-id
  • ~/.ssh/authorized_keys
  • ssh-agent
  • ssh-add
  • ssh -v
  • restorecon
  • chmod 700 ~/.ssh

40 min5 exercises

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.

FileWhereTreat it as
~/.ssh/id_ed25519Your machine (the client)Secret. Mode 600. Never copy it anywhere
~/.ssh/id_ed25519.pubAnywherePublic. Safe to share and paste
~/.ssh/authorized_keysThe server, in the account you log in asA list of public keys allowed to log in as that account, one per line
~/.ssh/known_hostsYour machineHost 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:

PathModeOwner
/home/userno group or other write (755 or 750)user
~/.ssh700user
~/.ssh/authorized_keys600 (or 644)user
~/.ssh/id_ed25519 (client)600user

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.

  1. Warm-up

    Create a key pair

    As user alex on node1, 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.pub exist. The private key is id_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
  2. Practice

    Log in to node2 without a password

    Install alex's public key into alex's account on node2 and prove you can run ssh node2 hostname without being asked for a password. Show the line that was added on node2 and its file's permissions.

    Check your result

    ssh node2 hostname prints node2.lab.example immediately. On node2 ~/.ssh/authorized_keys contains the key and has mode 600; ~/.ssh has mode 700.

    Hint

    ssh-copy-id alex@node2 asks for the password once and appends the key. Add -o PreferredAuthentications=publickey when 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'
  3. Practice

    Install a key by hand

    Without ssh-copy-id, install alex's public key for the account dana on node2 by logging in as root there (or sudo) and creating the directory and file with correct owner, mode and SELinux label. Then test ssh dana@node2 using alex's private key on node1.

    Check your result

    ls -ldZ /home/dana/.ssh shows dana dana ... ssh_home_t with mode 700; authorized_keys is mode 600. ssh -i ~/.ssh/id_ed25519 dana@node2 id succeeds without a password.

    Hint

    Create ~dana/.ssh as dana (install -d -m 700 -o dana -g dana), append the key, chmod 600, then restorecon -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
  4. Practice

    A key with a passphrase and an agent

    Create a second key ~/.ssh/id_work with a passphrase, install it for alex on node2, and show that ssh -i ~/.ssh/id_work node2 asks for the passphrase. Then load it into ssh-agent so 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 -l lists the key fingerprint and logins no longer prompt.

    Hint

    eval "$(ssh-agent -s)" starts an agent for this shell, ssh-add ~/.ssh/id_work unlocks 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
  5. Challenge

    The key is ignored: find out why

    On node2 deliberately set chmod 777 ~/.ssh and then chmod 666 ~/.ssh/authorized_keys. Try the login, read the client's ssh -v output 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 -v shows Offering public key then Authentications that can continue; journalctl -u sshd on node2 shows Authentication refused: bad ownership or modes for directory. After chmod 700 / 600 the key works again.

    Hint

    sshd has StrictModes yes by default, which refuses keys when the home directory, .ssh or authorized_keys are 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