/forwagent

Experimental gpg (and ssh) agent forwarding for Windows.

Primary LanguageGo

forwagent - gpg-agent forwarder

Forwagent allows forwarding gpg-agent sockets on Windows for use with GPG and SSH over TCP. For example, allow WSL or VMs to create signatures or SSH using keys stored on a YubiKey, without having access to the YubiKey itself. Tunneled connections use an encrypted transport, with mutual authentication of client and server. The server works on Windows, only. The client has only been tested on Ubuntu, but might work on other distros.

NOTE: This project should be considered experimental, and is provided as-is.

Building

  • Install Go (https://golang.org/dl/)
  • For the server, run go get github.com/Tschrock/forwagent/forwagent-server
  • For the client, run go get github.com/Tschrock/forwagent/forwagent
  • The built files will be in $HOME/go/bin

Quick setup

Both client and server need to have GnuPG installed, and their versions must be close to ensure compatbility of the gpg-agent protocol.

Server setup (Windows)

  • The users private key should be usable from Windows.
  • The gpg-connect-agent.exe executable should be in the users PATH.
  • The gpg-agent.conf should have enable-putty-support.

Run the forwagent-server.exe executable to start the server. On first run, this will generate a server key pair, and display the public portion. This is the servers public key, save it somewhere (it's displayed each time you run the server). By default the server runs on 127.0.0.1:4711, but this can be set by providing an IP:PORT string as an argument to the executable.

You'll most likely want to automate the startup of the server so that it runs each time the computer starts. See the files in doc/ for suggestions on how to do this.

Client setup (Ubuntu)

Run the forwagent binary to start the client. On first run, this will generate a client key pair, and display the public portion. This is the clients public key, which needs to be added to the server to allow connections. By default, the client will attempt to connect to a server on 127.0.0.1:4711, but this can be configured by providing an IP:PORT string as an argument to the executable.

When the client is run, two unix domain socket files are created in ~/.gnupg/, named S.gpg-agent and S.gpg-agent.ssh. These can be used by gpg and ssh, and will be tunneled to the sockets on the server. You'll need to configure SSH to use the socket:

$ export SSH_AUTH_SOCK="~/.gnupg/S.gpg-agent.ssh"

You'll most likely want to automate the startup of the client so that it runs each time the computer starts. See the files in doc/ for suggestions on how to do this.

Authentication (client and server)

With both server and client set up, you should have public keys for each of them. You should also have a directory named .forwagent in the users home directory on both systems.

In the servers .forwagent directory, create a file named clients.allowed and paste the client public key. To allow multiple clients, add multiple keys, one per line.

In the clients .forwagent directory, create a file named servers.allowed and paste the servers public key. To allow connecting to multiple servers (though not more than one at a time), add multiple keys, one per line.

Forwarding over SSH

You can use a reverse tunnel to connect over an ssh connection:

ssh -R 4711:127.0.0.1:4711

This can also be set up in your ssh config:

Host yourserver
    RemoteForward 4711 127.0.0.1:4711

Getting gpg to use the right socket

Unfortunately, gpg is hardcoded to use /run/user/$UID/gnupg/S.gpg-agent for the gpg-agent socket. Fortunately, we can place a text file there to redirect it somewhere else.

Example /run/user/$UID/gnupg/S.gpg-agent file:

%Assuan%
socket=${HOME}/.gnupg/S.gpg-agent

This redirects the gpg-agent socket to ~/.gnupg/S.gpg-agent, which is what forwagent uses by default.

You can also get a bit fancy with this and set up an environmet variable to easily switch between a local and remote ssh agent:

%Assuan%
socket=${HOME}/.gnupg/S.gpg-agent${GPG_SOCKET_SUFFIX}
$ gpg ... # connects to local agent socket
$ GPG_SOCKET_SUFFIX=.remote gpg ... # connects to remote agent socket

Here's a small addition to your .profile that automatically starts the client and sets the environment variable whenever you connect over ssh:

# If we're connection over ssh, set up forwagent for gpg forwarding
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
    # Make sure forwagent is running
    pidof -q forwagent || forwagent -gpgsocket "$HOME/.gnupg/S.gpg-agent.remote" -sshsocket "$HOME/.gnupg/S.gpg-agent.ssh.remote" > /dev/null &
    # Set the gpg socket suffix
    export GPG_SOCKET_SUFFIX=".remote"
    export SSH_AUTH_SOCK="~/.gnupg/S.gpg-agent.ssh.remote"
fi

Usage

Once set up, you should be able to run gpg commands on the client machine, and private keys from the server should be used. Note that the client installation won't have the same public keyring as the server, so you'll need to add your public key to it.

You should also be able to list your GPG authentication key under SSH by running:

$ ssh-add -L

And the ssh command should pick it up and use it automatically.

NOTE: If gpg requires a PIN or passphrase for any action, this will be prompted for on the server, not the client.

Listening on a different interface

By default the loopback interface is used, which works fine for WSL, but may not be suitable for use with VMs. To limit attack surface, I wouldn't recommend listening on a publically facing interface. An alternative is to listen on a virtual interface accessible to both the host and VM.