Harden SSH on a Linux instance with Multi-Factor Authentication

With stories like the recent iCloud hack popping up in mainstream media, security and privacy are becoming increasingly important to the average consumer. These topics however are even more important to system administrators, those of us entrusted with safeguarding sensitive data against an ever changing threat. As technology professionals we have access to a number of tools to assist in defending our networks and protecting private data, but one of the most powerful tools at our disposal is multi-factor authentication. In this post we will take a high level look at multi-factor authentication and discuss the implementation of one multi-factor authentication solution to secure SSH access on a Linux instance.

What is mult-factor authentication?

Put simply, multi-factor authentication is the use of two or more independent authentication factors. That is to say a knowledge factor (something a user knows) and a physical factor (something a user has). One example of multi-factor authentication is authenticating against an ATM machine. When authenticating against an ATM machine, you are required to present both your ATM card (the physical factor) and your PIN (the knowledge factor).

Hardening SSH with a Yubikey

Single factor authentication has been available in SSH since, well, the beginning, but SSH 6.2 added support for the AuthenticationMethods setting which allows for multiple authentication methods to be daisy-chained for a specific user or group. In this tutorial we will be using the AuthenticationMethods setting in concert with the pam_yubico module to require our users to present both a valid private SSH key and an OTP (one time password) generated by a Yubikey. Before we begin, though, we should take note of a few things:

  1. If a user doesn’t present a valid private key the authentication process will never reach the Yubikey phase and authentication will fail.

  2. The enhanced authentication will only apply to the users or groups specified. Design your user account and privilege structures accordingly.

  3. We will be working with an AMI running Amazon Linux in this tutorial, but the steps are nearly identical for any RHEL-based distribution. The only difference is that you wouldn’t need to remove the pre-installed EPEL repository on a distribution other than Amazon Linux.

Step 1: Remove pre-installed Amazon EPEL repository

The pam_yubico package doesn’t appear to be present in the pre-installed EPEL repository so we’ll begin by removing the pre-installed EPEL repository with the following command:

[bash] sudo rpm -qa | grep epel | while read repository;do sudo rpm -e $repository;done [/bash]

Step 2: Install standard EPEL repository

We’ll now install the standard EPEL repository using the following command:

[bash] sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm [/bash]

Please note that at the time of writing the most current version of EPEL within the CentOS 6 branch is 6.8. The version number of the package you install may change depending on when you’re following this tutorial.

Step 3: Install pam_yubico package

We’ll now install the pam_yubico package with the following command:

[bash] sudo yum -y install pam_yubico [/bash]

Step 4: Create group for multi-factor authentication

Now we’ll create a group for users that will be required to use multi-factor authentication:

[bash] sudo groupadd mfa [/bash]

For the purposes of this tutorial we’re naming the group “mfa”, but you can name it anything you would like.

Step 5: Add user to mfa group

We’ll now place a user into the group we just created with the following command:

[bash] sudo usermod -aG mfa ec2-user [/bash]

For the purposes of this tutorial we’re using the usermod command to add an existing user to the mfa group. If you would like to create a new user account and add it to the mfa group you would use the useradd command. For example, you can create a new user named bob and add him to the mfa group with the following command:

[bash] sudo useradd -G mfa bob [/bash]

Step 6: Edit SSH server configuration

We now have to make changes to the SSH server configuration file so it will enforce additional authentication methods for users in the multi-factor authentication group. We’ll begin by opening the SSH server configuration file in the Vim with the following command:

[bash] sudo vim /etc/ssh/sshd_config [/bash]

Once the file is open, we’ll first hit i on our keyboard to enter insert mode then change the ChallengeResponseAuthentication parameter to yes so the line looks like:

[bash] ChallengeResponseAuthentication yes [/bash]

We’ll continue our edits by moving to the bottom of the file and adding the following line:

[bash] Match Group mfa AuthenticationMethods publickey,keyboard-interactive [/bash]

We chose to name our multi-factor authentication group “mfa” so we’re using the same name above. If you chose to name your group something other than “mfa” you will need to change the name of the group in the SSH server configuration file. For example, if you named your group mfa_users you would change:

[bash]Match Group mfa[/bash]


[bash]Match Group mfa_users[/bash]

Once you’ve completed the edits you can save and close the file by hitting escape to exit insert mode then typing  :wq to write your changes to the file and quit the Vim editor.

Step 7: Restart SSH server

Now that we’ve completed our edits we need to restart the SSH server service so it will pick up our configuration changes. Restart the SSH server service with the following command:

[bash] sudo service sshd restart [/bash]

Step 8: Obtain Yubicloud API key

Now that we have created our multi-factor authentication group, added our users to the group, and configured the SSH server for multi-factor authentication, we need to obtain a Yubicloud API key in order to validate the OTP emitted by the Yubikey.

To obtain a Yubicloud API key you need to:

  1. Navigate to https://upgrade.yubico.com/getapikey/ in your browser.

  2. Enter your Email address and a yubikey-generated OTP in the form provided.

  3. Click “Get API Key”

Store your Client ID and Secret Key in a safe location as they will be used in later steps of this tutorial.

Step 9: Create yubi-auth PAM module configuration file

We’ll now begin configuring the Yubico PAM module for multi-factor authentication. We’ll begin by creating the file with the following command:

[bash] sudo vim /etc/pam.d/yubi-auth [/bash]

Next, we’ll edit the file to match the example below, substitute your Client ID and Secret Key for the placeholders in the id and key fields.

[bash] auth sufficient pam_yubico.so id=your_yubicloud_id key=your_yubicloud_api_key authfile=/etc/ssh/yubikey_mappings url=https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s debug [/bash]

Step 10: Create Yubikey mappings file referenced in yubi-auth file

Now we’ll create the yubikey_mappings file we referenced in the yubi-auth file. Create the yubikey_mappings file with the following command:

[bash] sudo vim /etc/ssh/yubikey_mappings [/bash]

Then edit the file to look like the example below. You will need to substitutie the name of a user account which is in the multi-factor authentication group for the username placeholder and the public ID of the Yubikey the user will be using for the yubikey_id placeholder. The public ID of the Yubikey is the first twelve characters of any OTP emitted by the Yubikey and can also be found at demo.yubico.com


Step 11: Edit SSH server PAM configuration file

Finally, we’ll edit the PAM configuration file for the SSH server to include the yubi-auth module as an authentication source. Open the file for editing with the following command:

[bash] sudo vim /etc/pam.d/sshd [/bash]

Add the following line to the beginning of the file and save the changes.

[bash] auth include yubi-auth [/bash]

Barring any errors in configuration (or errant solar flares), connecting to your SSH host that is secured with multi-factor authentication should produce output similar to the following:

[bash] Authenticated with partial success. Yubikey for `ec2-user’: [/bash]

A quick look at the above output indicates that the user has been partially authenticated (with their SSH key) and an OTP from the Yubikey assigned to that user is required to complete authentication. At the beginning of this post we discussed multi-factor authentication at a high level and looked at the two core elements of multi-factor authentication; the knowledge factor and the physical factor. The two authentication elements used in this particular solution are the private SSH key ( the knowledge factor – only when encrypted with a passphrase) and the Yubikey (the physical factor).

One final note: As mentioned in the video, SSH will fall back to password authentication if validation of the OTP generated by the the Yubikey fails. I’m not certain as to why this is so, but my best guess is that because password authentication is a form of keyboard-interactive authentication it will be attempted should validation of the  OTP fail.  As also mentioned in the video, a couple ways of working around this “bug” exist. They include (in order of effectiveness):

  1. Disabling password authentication for SSH entirely.

  2. Not providing a password for the account that requires the Yubikey as a second factor of authentication. This is technically a solution as an absent password (and password hash) can’t be validated against standard input entered during the connection attempt and will, therefore, fail. I DO NOT recommend this approach though as it opens additional security holes around privilege escalation if an attacker has compromised the machine.

If you encounter any issues with any of the steps in this tutorial please don’t hesitate to leave a comment describing your issue and I’ll be happy to assist you.


Leave a Reply

Your email address will not be published. Required fields are marked *