SSH keys and authentication
How key-based login replaces passwords: the key pair, authorized_keys, ssh-agent. Why it is more secure and how to set it up.
How key-based login replaces a password: the key pair, authorized_keys,
ssh-agent. Why it's more secure and how to set it up.
Why use keys if you already have a password
Logging in to a server with a password works, but it scales badly and breaks easily. A password can be brute-forced, it leaks into logs and shell history, and you end up typing it by hand or storing it in plain text in scripts. On a production server, dozens of people and automated systems (CI, Ansible, backups) connect over SSH, and a password quickly turns into a hole.
Key-based authentication solves this. You don't show up at the server with a secret that has to be handed over; you show up with proof that you hold a private key. The private key itself never leaves your machine: the server checks a signature, it never receives the key.
The key pair: private and public
An SSH key is two linked files:
- The private key (
id_ed25519) lives only on your machine, in~/.ssh/. You never give it to anyone or copy it to a server. - The public key (
id_ed25519.pub) can be shared freely. You put it on servers, in GitLab, in GitHub.
The math works so that the public key can verify a signature made by the private one, but you can't reconstruct the private key from the public one. That's why publishing the public half is safe.
Create a pair:
$ ssh-keygen -t ed25519 -C "deploy@laptop"
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):
Enter same passphrase again:
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:3buXJPW3ZuRNZDOU3yOrTa/Xj6WTNh60v8hlMye6u50 deploy@laptop
...
-t ed25519 picks a modern algorithm: short keys, fast, strong. RSA still
works too. Without -b, ssh-keygen makes a 3072-bit RSA key, and many
people take 4096 out of habit (-t rsa -b 4096). -C adds a label
comment, handy later for figuring out whose key this is.
The passphrase encrypts the private key itself. If your laptop gets
stolen, a key with no passphrase can be used right away. With a
passphrase, the key has to be decrypted first. ssh-agent (more on it
below) saves you from typing it on every connection.
authorized_keys: who the server lets in
The server lets in anyone whose public key is listed in the
~/.ssh/authorized_keys file of the target user. One line corresponds to
one allowed key.
The easiest way to put your key on a server is this command:
ssh-copy-id deploy@server
It logs in to the server by password once and appends your
id_ed25519.pub to the ~/.ssh/authorized_keys of the user deploy.
After that, ssh deploy@server lets you in without a password.
On the first connection to a new server, ssh asks whether you trust its
host key (Are you sure you want to continue connecting). After yes,
the server's fingerprint gets written to ~/.ssh/known_hosts on your
machine, and ssh checks it on every login after that.
Permissions on ~/.ssh
Permissions on these files matter a lot. As long as the StrictModes
setting is on (and it's on by default), sshd checks the home directory,
~/.ssh and authorized_keys. If anyone besides the owner can write to
them, or the owner is someone else, sshd ignores that user's keys. The
usual permissions are:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
One thing is strictly required: write access only for the owner. With
644 on authorized_keys the login still works, but with 664 or 777
on ~/.ssh it stops. Another trap: a file created as root belongs to
root. If it has 600 permissions, sshd can't read it on behalf of the
user.
The client checks permissions too, but only on the private key. If
~/.ssh/id_ed25519 is readable by others, ssh refuses to use it:
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
Permissions 0644 for '/home/dev/.ssh/id_ed25519' are too open.
...
This private key will be ignored.
If a key login "silently doesn't work" and ssh asks for a password again,
the first thing to check is the permissions and owner of ~/.ssh. The
client won't show the reason. sshd writes it to its log on the server:
Authentication refused: bad ownership or modes for directory /home/deploy/.ssh
ssh-agent: enter the passphrase once
ssh-agent keeps the decrypted key in memory for the session. You type
the passphrase once when you add the key, and connections after that go
through without asking.
$ eval "$(ssh-agent -s)"
Agent pid 4123
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/user/.ssh/id_ed25519:
Identity added: /home/user/.ssh/id_ed25519 (deploy@laptop)
ssh-add -l shows which keys are currently in the agent.
On a desktop, the system usually already has an agent running. Inside CI you start the agent by hand and feed it a key from a protected variable, so the pipeline connects over SSH without printing the key to the log.
The same key works for git
GitLab and GitHub use exactly the same mechanism. You add your public key
in your profile settings, and git push to a git@... address goes
through without a password. Check the connection:
$ ssh -T git@gitlab.com
Welcome to GitLab, @username!
GitHub answers similarly to ssh -T git@github.com:
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
One key covers both servers and git hosting. You don't need a separate password for each service.
Common mistakes
- Copying the private key to the server instead of
.pub. Only the file ending in.pubshould go to the server. - Putting
authorized_keysthere as root and forgettingchown. The file exists, the key in it is correct, but sshd won't read it. - Running
chmod 777on~/.ssh"just to make it work for sure". The effect is the opposite: sshd stops trusting the directory. - Looking for the cause only in the client's output. The client prints
Permission denied (publickey,...)or asks for the password again, but the real reason shows up in the sshd log. - Turning off
StrictModesto get around the permission check. That fixes the symptom and opens the door for anyone who can write to the directory to swap in their own key.
What to remember
The private key never leaves your machine; only the public key goes to servers and hosting providers.
The server lets in whoever has their public key in
~/.ssh/authorized_keys. Only the owner should be able to write to
~/.ssh and authorized_keys, or sshd will ignore the key. Usual
permissions: 700 on the directory and 600 on the file.
ssh-agent holds the decrypted key so you don't enter the passphrase on
every connection. The same key works for git over SSH too.
The next lesson covers how ~/.ssh/config and a bastion make access to
many servers easier.
What you'll need for the tasks
- Enter a container as a given user:
docker exec -it -u <name> <container> bash, leave withexit. - Create a key:
ssh-keygen -t ed25519 -C <label>. - Put the key on the server:
ssh-copy-id <user>@<server>. - The agent:
eval "$(ssh-agent -s)",ssh-add,ssh-add -l. - Permissions and owner:
ls -la,chmod,chown. - The sshd log in the labs:
docker logs <server-container>.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson linux-security-01-ssh-keys
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress