Using MFA with Awsume to Secure the AWS CLI

In this blog post we’re going to discuss how to set up a new user in the AWS Management Console, how to enable MFA on that user, how to force the user to login with MFA, how to minimize the duration of the MFA session, and how to minimize the work necessary to use MFA with Awsume.

Securing access to the AWS CLI with MFA is a best practice. Failing to do so means that if the access key and secret are compromised, the API can be used to compromise your AWS account. If the account in question has any significant privileges, this can spell doom for your organization.

AWS supports securing API access via the CLI with MFA. Using MFA on the command line can prove challenging, however. There’s a learning curve and there’s a lot to type. You can outsource this to some specialized scripts, but even then, there are better options available and there are a few things that are easy to overlook.

Creating a New User Account in the AWS Management Console

The steps outlined in this blog post will be limited to user accounts. However, the same concepts presented in this document can be applied to roles, as well. To limit the scope of this post and to ensure that it is easier to understand, we’ll be sticking to user accounts. We’ll also be forgoing the usage of group to make this process easier to stomach.

You’ll need access to the AWS Management Console with permissions in IAM to create users and policies, the permission to attach and detach policies, as well as the permission to enable MFA on user accounts. So, essentially, you’ll need to be an administrator.

As a best practice, create a secondary administrator account and login to perform administrative actions using that account. Don’t login to Amazon as a root account unless absolutely necessary.

With your administrator account, login to the AWS Management Console. Select the IAM service. In the navigation bar on the side, click the Users link.

AWS IAM Users

AWS IAM Users

Click the add user button. The add user screen will appear. Enter a new username. In our example, the username will be ‘cli-user’. Select programmatic access and deselect AWS Management Console Access. Console access will not be needed for this demonstration. Click on the Next: Permissions button to proceed.

Adding a User to IAM

Adding a User to IAM

On the Set Permissions page, select “Attach existing policies directly.” Then filter the policies by job function and check the PowerUserAccess policy. This will demonstrate a user with significant privileges in AWS. Once complete, press the Next: Tags command button.

Attaching a Policy to a User in IAM

Attaching a Policy to a User in IAM

For our purposes, we’ll skip the Tags section go and proceed to click the Next: Review button. On the Review page proceed to verify that all of the use information that you’ve entered is valid and then click the Create User button to continue. Finally, download the CSV containing the users access key ID and secret access key. We’ll need this information in order to configure the AWS shell command and Awsume. Once you’ve downloaded the file, press the close button to dismiss the wizard.

Downloading the Security Credentials of the New User in IAM

Downloading the Security Credentials of the New User in IAM

Back on the IAM user screen you will now see the newly created user. At this point, we can use the new user to make API calls via the AWS CLI. This is only partially secure, however, because it only requires the Access Key ID and the Secret Access Key. Let’s demonstrate this by configuring the AWS CLI to use our new user.

The Newly Created User in IAM

The Newly Created User in IAM

Configuring the AWS CLI with the New User

You’ll need the AWS CLI in order to perform these actions. Open a terminal and type the following command. This will allow you to configure the Access Key ID and Secret Access Key.

$ aws configure
AWS Access Key ID [None]: <omitted>
AWS Secret Access Key [None]: <omitted>
Default region name [None]: us-east-2
Default output format [None]: json

We can now verify that these settings are correct by typing the following command. Note that the list roles command is available to all power users. We’ll use this command to test the capabilities of the logged in user.

$ aws iam list-roles
{
  "Roles": [
    {
      "Path": "/aws-service-role/support.amazonaws.com/",
      "RoleName": "AWSServiceRoleForSupport",
      [...]

Note that the AWS CLI stores the credentials and settings for the user in a pair of configuration files located under the user’s home path in the .aws directory. In this directory, you’ll find two files: config and credentials. The config file will contain both the region and output formats. In the credentials file, the AWS Access Key ID and Secret Access Key are defined. Also note that the profile names in both the config and credentials files are “default” by default. We can change these profile names to match the name of the user if we so desire. However, for simplicity purposes, we will leave the default profile name in this demonstration.

$ cat .aws/config
[default]
region = us-east-2
output = json

$ cat .aws/credentials
[default]
aws_access_key_id = <omitted>
aws_secret_access_key = <omitted>

You may have noticed that we were able to invoke the list-roles IAM function without providing an MFA token. We have not configured multifactor authentication on our user yet. Let’s go back to the AWS Management Console to accomplish this.

Assigning an MFA Device to the New User

Back in IAM, click on the Users link in the sidebar to bring back the list of users. Click on the user that we created in the previous step. This will bring you to the users summary page. Activate the security credentials tab. Notice at this point, next to the Assigned MFA Device label, it currently states that no MFA device is assigned. We’ll need to add a virtual MFA device for this particular user as the AWS CLI does not currently support hardware devices, such as Yubikey.

The IAM User Security Credentials Tab

The IAM User Security Credentials Tab

Click the Manage button to open the Manage MFA Device dialogue. In this dialog, ensure that the Virtual MFA Device checkbox is checked and Continue. This will bring you to the Setup Virtual MFA Device screen. Click the Show QR Code and use a compatible application to scan the displayed code. Then type in two consecutive different MFA codes in the textboxes below. Then click the Assign MFA button to continue.

Creating a new Virtual MFA Device in IAM

Creating a new Virtual MFA Device in IAM

You should now receive a message indicating that you have successfully assigned a virtual MFA device. Proceed to click Close to dismiss the dialog.

Our new user is now allowed to use MFA to log in, but it is not required by the CLI. We can verify this by going back to the command line and calling the list-roles IAM function again. The command still works even though we have configured the account to use MFA. In order to rectify this, we’ll need to configure AWS to enforce MFA for this user.

Enforcing MFA for the New User

In IAM, click the Policies link in the sidebar then click the Create Policy button. On the Create Policy screen switch to the JSON tab then paste the following:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

This policy will effectively deny access to all resources if the user didn’t authenticate with short-term credentials (in other words, MFA). Click the Review Policy button. On the Review Policy page proceed to name the policy and give it a description and click the Create Policy button.

Creating an MFA Policy in IAM

Creating an MFA Policy in IAM

Back in IAM, click the Users link in the sidebar and then select our user on the Users page. The Permissions tab should be activated. Click the Add Permissions button and then select Attach existing policies directly. In the search text box, enter the name of the policy that you just created.

Check the checkbox next to the policy and click the Next: Review button to proceed. Finally, click the Add Permissions button to attach the policy to the user.

Attaching the MFA Policy in IAM

Attaching the MFA Policy in IAM

We can verify that the user no longer has the ability to call the list-roles IAM function without supplying an MFA token.

IAM User Permissions Summary

IAM User Permissions Summary

Next, we’ll discuss how to login to the AWS CLI using the configured virtual MFA device.

Logging into AWS CLI using MFA with Awsume

Supplying an MFA token through the AWS CLI is possible, but it is complicated. Awsume makes it much easier to perform this operation and it provides the ability to refresh the MFA token when it has expired. If you need assistance installing Awsume on an Ubuntu system, see my previous blog post.

The first thing that we need to do is inform the AWS CLI that MFA is required for this user. This can be accomplished by using the set subcommand for aws configure. The value for the mfa_serial attribute is the resource identifier of the MFA Virtual Device created above (the value next to the Assign MFA Device label on the Security Credentials tab of the user in IAM).

$ aws configure set mfa_serial arn:aws:iam::<omitted>:mfa/cli-user

Opening the config file under the .aws directory in your home directory will reveal the change under the default section.

$ cat .aws/config
[default]
region = us-east-2
output = json
mfa_serial = arn:aws:iam::<omitted>:mfa/cli-user

Simply adding this value will tell Awsume that MFA needs to be performed. When you invoke Awsume with the profile name declared in the config file, it will prompt you for an MFA token. At this point, you will launch the authenticator app configured and supply the token, as shown below:

$ awsume default
Enter MFA token: <omitted>
Session token will expire at 2021-01-27 11:47:40

We can verify that the call to the list-roles IAM function works as before. Unfortunately, you’ll note that the session token expiry is a full twelve hours, by default. This is typically unsatisfactory and we’ll address this in the next section.

Configuring the MFA Session Token Expiry for the AWS CLI

The duration of the session token expiry can be range from as low as 15 minutes to as large as 36 hours. For most scenarios, I don’t recommend anything longer than about an hour.

We can adjust the policy that we created in the previous step to account and add a condition to account for the age of the MFA session. So if we deny access to all resources when the session age exceeds 3600 seconds , this will limit an MFA session to one hour.

Go back to IAM in the Amazon Management Console and select the Policies link from the sidebar and navigate the policy that we created in the previous step. Select the policy and click the Edit policy button to edit it. Activate the JSON tab and adjust the policy to reflect the content below:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    },
    {
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "NumericGreaterThan": {
          "aws:MultiFactorAuthAge": 3600
        }
      }
    }
  ]
}

Note that I separated these conditions into multiple statements. This is because under a single statement, the conditions are AND’d together. If we combined into a single statement, then the Deny rule would only be in effect if the MFA session age was older than an hour and the user did not authenticate with MFA, which is technically impossible.

Once you save the changes our user will have to re-authenticate with an MFA token once the duration expires. We can verify this by setting the above duration to a small value, such as 60 seconds. After waiting for a minute, the call to the list-roles IAM function fails with an explicit deny.

You’ll notice, however, that Awsume still believes that the session expires after twelve hours. There is one final hurdle to clear and we’ll accomplish that next.

Configuring Awsume with an MFA Session Duration

As soon as your session expires, you’ll start receiving explicit denies. This won’t be a problem if you have a long expiry, but it will prove problematic with Awsume once it does. I fumbled about until I finally found the cache file to remove (oddly enough under the .awsume/cache/ folder) to force Awsume to prompt me for another MFA token.

Unfortunately, Awsume doesn’t give you an easy way to configure an MFA session duration. If we look at Awsume’s code, we find the call to the STS get-session-token function passes a session duration value from the config.yaml. Thus, if we adjust Awsume’s configuration file (located at ./awsume/config.yaml) to match our policy, Awsume and AWS should stay synchronized:

colors: true
fuzzy-match: false
role-duration: 0
debug:
  session_token_duration: 3600

Now, Assume will prompt you for a new MFA token once your session expires. Note that due to a limitation in the get-session-token STS function, you cannot set a duration less than 900 seconds here (15 minutes). Attempting to set a duration less than 15 minutes or greater than 36 hours will result in an error.

Leave a Reply