Multi-factor authentication (MFA) provides an extra layer of security to your accounts and helps prevent unauthorized access, even if a user’s access key is compromised. It’s fairly straightforward to add MFA to use AWS Management Console, which is highly recommended. It involves a few more steps when using the CLI, which you will see in this tutorial.

When using AWS CLI, developers generally store their IAM keys in plaintext on their computers. In the previous article, we discussed how to rotate AWS access keys. This is a good practice, but anyone with access to those credentials can still impersonate the owner before they are revoked. These keys are generally quite powerful and allow access to critical parts of the system. In this tutorial, you will learn how to add Yubikey support for using AWS CLI so that even if the IAM key is compromised, the hackers can’t use it without access to the physical security token.


Set up the environment

To test using a Yubikey with AWS CLI, first, you’ll need an IAM user.

Log in to AWS Management Console and go to Identity and Access Management (IAM).

Click Users in the menu and then click the Add Users button.

Enter demo_user as the user name and click Next.

Click Next again on the Permissions page and click Create user on the review page.

After creating your user, click on the user name and switch to the Security credentials tab.

Scroll down to Access keys and click Create access key.

Select Command Line Interface (CLI) for your intended usage, tick the I understand… checkbox at the bottom and click Next.

Command Line Interface (CLI) is selected as the intended use for the key

Click Create access key on the next page.

In your AWS credentials file (~/.aws/credentials), copy and paste your access and secret access keys into a new profile with the same name so it looks like this:

Click Done to finish the key creation process.

Open a terminal and run the following command:

aws s3 ls --profile demo_user

You should see Access Denied error as you haven’t granted any permissions to this access key.

Terminal showing Access Denied error while trying to list the buckets

Now, create a role that gives this access to the user.

Click Roles and Create role.

Select Custom trust policy and paste the following policy. Make sure to replace <YOUR ACCOUNT NUMBER> with the actual value.

  "Version": "2012-10-17",
  "Statement": [
      "Sid": "Statement1",
      "Effect": "Allow",
      "Principal": { "AWS":"arn:aws:iam::<YOUR ACCOUNT NUMBER>:user/demo_user" },
      "Action": "sts:AssumeRole",
      "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }

Click Next.

On the Add permissions page, search S3 and tick the checkbox next to the AmazonS3ReadOnlyAccess policy.

Click Next.

Enter s3-read-only-mfa-role as the role name and click Create role.

As you can see in the role policy, there is a condition for the MFA present. In the next section, you will add that to the user.

Add Yubikey to the User

Open the user’s Security Credentials tab and scroll to the Multi-factor authentication (MFA) section.

Click the Assign MFA device button.

On the Select MFA device page, enter yubikey as the device name.

Select the Authenticator app as the device type. This is a bit counter-intuitive as Yubikey is listed as a security key, but you will generate a Time-based one-time password (TOTP) with your Yubikey, so make sure to select this option.

Select MFA device page showing yubikey entered as device name and Authenticator app selected as device type.

Click Next.

On the Set up device page, click the Show secret key link.

Set up device page showing the Show secret key link

As you will be using the CLI, the QR code will not do it in this case, but the QR code is just a visual representation of the same secret key anyway.

Copy the secret key displayed on your screen and run the following command in a terminal:

ykman oath accounts add -t yubikey { YOUR SECRET KEY }
Make sure to use the -t parameter as it enforces touching the key. It adds extra security as the codes cannot be generated without physical access.

It’s now time to generate 2 time-based codes. So to achieve this, run the following command:

ykman oath accounts code yubikey

Copy the 6-digit integer and paste it into the MFA code 1 field.

Keep running the command above to generate a second code. It may take a few tries as it’s time-based. For example, my attempt looked like this:

Terminal window showing ykman commands to add the key and generate the codes

Now it’s time to tie them all together. For this, you will need the following to your AWS config file (~/.aws/config):

[profile demo_user]
source_profile = demo_user
mfa_serial = arn:aws:iam::<YOUR ACCOUNT NUMBER>:mfa/yubikey
role_arn = arn:aws:iam::<YOUR ACCOUNT NUMBER>:role/s3-read-only-mfa-role

Make sure to replace <YOUR ACCOUNT NUMBER> with the actual value in both places.

Rerun the same test command to see if it behaves differently:

aws s3 ls --profile demo_user

This time you should see the CLI asking for an MFA code:

Terminal showing AWS CLI asking for an MFA code

Press Ctrl + C to exit this, as you don’t have the code at the moment. To generate the code, run the same command you used while registering the MFA device:

ykman oath accounts code yubikey

Then, rerun the AWS command and copy/paste the 6-digit code generated by Yubikey and press enter. This time you should see the successful results:

Terminal window showing successful results after MFA code has been entered

By default, the temporary credentials are valid for 1 hour. You can change this value in the role settings by clicking the Edit button in the role summary section.

Make sure NOT to choose for a very long time, as the temporary security credentials cannot be revoked.

If you want to double-check that you’re really using these temporary credentials for the role you created, run the following command:

aws sts get-caller-identity --profile demo_user

You can see in the Arn field the assumed role is s3-read-only-mfa-role.

To further test, attempt to create a new bucket by running the following command:

aws s3 mb s3://my-demo-bucket-56345765 --region us-east-1 --profile demo_user

You should get an Access Denied error as you only have read-only access to the S3 service.

If you want to revoke the credentials locally, you can delete the AWS CLI cache by running the following command:

 rm -r ~/.aws/cli/cache


In this tutorial, you learned how to enforce using MFA with Yubikey for your CLI operations. This way, the access key you store in your credentials file is just used to assume the role with the actual permissions. The stored keys have no permissions at all. Therefore even if they are compromised, as long as the attacker doesn’t have access to the physical security key, they will not be useful to them. I hope you found the article helpful in improving your AWS account security.

Categories: awsiam