Unlocking SSH keys using pass
I use
pass
as my
password manager. It stores passwords and other secrets as
GPG-encrypted files in a Git repository. I host that repo on GitHub
and have clones of it on my work and personal computers. I store
nearly all of my personal passwords in pass
, including
passwords for my GitHub SSH keys. I’ve gone through three ways of
unlocking these SSH keys.
I started by running pass -c SSH/Ubuntu
to copy my SSH
key’s password to the keyboard, running a Git command like
git push
, then pasting my password into the prompt. I
wasn’t satisfied with how much typing this process required. Plus,
sometimes I’d forget to copy the password to my clipboard before
running the Git command. I’d have to Ctrl+C that command and rerun it
after copying the password. Even though I used
ssh-agent
and wasn’t unlocking my SSH key that often, I
decided to automate this process.
My second method used a script I called sshpass
. Here it
is in its entirety:
#!/usr/bin/expect -f
set password [exec pass SSH/Ubuntu]
spawn ssh-add
expect "passphrase"
send -- "$password\r"
interact
This is an expect
script. According to
its man page,
expect
is “a program that ‘talks’ to other interactive
programs according to a script”. In this case, the script instructs
expect
to store the output of
pass SSH/Ubuntu
in the variable password
,
then run ssh-add
, wait for it to prompt for a password,
and write the password to ssh-add
’s stdin. For reasons I
don’t totally understand, the interact
command, which
attaches the terminal’s standard streams to ssh-add
’s, is
necessary to let ssh-add
finish running.
Now, all I had to do was run sshpass
before running a Git
command, but I’d still sometimes forget to run sshpass
.
Finally, this week, I came across a third solution that addresses this
issue on Linux, mainly based on
this blog post.
First, I added AddKeysToAgent yes
to my SSH configuration
at ~/.ssh/config
. This causes ssh
to add
keys to ssh-agent
when they’re unlocked. This is
important because we’ll no longer be explicitly calling
ssh-add
.
Then, I added the following to my .zshrc
:
eval $(ssh-agent) > /dev/null
export SSH_ASKPASS=~/ssh-pass-passphrase.bash
alias ssh="setsid -w ssh"
alias git="setsid -w git"
This starts an instance of ssh-agent
, then sets the
SSH_ASKPASS
environment variable. If this variable is
set, when ssh
needs a password to unlock an SSH key, it
runs the script pointed at by the variable and uses the output as the
password. Note that this only works if ssh
isn’t run in a
terminal. That’s the purpose of the aliases for ssh
and
git
: setsid
runs these programs in a new
shell session that isn’t associated with the terminal in which I’m
running ssh
or git
. The -w
flag
causes setsid
to wait until ssh
or
git
exits before exiting.
~/ssh-pass-passphrase.bash
is a script that writes my SSH
key’s password to stdout:
#!/bin/bash
pass SSH/Ubuntu
Now, I can run Git commands without worrying if my SSH key is unlocked
beforehand. If it isn’t, ssh
automatically calls
pass
to get my SSH key’s password and unlocks the key
before Git runs the command.
So far, this setup only works on Ubuntu on my personal computer. My
next step is to get it working on Mac OS, so I can use it on my work
computer too. After that, I’d like to look into ways to avoid spawning
an instance of ssh-agent
in every terminal I run this
command in.