Enabling SSH Key Pair Authentication in Ubuntu Server 18.04

Posted on Thursday June 28th, 2018

About SSH Keys

Here's a nice intro about SSH Keys written by DigitalOcean on their How-To guides: How to Setup SSH Keys and How to Configure SSH Key based authentication on a Linux Server:

Secure Shell (better known as SSH) is a cryptographic network protocol which allows users to securely perform a number of network services over an unsecured network. SSH keys provide a more secure way of logging into a server with SSH than using a password alone. While a password can eventually be cracked with a brute force attack, SSH keys are nearly impossible to decipher by brute force alone.

Generating a key pair provides you with two long string of characters: a public and a private key. You can place the public key on any server, and then unlock it by connecting to it with a client that already has the private key. When the two match up, the system unlocks without the need for a password. You can increase security even more by protecting the private key with a passphrase.

Requirements

This guide is was written and tested on a fresh installation of Ubuntu Server 18.04 LTS (Bionic Beaver), but the process and configuration should be the same for any modern Linux Distro.

We will also assume that (1) SSH is already enabled and working using a user and password combination, (2) you have access to the server and (3) you have superuser/admin rights.

1. Generate a RSA Key Pair

First, you need to generate a RSA Key Pair:

You should end up with a Public Key file, we'll call it mykey.pub throughout this guide.

2. Set up the Authorized Keys file

Look for a directory in your server user's home directory called .ssh. Note that the period before the name means that it is a hidden directory, so you have to use ls -a in order to display all files, including the hidden ones.

If the .ssh directory doesn't exist yet, we'll create it:

cd ~
mkdir .ssh
chown myuser:myuser -R .ssh

Make sure the directory's permissions are correct by running ls -la.

Copy your Public Key to the server

You can use any method you prefer to copy your public key file to the server. I like to use scp:

scp  ~/.ssh/mykey.pub <user>@<host>:~/.ssh/mykey.pub

Add your Public Key to the Authorized Keys list

We'll now create a file called authorized_keys inside the .ssh directory, and we'll copy the contents of your public key into this file. If the file already exists this command will only append your public key to the file but it won't delete any previous records.

touch ~/.ssh/authorized_keys
cat ~/.ssh/mykey.pub >> ~/.ssh/authorized_keys

3. Configure SSH to use Authorized Keys

The main configuration file for the SSH server is in /etc/ssh/sshd_config. Before continuing, I recommend you first create a backup of the configuration file.

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

Edit the file using nano (or vim):

sudo nano /etc/ssh/sshd_config

Make the following changes to the configuration file. All of these options should already be in the file, you might have to uncomment (remove the # at the start of the line) or change the default value.

Public Key Authentication

PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

Disable root account

PermitRootLogin no

Enable PAM

We have to make sure that PAM (Pluggable Authentication Modules) is enabled in our config, since it provides the base API to enable Key Authentication. Here's a nice explanation about PAM.

UsePAM yes

Save the config file and restart the SSH daemon service.

sudo systemctl restart ssh

Without closing your current terminal/session, attempt to login now from a second terminal window using your IdentityFile (.pem) or GPG card. If all went well, the server should not have asked for your password and instead should have logged you in directly.

4. Disable Password Authentication

If you were able to login using your Private Key, you can now safely disable password logins. We'll edit the SSH Config file again:

sudo nano /etc/ssh/sshd_config

Disable password login

PasswordAuthentication no
PermitEmptyPasswords no

Restart once again the SSH daemon:

sudo systemctl restart sshd_config

Try to login with your Private Key, it should still work.

Try to login again without an IdentityFile and instead of the server asking you for a password, you should get an error such as:

Permission denied (publickey).

Nice.

5. Delete your User Password

Now there's no point in keeping a user password in your server, you won't be able to use it to login and it'll only nag you when you try to run SuperUser (sudo) commands. Or worse, you might forget it.

First we make sure that our user does not need to input a password when running sudo, so we'll modify the sudoers file located in /etc/sudoers.

sudo nano /etc/sudoers

Look for a line containing your username such as:

myuser  ALL=(ALL:ALL) ALL

If it exists, modify it as follows, if not, simply create the line.

myuser  ALL=(ALL) NOPASSWD:ALL

Make sure that your user definition is placed after any other user or group definition that includes your user. For example, it should go after the %sudo group definition.

According to the sudo man page:

When multiple entries match for a user, they are applied in order. Where there are multiple matches, the last match is used (which is not necessarily the most specific match).

Test your new sudo powers, it shouldn't have asked you for your password.

Finally, you can safely delete your user password. Replace myuser with your current user in the command:

sudo passwd --delete myuser

That's it.

References



Configuring a Yubikey with GPG for SSH Authentication

Posted on Wednesday June 27th, 2018

This guide was tested on my current development setup:

  • Local: macOS 10.13.5 High Sierra on a MacBook Pro 15-inch Touchbar
  • Remote: AWS EC2 Ubuntu 18.04 LTS (Server, Bionic Beaver)

And for the hardware, I'm using a couple of YubiKey 4. I highly recommended that you get at least a pair of them.

Note: throughout the guide and in the GnuPG references, the YubiKey is referred to as a card, while key refers to a RSA Key.

1. Configure your Local Machine

We need to install some utilities in the local machine provide the basic functionality to interfase with the YubiKey. We'll be using GnuPG:

GnuPG is a complete and free implementation of the OpenPGP standard as defined by RFC4880 (also known as PGP). GnuPG allows you to encrypt and sign your data and communications; it features a versatile key management system, along with access modules for all kinds of public key directories. GnuPG, also known as GPG, is a command line tool with features for easy integration with other applications. A wealth of frontend applications and libraries are available. GnuPG also provides support for S/MIME and Secure Shell (ssh).

The easiest way to install GnuPG in macOS is by using Homebrew:

brew install gnupg2 gpg-agent pinentry-mac

If you bash profile does not specify a language with LANG, gnupg2 will try to guess the best language for you. For some unknown reason, my installation decided that it'd be better in spanish and while the intention is appreciated, the command line utilities are a bit wonky in languages other than english.

However, this is a very quick fix. We'll set the appropiate LANG environment variable in the bash profile to en.

echo "export LANG=en" >> ~/.bash_profile

Along with GnuPG, we've installed a utility called gpg-agent which operates as a link between the YubiKey and the underlying GPG libraries. In order to improve the compatibility between macOS and the YubiKey, we need to add the following lines to the gpg-agent configuration file located in ~/.gnugp/gpg-agent.conf

pinentry-program /usr/local/bin/pinentry-mac
default-cache-ttl 3600
default-cache-ttl-ssh 3600
max-cache-ttl 7200
max-cache-ttl-ssh 7200
enable-ssh-support

We also need to update the shell environment to allow ssh to use gpg-agent as an authentication service.

echo "export GPG_TTY=$(tty)" >> ~/.bash_profile
echo "export SSH_AUTH_SOCK=${HOME}/.gnupg/S.gpg-agent.ssh" >> ~/.bash_profile

Close all your current terminal windows and restart the Terminal application.

Restart the gpg-agent service and update its settings:

gpg-connect-agent killagent /bye
gpg-connect-agent updatestartuptty /bye
gpg-connect-agent /bye

Finally, insert your YubiKey in a USB port and check if it is being correctly detected by running the command:

gpg --card-status

You should see the details of your YubiKey (card) in the console. Take note of the Serial Number of the card, it might be of use later in the setup.

2. Initialize the YubiKey

2.1 Change the PINs

In compliance with the standards, the YubiKey operates with two different PINs:

  • User PIN: a 6-digit (or longer) PIN used to unlock and enable the card to perform operations such as signing, encrypting and authenticating.
  • Admin PIN: a 8-digit (or longer) PIN used to configure the card.

The first thing we'll do is change the default PINs in the card, both the User PIN and the Admin PIN. In the beginning of this guide we selected the program pinentry-mac as the pin-entry mechanism. This is a simple application that displays a small pop-up window whenever a PIN needs to be entered. It will also display some additional information about the action that is being requested to authorize with the PIN, and the type of PIN that is being requested (User or Admin).

To change the PINs we use the utility:

gpg --change-pin
  1. Change the User PIN entering the option 1, the default User PIN is 123456.
  2. Change the Admin PIN entering the option 3, the default Admin PIN is 12345678.

2.2 Set the Card Holder's Data (optional)

This step is completely optional, but it's helpful when managing more than 1 card.

gpg --card-edit

The --card-edit command initializes a simple console. We first need to enable the admin mode entering:

gpg/card> admin

The console should say Admin commands are allowed. In the following steps your Admin PIN might be requested in order to modify the card's details.

  1. Enter name to set the holder's name, it asks for the Surname (last name) and the Given name (first name) independently.
  2. Enter sex to set the holder's sex
  3. Enter lang to set the 2-letter language code (see ISO 639-1)

Finally, you can enter quit to exit the console.

gpg/card> quit

3. Generate your RSA Keys on your YubiKey

The YubiKeys come blank, that is, without any preset RSA Keys. The YubiKey 4 can generate and store 3 different types of RSA-2048 keys: - Signature Key - Encryption Key - Authentication Key

Run again the gpg --card-status command in your terminal and it should display the updated card info. The 3 types of keys are displayed at the bottom of the list, and they should not be set and should be displayed as [none]

We'll use gpg --card-edit again and we'll enable the admin mode by typing admin into the console.

The generate command provides some options for the generation of the keys. All the options should be entered quickly to prevent a timeout in the process. Please read ahead and fully understand the available options before running the command. At any point of the process it might ask you for the User PIN or the Admin PIN, please pay attention to the pinentry dialogs and enter the correct PIN when requested.

gpg/card> generate

Key generation options:

  • Make a backup copy of the private key? No, do not make a backup. This card will be used for SSH Authentication only, which means that if the key is lost, you can have a backup card to authenticate against your servers. If you don't make a backup copy then the private key will never leave your YubiKey. (However, if you plan to also use this YubiKey to encrypt files, sign emails or some other use, then you might consider making a backup.)
  • Set the expiration time. If you set your keys to expire, also remember to periodically update your public keys.
  • Enter the Holder's Real Name. Enter both the first and last name in the same field. Preferably, this should match the Holder's name provided in the card initialization.
  • Enter the Holder's Email.
  • Optionally add a comment to the key

Wait a few seconds while the key is being generated.

At the end, you should see the following message:

public and secret key created and signed.

This means the YubiKey has successfully generate a new set of public-private key pairs and it has stored them on the device.

Press <enter> on the GPG console to see the card status. You should now see the signature of the created keys at the bottom of the list. You can now quit the GPG console.

gpg/card> quit

4. Extract your Public Key

Run the following command in your terminal:

ssh-add -L

You should now see the public key of the card.

If the console says The agent has no identities. you might have an error in your local machine settings. Check the Step 1 of this post.

We'll save the Public Key to a file to make handling it easier:

ssh-add -L > ~/.ssh/yubikey_gpg.pub

Now you have a secure key pair that can be used to authenticate in SSH or other services. Remember, the private key lives securely in your YubiKey and cannot be extracted, while your public key has been saved in the .pub file and can be shared.

5. Repeat for a second YubiKey

I highly recommended that you repeat this process for a second (or third) YubiKey in case of loss or damage to either one of them.

6.A Import your Public Key to AWS EC2

The AWS documentation has a simple step-by-step guide to import your public key into your AWS console panel. After you have imported your public key, you can select it when launching a new EC2 instance. Sweet!

"Importing Your Own Public Key To Amazon EC2"

6.B Add your Public Key to an Existing Server's Authorized Keys List

I'll be assuming Ubuntu 18.04 on AWS EC2, but the process should be more or less the same for any other distro, version or cloud provider.

You'll need to use your existing .pem file to login and configure the remote server. I recommend you to keep a SSH session open and then open a second session to modify and test your server's settings. This way, if anything wrong were to happen, you could still revert the changes by using the first session while making sure you do not get locked out from your own server.

If your server is not yet configured to authenticate SSH with Identity Files or Keys, see my other post.

Copy the Public Key file from your local machine to the remote machine. You can use this one-liner by adjusting the location of the awskey.pem file in your local machine, and specifying the correct <user>@<host>:

local> scp -i awskey.pem ~/.ssh/yubikey_gpg.pub <user>@<host>:~/.ssh/yubikey_gpg.pub

Once your Public Key is copied, log into the remote machine in the second SSH session. We will now append your new public key to the list of authorized keys in your server. Enter this command on the remote machine:

remote> cat ~/.ssh/yubikey_gpg.pub >> ~/.ssh/authorized_keys

That's it, we're done. Now you can use your YubiKey to log in to your server! However, you can still log in with the original Identity File (.pem), which might be desired or not.

Disable the original Identity File (.pem)

If you'd like to disable logging-in with the original .pem file, we have to remove its public key from the authorized_keys list.

You could do something like:

remote> mv ~/.ssh/authorized_keys ~/.ssh/authorized_keys.backup
remote> touch ~/.ssh/authorized_keys
remote> cat ~/.ssh/yubikey_gpg.pub >> ~/.ssh/authorized_keys

Remember to double check your changes before logging out.

Clean up the files in the remote server

If you like things neat and tidy, you should also remove the .pub file that was placed in the remote server.

remote> rm ~/.ssh/yubikey_gpg.pub

7. Logging-in with the YubiKey

The process to log in via SSH using your YubiKey is the same, except we'll skip the ugly -i awskey.pem part. Example:

USING PEM FILE:  ssh -i ~/path/to/awskey.pem <user>@<host>
USING YUBIKEY:   ssh <user>@<host>

Nice!

When you try to log in, your local machine will detect that the SSH server requires an Identity File. Then, it will check the server's authorized_keys against the local gpg-agent which in turn will check in the YubiKey. If the YubiKey provides a matching authorization public key, it'll request to unlock the card via the pinentry program. Once unlocked, the card will handle the authentication securely using it's private key. If everything matches (as it should), you're in!

Troubleshooting

During my experimentation with this setup, I had a some random gpg errors and bugs in my local machine, such as gpg: key generation failed: End of file. and gpg: error setting lang: Broken pipe.

I have some theories that the underlying gpg-agent gets out of sync with your terminal and the card, and the procedure that usually worked for me was:

  1. Fully quit your terminal and restart it.
  2. Restart the gpg-connect-agent daemon as shown in the Step 1 of this post.

Factory Reset your YubiKey

If something goes wrong, here's a quick Gist to reset your YubiKey 4. Bear in mind that you will loose your Private Keys and you will loose access to any service that is linked to your card's public key.

Sources & References

Some of the articles, documentation and guides that I consulted, in no particular order: