WSL SSH agent and Git
This article describes how you can say goodbye to the hassle of copying and pasting your SSH passphrase every time you want to connect to a machine via ssh, push/pull from a git repository via ssh or sign a commit/tag/push via ssh regardless of your shell being Powershell, Bash or anything else.
This is useful if you need to be able to use ssh/git fully in Windows and WSL. If you do all your work only in one of these environments, you probably do not need this.
What your current setup probably looks like
If you use both Windows and WSL for ssh/git, you probably either have a passphrase-less ssh key or you have to enter your passphrase every time. Another option is that you have two ssh agents running, one in Windows and one in WSL. This is not ideal, as you have to enter your passphrase twice. You probably also have to enter your passphrase every time you open a new WSL terminal.
Solution
Some solutions I found online get very complex (and usually are unstable). For example people try to map windows pipes to unix sockets or similar. We are going for a much simpler solution in this article.
We are going to setup WSL so that it uses the Windows ssh agent. This way we will have only a single shared ssh agent running on our machine, which will make our life much easier.
Initial setup
Firstly, we need to setup OpenSSH on Windows. You probably already have this working, but if not you can follow these instructions. This ssh agent will be used by both Windows and WSL. Git/ssh in Windows should work out of the box with this setup. In the rest of the article we will be configuring WSL to use the Windows ssh agent.
SSH
To access the Windows ssh agent in WSL, we are going to use the Linux ssh.exe
command. This ssh client directly communicates the Windows ssh agent.
Let’s see if everyting works as expected. If you have your ssh key setup on Github, you can use the following command in WSL:
ssh.exe -T git@github.com # you can use any other ssh server
Cloning/fetching/… with git via ssh
What about git? Git on Linux by default uses the Linux ssh
command to connect
to remote repositories. To make git use the Windows ssh command, use the
following command:
git config --global core.sshCommand ssh.exe
Now you can easily clone/pull/push etc. from git repositories. To test this, clone any repository via ssh in WSL:
git clone git@github.com:octocat/Hello-World.git # you can use any other git repository
Commit/tag/push signatures in Git
If you want to use your ssh key to sign commits/tags/pushes, you need to tell
git in WSL to use the Windows ssh agent again. For that we are going to use the
ssh-add.exe
command. We also need to tell git to use the ssh-keygen.exe
command, which is the counterpart to the ssh-keygen
Linux command. For some
reason, these programs are incompatible and since we are going to use the
ssh-add.exe
command to provide the keys, we also need to use the Windows
variant ssh-keygen to make the signatures.
It might seem like we have everything ready and just need to run a few commands, but it is not that simple. There is one edge case, in which it does not work. Consider the following scenario. You make a git repository in the Windows file system. For some reason you need to open this git repository in WSL and make a signed commit. If you try this, you will get an error:
$ GIT_TRACE=1 git ci -m 'my great message'
15:45:35.924860 git.c:742 trace: exec: git-ci -m 'wsl ssh agent and git'
15:45:35.924930 run-command.c:668 trace: run_command: git-ci -m 'wsl ssh agent and git'
15:45:36.072419 git.c:396 trace: alias expansion: ci => commit
15:45:36.072466 git.c:803 trace: exec: git commit -m 'wsl ssh agent and git'
15:45:36.072495 run-command.c:668 trace: run_command: git commit -m 'wsl ssh agent and git'
15:45:36.091469 git.c:455 trace: built-in: git commit -m 'wsl ssh agent and git'
15:45:36.338328 run-command.c:668 trace: run_command: ssh-add.exe -L
15:45:36.409375 run-command.c:668 trace: run_command: ssh-keygen.exe -Y sign -n git -f /tmp/.git_signing_key_tmpbPSuUD /tmp/.git_signing_buffer_tmpX3z9UD
error: Couldn't load public key /tmp/.git_signing_key_tmpbPSuUD: No such file or directory?
fatal: failed to write commit object
What the hell? Why can our program not see the file? The reason is that since
our working directory is in the Windows file system, the root of the file
system, which is denoted by the backslask symbol /
, links to the root of the
Windows file system, but our Linux git created the temporary files in the Linux
files system. To fix this, we are going to create a simple wrapper shell script,
which is going to translate the paths for us. Since this is a common problem
when working accross different file systems, we have a utility program
wslpath
, which solves this problem. Our shell script is going to look like
this:
ssh-keygen.exe "$1" "$2" "$3" "$4" "$5" "$(wslpath -w "$6")" "$(wslpath -w "$7")"
Save this script as gpgSshProgram.sh
and make it executable:
chmod +x gpgSshProgram.sh
Now we are ready to configure git to use our wrapper script instead of
ssh-keygen.exe
directly. To do this, run the following commands:
git config --global gpg.ssh.defaultKeyCommand "ssh-add.exe -L"
git config --global gpg.ssh.program "/absolute/path/to/gpgSshProgram.sh"
Note that the path must be absolute.
Now you can sign commits/tags/pushes using git in WSL. To try this out, you need to set git to use ssh for making signatures (default is openpgp). To accomplish this, use this command:
git config --global gpg.format ssh
Then you can make a signed commit using this command:
git commit -S
Conclusion
This solution is very simple and makes working with ssh and git super easy. All of this is possible thanks to WSL being awesome and git being highly customizable. The tooling available to developers is amazing, but to figure out the solution described in this article was not easy by any means. I hope this article helped you make your workflow more effecient and pleasant.