Using Multi-Factor Authentication with AWS CLI

Having multi-factor authentication in the web UI is great but I do most of my AWS work via the CLI, so just blocking access to the GUI is not sufficient. In this tutorial, I explain how to apply multi-factor authentication also to your CLI/API users.

NOTE: This article assumes that you've already set up multi-factor authentication for your account. If you haven't but would like to, here's a video that explains how to do it. Once you have multi-factor authentication enabled for the web UI, you can read the rest of this article to learn how to use it for CLI as well.

Create an MFA-required Policy

First, create a multi-factor authentication policy following these steps:

  1. Navigate to My Security Credentials > Policies and click Create Policy create-policy

  2. On the next page, open the JSON tab and copy the following policy into the textbox. This policy denies access to all resources unless the user has been authenticated using multi-factor authentication. Click Review Policy

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "Version": "2012-10-17",
    "Statement": {
    "Sid": "DenyAllWhenMFAIsNotPresent",
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": {
    "BoolIfExists": {
    "aws:MultiFactorAuthPresent": false
    }
    }
    }
    }

    create-policy-2

  3. Give the policy a name, such as mfa-required-policy and an optional description. Click Create Policy.

Understanding the MFA-required Policy

If you're not interested in knowing why the policy we just created works, and just want to start using it, skip the next two paragraphs.

Take a look at this helpful chart. What it tells you is that an explicit deny rule trumps all allow rules. In other words, it doesn't matter what other rules apply; unless the user is authenticated using multi-factor authentication, all their requests will be denied.

This means that it's easy to apply the requirement of multi-factor authentication to all resources, but if you want to be selective things can get a bit complicated. If you want to allow regular authentication by default and single out a few resources or actions for which you require multi-factor authentication, you can simply take the policy and replace the "*" in Action and Resource with whatever you want. However, the reverse is possible. You can't have multi-factor authentication by default and then declare a few resources for which you'd still like to use single-factor authentication. This article in the official documentation has some examples of different, more granular policies.

Attaching the MFA policy to Your User

Now, attach this new policy to a user. You have two options:

  1. Attach the policy directly to a user.
  2. Create a group for multi-factor authenticated users and add your user to that group.

Since we only have a single policy that we want to attach to users, I'm not convinced that creating a group adds a lot of value. Below, I'm going to show you how to do option number 1.

  1. In the My Security Credentials view select the Users pane from the left-hand sidebar.

  2. Select your user from the list by clicking their name and click the big blue button that reads Add Permissions.

  3. On the Grant Permissions page, select Attach Policies Directly, look up the policy we just created from the list and tick the checkbox next to it. Then click Next: Review. attach-policies-1

  4. Click Add Permissions.

To test that the policy is working try using the CLI for anything your user should normally have permissions to. You should get back an Access Denied message. Below, I'm trying to list the s3 buckets on my account:

1
2
3
aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

Great, so now I can't do anything! Let's learn how to use MFA-tokens to get around the policy we just installed.

Acquiring Session Credentials Using AWS CLI and MFA token.

AWS uses two types of credentials:

  • long-term credentials The normal AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
  • short-term credentials Temporary credentials that are generated from your long-term credentials and, in this case, a multi-factor authentication token.

What AWS let's you do is use your MFA token to request short-term credentials. You can then use these short-term credentials to access anything the long-term credentials for that user would permit except with MFA restrictions removed.

Here's how to request short-term credentials using the CLI. The command you need is aws sts get-session. It takes two parameters:

  • serial-number: Serial number is an identification code for your hardware or virtual ARN device. If you've set up your user with a device, you can find the code in the web console. mfa-device-arn
  • token-code: This is the one-time code you get from your MFA device.

Once you have these two parameters, you can request short-term credentials like this:

1
2
3
`aws sts get-session-token \
--serial-number [mfa-device-code] \
--token-code [mfa-token]`

If all goes well, you should get back something like the following:

1
2
3
4
5
6
7
8
{
"Credentials": {
"SecretAccessKey": "fab1/qUWBUK2ikblxXX2GEciIqCQ7212NDqvEqJZ",
"SessionToken": "FQoDYXdzEDEaDMIZdvd2aWTm5HrOUyKwAYaVy3Y/IFuba+8g/FgduLoG8IboJ92dtz3stalpLkEsEnGWvrlc025SFjypJahSgyIG8/Pp6YUQADrqmjpz/XAeaut8AFX9n25qbxBZcHYqlH5DW7yXtyyabsKahrZtE9ee+h2eVmr0P8EK2sGeM5cuVSo19f49Bap8iAfPG10FBawZGuv57uUdbbVVMxYe6nvJUNv8ughphO0MNkIiXW1Kw4DAV6APpYlpncs0JXlVKPObidYF",
"Expiration": "2018-04-03T03:36:19Z",
"AccessKeyId": "ASIAJASCXGQWXJN7OP4Q"
}
}

By default, these credentials expire in 12 hours. You can change that by passing in a --duration-seconds option to get-session-token command.

Using Short-Term Credentials

To actually use the short-term credentials, you have two options.

  1. Export them as environment variables
    1
    2
    3
    export AWS_ACCESS_KEY_ID=<Access-Key-as-in-Previous-Output>
    export AWS_SECRET_ACCESS_KEY=<Secret-Access-Key-as-in-Previous-Output>
    export AWS_SESSION_TOKEN=<Session-Token-as-in-Previous-Output>

Now, when you use the CLI it will default to those credentials. Try issuing a command that your user has permissions to, such as the aws s3 ls I did earlier.

  1. Add them to your credentials file
    1
    2
    3
    4
    [profile-name]
    aws_access_key_id = <Access-key-as-in-returned-output>
    aws_secret_access_key = <Secret-access-key-as-in-returned-output>
    aws_session_token = <Session-Token-as-in-returned-output>

Now you can use these credentials by passing in the --profile option. For example: aws --profile profile-name s3 ls.

Obviously, neither of these two methods is convenient if you use multi-factor authentication every day. That's why I wrote this simple program that allows me to quickly request MFA credentials and then start using them right away. Check it out and see if you like it.

There's also a project called broamski/aws-mfa, which gives you many more options. I haven't used it personally, but it looks like a reasonable solution for someone who finds my simple script too limiting.

As always, I hope this article was helpful. If you have questions or feedback, don't hesitate to leave a comment or shoot me an email.