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.
Prerequisites
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.
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.
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.
Click Next.
On the Set up device page, click 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 }
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:
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:
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:
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.
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
Conclusion
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.