#g33kr_

[EAtT] SSH key authentication with GPG


If you've read my article One GPG key pair (to rule them all), you'll recall that we created a primary ("master") GPG key pair with three subkeys. One of the subkeys was assigned the Authentication [A] capability. In this article, I'll cover how to use that authentication subkey as your login key for remote secure shell (SSH) connections.

SSH public key authentication

The OpenSSH server supports various methods of user authentication, each with different degrees of inherent security. Basic password authentication (the default) is generally considered to be the least secure method. Password authentication only requires knowledge of a username and password in order to gain access to a host via SSH. Usernames can often be derived from things like email addresses or knowledge of the host operating system (for instance, most Unix-like systems have a root account). Passwords can be guessed based on knowledge of the user or system owner, or via brute-force attacks that try thousands of common passwords in a short time.

One of the more commonly used methods of providing enhanced login security for SSH is public key authentication. Rather than a username and password, an OpenSSH server employing the public key authentication method requires that the remote user possess a cryptographic key in order to login. Access to the key files themselves are often tightly secured by file permissions, and the keys usually require a passphrase be entered in order to use them. This provides two factors of authentication for SSH access:

  1. The user must possess the private key file, and
  2. The user must know the key's passphrase.

To use public key authentication, a user who requires SSH access to one or more remote servers generates a key pair. The private key is stored in their local ~/.ssh directory, and the public key is copied into a file called authorized_keys which is stored in the home directory of the account to which the user wants to authenticate. For example, to login as the user account 'bob' on a computer called 'serverA', a remote user's public key would be stored in the file /home/bob/.ssh/authorized_keys on serverA. When the remote user connects via ssh bob@serverA the OpenSSH server performs a cryptographic challenge using the public key stored in authorized_keys, and the user's SSH client responds using the local private key to authenticate.

The authorized_keys file for a user account can also contain multiple public keys, allowing more than one remote user to authenticate to the same account on a server. For example, a common method of providing root access to a server managed by a small team is to add the SSH public key for each allowed admin to the /root/.ssh/authorized_keys file on that server. This provides root access to each admin without giving them the password for the root account itself. If an admin leaves the team their access is revoked by removing their key from the authorized_keys file. This is much less impactful than using password authentication, which would require changing the root password when an admin leaves the team and updating the other admins with the new password.

This is also a fairly common method of providing temporary access to a server for the purpose of remote support.

Configure the server side

Configuring an OpenSSH server to use public key authentication is very simple, and the option is usually enabled by default. Simply add the following to the /etc/ssh/sshd_config file:

PubkeyAuthentication yes

If you want to disable password authentication completely, you can also use the option:

PasswordAuthentication no

Remember to do this only after you have configured public key authentication, or you may find yourself locked out of your remote server.

You can also allow the root account to login via SSH but force public key authentication for the account by using the option:

PermitRootLogin prohibit-password

This is useful in the case where you want to allow normal users to authenticate with a password (for example, on an SFTP server where users are assigned the rssh shell). You may also require root login to administer the server, but you don't want root to be able to login with a password. A combination of PasswordAuthentication yes and PermitRootLogin prohibit-password will achieve the desired results.

Any time you make changes to your /etc/ssh/sshd_config be sure to restart OpenSSH for the changes to take effect:

# systemctl restart ssh

Public key authentication is only used for SSH sessions. Setting these options will have no effect on logging in via a TTY or console, where a password would still be required.

gpg-agent vs. ssh-agent

The gpg-agent and ssh-agent programs perform very similar tasks. Both are used to temporarily remember passphrases for private key access so that a user does not have to enter the passphrase every time they use the key. Generally, the user is prompted for the key passphrase the first time the key is accessed during a login session. The agent then caches the key and enters it on the user's behalf for some period of time. When the cache expires, the user is then prompted to re-enter the key passphrase the next time they access the key.

Traditionally, SSH keys and GPG keys have been regarded as separate things. A user's SSH key pair was separate and distinct from their GPG keyring. ssh-agent is used to cache passphrases for SSH private keys, and gpg-agent is used to cache passphrases for GPG private keys. However, a GPG public key with the Authentication [A] capability can be exported in a format capable of being used with OpenSSH. And recent versions of gpg-agent are capable of performing as an agent for SSH requests if such a key is being used.

This new paradigm allows gpg-agent to replace ssh-agent and allows the user to employ their GPG key as their login key for SSH access. Key management is streamlined for the user by eliminating the need to manage different key pairs with different tools. This also opens up other possibilities like using a smartcard to store one's SSH key (because it's actually a GPG key).

Disable ssh-agent

If you're going to use gpg-agent as a replacement for ssh-agent you should disable ssh-agent to prevent any conflict between the two. How exactly to do this will vary based on your distribution and the desktop environment you may be using. On Debian-based systems, you can usually disable ssh-agent by removing or commenting the following line in /etc/X11/Xsession.options:

use-ssh-agent

This should prevent ssh-agent from starting the next time you login. For users of some desktop environments, the gnome-keyring-daemon also has an SSH agent component that may need to be disabled from starting on login.

Configure gpg-agent

You'll need to add a line to your ~/.gnupg/gpg.conf file to instruct GPG to use gpg-agent:

use-agent

You'll also need to make some changes to your ~/.gnupg/gpg-agent.conf file. At a minimum, you will need the following option:

enable-ssh-support

You may also define timers for how long gpg-agent will cache the passphrase for your key. These are the default timers (the values are in seconds):

default-cache-ttl 600
default-cache-ttl-ssh 1800
max-cache-ttl 7200
max-cache-ttl-ssh 7200

The default-cache-* options tell GPG to cache the passphrase for that length of time. Each time the key is accessed, if the timer has not yet expired it will be reset. The max-cache-* options set the maximum time the passphrase may be cached, regardless of the last time the key was accessed.

If you have multiple pinentry programs installed (e.g., pinentry-curses and pinentry-gnome3) you can instruct gpg-agent to use a specific pinentry with the pinentry-program option. For example:

pinentry-program /usr/bin/pinentry-curses

After you have disabled ssh-agent and configured gpg-agent for SSH authentication, be sure to log out of your session and log back in to pick up the new options.

Exporting your GPG key

In order to use your GPG authentication key, you'll need to export it in a form usable by OpenSSH. Use the following command to export the key to a file:

$ gpg --export-ssh -o ~/.ssh/filename.pub user@domain.tld

This is the key you'll copy to remote hosts for login access.

Adding your key to the agent

You'll need to add the keygrip for your authentication key to ~/.gnupg/sshcontrol. To get the keygrip for your key, use the following command:

gpg -K --with-keygrip user@domain.tld

Deploying your key

In order to login to a remote server with your GPG authentication key, you will need to first copy your exported public key to the server and store it in the file ~/user/.ssh/authorized_keys. If the server you are connecting to allows password authentication (and you have the account credentials), you can use the ssh-copy-id command to copy your public key to the server:

$ ssh-copy-id user@remotehost

You will be prompted for the account password, and if you successfully authenticate your key will be copied to the authorized_keys file in that user's home directory. You can then login using your key:

$ ssh user@remotehost

If the remote server does not allow password authentication then the public key must be added manually to the user's authorized_keys file using an existing account with sufficient access:

# mkdir -p -m 700 /home/user/.ssh
# cat filename.pub >> /home/user/.ssh/authorized_keys
# chown -R user:user /home/user/.ssh && chmod 600 /home/user/.ssh/authorized_keys

Other configuration options

Depending on your choice of distribution, gpg-agent may be largely configured by default and may work out of the box. If you have problems getting gpg-agent to authenticate your SSH sessions, you may find the following useful to add to your .bashrc or .zshrc:

unset SSH_AGENT_PID
export SSH_KEY_PATH="~/.ssh/filename.pub"
export GPG_TTY="$(tty)"
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
gpg-connect-agent updatestartuptty /bye >/dev/null

The first four set environment variables for your terminal session, and the last line tells gpg-agent the TTY where it should send pinentry prompts if you're using /usr/bin/pinentry-curses. That option can be helpful if you have multiple terminals open and are moving between them or are using a multiplexer like screen or tmux. You may also want to add an alias for the last line, so that you can quickly update your current TTY if you have a problem with pinentry authentication in the terminal:

alias fixga='gpg-connect-agent updatestartuptty /bye >/dev/null'

Using SSH key authentication

Once you have gpg-agent configured, you should now be able to SSH to a remote server where your key has been added:

$ ssh user@remotehost

gpg-agent will prompt you for your key's passphrase, cache the passphrase, and then authenticate your SSH connection. If you start an SSH session again before the cache timer has expired, you will connect to your SSH session without being prompted for the GPG key passphrase.

Key security

When using public key authentication for SSH access, you should always secure your private key with a passphrase. A private key without a passphrase is a "bearer instrument" usable by anyone who obtains a copy of it. Use a strong passphrase, and configure gpg-agent with reasonable cache TTLs. Remember that if your passphrase is cached, your key is subject to use with or without your knowledge by applications and scripts running under your user account.

To provide an even higher level of security for your GPG authentication key and the servers your key allows access to, check out my article Secure your GPG keys with a Yubikey 4 to learn how to move your GPG keys to a cryptographic smartcard. Doing so will help protect your authentication key (as well as your signing and encryption keys) from theft or compromise, and can provide the additional access control option of requiring that a physical button be pressed to access the private keys - even if the passphrase is cached by gpg-agent.

Wrap up

I hope you've found the above information useful and interesting. If you have questions, corrections, or feedback feel free to send me an email. Thanks for reading!