SSH Agent
Table of Contents
TL;DR
Store your private SSH key in pass:
pass insert -m ssh/priv < id_ed25519
Enable SSH support in gpg-agent (~/.gnupg/gpg-agent.conf):
enable-ssh-support
Point SSH_AUTH_SOCK at the gpg-agent socket (~/.profile):
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
Auto-load the key when i3 starts (~/.config/i3/config):
exec --no-startup-id bash -c 'pass show ssh/priv | ssh-add -'
Using gpg-agent as ssh-agent
I sometimes find my self in a peculiar situation, where I forget my
YubiKey; yes, I do have a tendency to forget things. But hopefully
some ingenuity will help me in these situations. One thing that tends
to help me is to have a backup plan, another is gpg. I have used
gpg or pass to store my tokens and passwords, and then sent them
(encrypted of course) to github, where I can just clone the repo and
source secrets directly into my shell environment, for example from
.zshrc or .bashrc
export MY_SECRET_TOKEN=$(pass show my/secret)
and by setting ~/.gnupg/gpg-agent.conf to a big enough cache I don't
have to enter the password every time I open a terminal (or when I use any of the other vault secrets).
This has worked quite well I must admit, and at least give some
security as long as I'm not letting any suspect people look into my
environment. Anyhow, since this worked quite well, I was thinking of
doing the same thing for my private key, there is actually no good
reason that this could not be in gpg vault instead?
Let me try to break down this into todo tasks.
- first we need to make sure that the private key is stored inside gpg vault.
- Make sure that the ssh-agent is running.
- add the ssh key using ssh-add
Prerequisite
Before we start we need to have some software available on the system.
- gnupg/gpg-agent
- The GNU Privacy Guard toolkit. Provides gpg for encryption/decryption and gpg-agent which acts as the credential daemon. This is the core of the whole setup.
- pass
- A minimal password manager that stores secrets as GPG-encrypted files in a directory tree. You use it here as the "vault" to store the private SSH key. Syncs via git, so accessible from any machine.
- systemd user session
- The gpg-agent is started and socket-activated via systemctl –user. This is standard on any modern systemd-based distro (Arch, Fedora, Debian 10+, Ubuntu 16.04+), but worth stating explicitly.
- pinentry
- small program to handle password entry.
Adding private key.
lets start by adding the key to the gpg vault , this is pretty straight forward
pass insert -m ssh/priv < id_ed25519
So this should be pretty self explanatory, the only thing that might be somewhat different is the -m which states that its a multiline.
ssh agent
Ssh keys can be passphrase-protected. But typing your passphrase every time could be some what frustrating. So instead the ssh-agent which is small daemon holds you decrypted private key in memory, handles authentication; and foremost, the private key is never exposed to other process.
One thing that keep in mind is that there are several different ssh-agent's, for example
- openssh
ssh-agentsimple, universal- gnupg
gnupggpg users,YubiKey- gcr-ssh-agent
gcrGnome desktop users- kwallet
kwalletKDE desktop users- seahorse
- Gnome GUI key manager
Which is the best one? No idea, I guess that depends on your environment and you usage. For me I'll be sticking to gnupg since I use gpg and YubiKey and it fits my i3 usage.
systemd and gpg-agent
first of we need to start the ssh-agent, i think there are several ways to do this, but i think the best and (maybe) the easiest way is to use systemd.
systemctl --user cat gpg-agent
# /usr/lib/systemd/user/gpg-agent.service [Unit] Description=GnuPG cryptographic agent and passphrase cache Documentation=man:gpg-agent(1) Requires=gpg-agent.socket [Service] ExecStart=/usr/bin/gpg-agent --supervised ExecReload=/usr/bin/gpgconf --reload gpg-agent
supervised just tells it to run in the foreground and all errors to
stderr, this means it will show up in the journalctl -u gpg-agent in
case of errors. This has obviously a socket connected to it:
systemctl --user cat gpg-agent.socket
# /usr/lib/systemd/user/gpg-agent.socket [Unit] Description=GnuPG cryptographic agent and passphrase cache Documentation=man:gpg-agent(1) [Socket] ListenStream=%t/gnupg/S.gpg-agent FileDescriptorName=std SocketMode=0600 DirectoryMode=0700
The important part is where the socket is
created. ListenStream=%t/gnupg/S.gpg-agent
Ok, this part should now be considered done ✅
gpg-agent ssh enable
Im not sure this is actually needed, but to be extra cautious we add
following line to the ~/.gnupg/gpg-agent.conf
echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf
As I mentioned, not sure its needed since we already enabled the systemd socket for ssh, but it creates a socket for auth.
ls -la | grep .ssh
Adding environment
We have most of it setup, the problem is that we need to add the
SSH_AUTH_SOCK to the ssh socket was created with gpg-agent.
In my case, since I'm using i3, I don't get anything setup
automatically (which is how I want it). But I want the variable set at
every login; that means for example if I use terminal to login. So
setting it only in i3 config won't do it. I could settle with .zshrc
but then again, this would mean it won't be set in emacs if I start
emacs without going through a console.
This is what .profile or .zprofile or even .xprofile now which
one should i use?
The idea is that .?profile files are read before anything is set,
right after we start loading the windowmanager , in that way ,
everything should follow.
But which one should i choose?
Good question, i settled with adding:
source ~/.profile
in all the files (except .profile) and in .profile i added.
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
So after a logout sequence i get this:
ssh-add -l
2048 SHA256:HiIjSZBhxBLqHNYhOBNR3b5foPQOwsfO6fqG7Yl0MG8 cardno:32_234_712 (RSA)
This is my YubiKey, so it seems as if this works..
But we won't be using this for now.
Adding key to agent
We already have the key in the gpg vault. We now want to automatically add this key to the gpg-agent. its quite simple idea.
pass show familjen/calle/ssh/spock| ssh-add -
Identity added: (stdin) (olsen.carl@gmail.com)
It now asks for a password, through pinentry which can be configured
in the ~/.gnupg/gpg-agent.conf
by adding for example
echo "pinentry-program /usr/bin/pinentry-qt5" >> ~/.gnupg/gpg-agent.conf
Or some other pinentry program.
and then
gpg-connect-agent reloadagent /bye
OK
Which reloads the configuration
Now back to the problem, we want to add the ssh key to the agent.
We cant add it to the .*profile since we won't be able to use the
pinentry, we can set it in .zshrc , but that means we need to open
a terminal before anything else, and that I will forget.
So the 3rd alternative is to add a line in i3 config.
exec --no-startup-id bash -c 'pass show familjen/calle/ssh/spock | ssh-add -'
Now, this makes it work, the only problem is that I need to type in a password as soon as I login, but thats reasonable.
Lets see if it worked after a reboot.
Verification after reboot
Login and then password
ssh-add -l
2048 SHA256:HiIjSZBhxBLqHNYhOBNR3b5foPQOwsfO6fqG7Yl0MG8 cardno:32_234_712 (RSA) 256 SHA256:LQBlj7brI5NIdiHlMPtlh3P2BYzoCvxs2/jHtABAGnw olsen.carl@gmail.com (ED25519)
Voilá! It worked ✅
Summary
My task was to store the private ssh key into a gpg vault.
This could then be retrieved and added to a ssh-agent ,
in my case I used gpg-agent since that kind of fits into the rest of
the system. The nice thing about this setup is that I can retrieve the
private key from any system as long as I have an Internet connection (the
vault is stored in a remote repo). By calling pass from the i3 startup config
, I could add the private key to the ssh-agent and it will be
used automatically every time I use ssh.