AWS CLI with MFA

February 26, 2022

Something you know and something you have is the base for multi-factor authentication. We already restricted our users to access the AWS console with MFA, but what about their programmable access to the cloud from their local machines?

Sometimes developers and DevOps engineers need interactive (CLI) or script-based access to live cloud environments from a remote machine. A good example is accessing an EC2 instance remotely using an SSM session for manual configuration or for debugging purposes.

It’s important to note that restricting user access to the AWS console with MFA has no effect on the ability of the user to call AWS APIs using his local AWS credentials without providing any additional authentication tokens. A user with a permissive role storing her AWS keys on her local machine is a potential security breach.

The base for MFA authentication with AWS CLI is to request session AWS keys from GetSessionToken API.

Doing it from the CLI has the following form:

 aws sts get-session-token --serial-number "[MFA device ARN]" --token-code [6 digits token]

The challenge now is to get the ARN for the user’s MFA device. Assuming the user has a single MFA device, here’s how we fetch it:

MFA=$(aws iam list-mfa-devices | jq '.MFADevices[0].SerialNumber' --raw-output)

TOKEN=$(aws sts get-session-token --duration-seconds 900 --serial-number $MFA --token-code 123456)

Last, we parse the AWS access key and secret key from the token response and set them as environment variables to be used by the following AWS CLI commands

export AWS_ACCESS_KEY_ID=$(echo $TOKEN | jq ".Credentials.AccessKeyId")

export AWS_SECRET_ACCESS_KEY=$(echo $TOKEN | jq ".Credentials.SecretAccessKey")

export AWS_SESSION_TOKEN=$(echo $TOKEN | jq ".Credentials.SessionToken")

From permissions side, we ensure the user is using the MFA token when accessing AWS APIs and resources. We do that with the aws:MultiFactorAuthPresent condition in the user allowed actions of her IAM user policies (or even better - on the IAM group she’s part of).

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iam:ListMFADevices"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*"
      ],
      "Resource": "*",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

With the above IAM policy, the user is allowed to run any EC2 describe (such as aws ec2 describe-instances) API only with MFA generated credentials


Profile picture

Written by Assaf Kamil who lives and breathes the cloud, server-side, frontent, DevOps. You should follow them on Twitter