You are here
Getting and Using Session Tokens with MFA in the AWS CLI
Securing your AWS IAM user with multi factor authentication (MFA) is a good idea. However, when working on the command line interface (CLI), the need to enter changing token codes creates some overhead. This article shows how to use MFA in the CLI.
Enabling MFA
This documentation shows how to enable MFA. With these settings, logins to the web console via the browser will only work with a code from your MFA device.
To also force IAM users to having to enter an MFA token code when using the CLI, set the IAM policy as shown here.
With this setup, any command given to the AWS CLI will fail without a proper token code:
$ aws eks list-clusters An error occurred (AccessDeniedException) when calling the ListClusters operation: User: arn:aws:iam::000000000012:user/yourusername is not authorized to perform: eks:ListClusters on resource: arn:aws:eks:eu-central-1:000000000012:cluster/* with an explicit deny
Requesting and Setting Credentials Manually
Here is how to get a token and use it in every request. This process is also shown here on YouTube.
First, find the amazon resource name (ARN) of your MFA device. Go to the user table in IAM service and select your user. The tab "Security credentials" shows the ARN at "Assigned MFA device".
With that, a token can be requested from the Security Token Service (STS), using one of the codes from your MFA device:
$ aws sts get-session-token --serial-number arn:aws:iam::000000000012:mfa/yourusername --token-code 123456 { "Credentials": { "AccessKeyId": "A...2", "SecretAccessKey": "x...F", "SessionToken": "IQ....DZS", "Expiration": "2022-02-27T22:10:04+00:00" } }
To make following commands in the AWS CLI use this token, it can be exported in the environment variables like this:
$ export AWS_ACCESS_KEY_ID=A...2 $ export AWS_SECRET_ACCESS_KEY=x...F $ export AWS_SESSION_TOKEN=IQ...DZS
Here is a good article about environment variables in Linux.
A request made within the lifetime of that token will now succeed:
$ aws eks list-clusters { "clusters": [ "my-cluster-name-here" ] }
Requesting and Setting Credentials By Script
To ease the process demonstrated above, I wrote two scripts.
The first script, aws-unset-mfa-access-token.sh, removes the three variables from the environment:
#!/bin/bash if [ ${BASH_SOURCE[0]} == ${0} ] then SOURCED=false else SOURCED=true fi while getopts ":h" option; do case $option in h) # Display help echo "This will unset the following environment variables:" echo echo " AWS_ACCESS_KEY_ID" echo " AWS_SECRET_ACCESS_KEY" echo " AWS_SESSION_TOKEN" echo echo "To be able to change environment variables, this script has to be executed with 'source' like this:" echo "source ./aws-unset-mfa-access-token.sh" if [ $SOURCED = true ] then return else exit fi ;; esac done if [ $SOURCED = false ] then echo "You are not running this script as source." echo "To make these changes permanent, call this script source'd like this:" echo "source ./aws-unset-mfa-access-token.sh" exit fi echo "Removing AWS_ACCESS_KEY_ID from environment variables ..." unset AWS_ACCESS_KEY_ID echo "Removing AWS_SECRET_ACCESS_KEY from environment variables ..." unset AWS_SECRET_ACCESS_KEY echo "Removing AWS_SESSION_TOKEN from environment variables ..." unset AWS_SESSION_TOKEN
The second script, aws-set-mfa-access-token.sh, requests new credentials from the STS as shown above and sets them in the environment variables:
#!/bin/bash if [ ${BASH_SOURCE[0]} == ${0} ] then SOURCED=false else SOURCED=true fi while getopts ":h" option; do case $option in h) # Display help echo "This will call AWS STS with your current credentials, get a temporary access token and set this token with the following environment variables:" echo echo " AWS_ACCESS_KEY_ID" echo " AWS_SECRET_ACCESS_KEY" echo " AWS_SESSION_TOKEN" echo echo "Usage:" echo echo "aws-set-mfa-access-token MFA_SERIAL_NUMBER TOKEN_CODE" echo echo "Example:" echo echo "aws-set-mfa-access-token arn:aws:iam::000000000042:mfa/youruser 424242" echo echo "This will cause every request to be send with this access token." echo echo "To be able to change environment variables, this script has to be executed with 'source' like this:" echo "source ./aws-set-mfa-access-token.sh" if [ $SOURCED = true ] then return else exit fi ;; esac done if [ $SOURCED = false ] then echo "You are not running this script as source." echo "To make these changes permanent, call this script source'd like this:" echo "source ./aws-set-mfa-access-token.sh" exit fi if [ $# -lt 2 ] then echo "Please provide two arguments:" echo "1. MFA_SERIAL_NUMBER" echo "2. TOKEN_CODE" return fi echo "Checking if environment variables are set ..." echo if [ -z ${AWS_ACCESS_KEY_ID+x} ]; then echo "AWS_ACCESS_KEY_ID is unset"; AWS_ACCESS_KEY_ID_ALREADY_SET=false else echo "AWS_ACCESS_KEY_ID is set to '$AWS_ACCESS_KEY_ID'"; AWS_ACCESS_KEY_ID_ALREADY_SET=true fi if [ -z ${AWS_SECRET_ACCESS_KEY+x} ]; then echo "AWS_SECRET_ACCESS_KEY is unset"; AWS_SECRET_ACCESS_KEY_ALREADY_SET=false else echo "AWS_SECRET_ACCESS_KEY is set to '$AWS_SECRET_ACCESS_KEY'"; AWS_SECRET_ACCESS_KEY_ALREADY_SET=true fi if [ -z ${AWS_SESSION_TOKEN+x} ]; then echo "AWS_SESSION_TOKEN is unset"; AWS_SESSION_TOKEN_ALREADY_SET=false else echo "AWS_SESSION_TOKEN is set to '$AWS_SESSION_TOKEN'"; AWS_SESSION_TOKEN_ALREADY_SET=true fi echo if [ $AWS_ACCESS_KEY_ID_ALREADY_SET = true ] || [ $AWS_SECRET_ACCESS_KEY_ALREADY_SET = true ] || [ $AWS_SESSION_TOKEN_ALREADY_SET = true ]; then echo "The already existing variables will be overriden. Continue?" read -p "Continue? (Y/N): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || return else echo "No existing variables set. Will continue ..." fi echo "Getting new MFA access token ..." CREDJSON="$(aws sts get-session-token --serial-number $1 --token-code $2)" ACCESSKEY="$(echo $CREDJSON | jq '.Credentials.AccessKeyId' | sed 's/"//g')" SECRETACESSKEY="$(echo $CREDJSON | jq '.Credentials.SecretAccessKey' | sed 's/"//g')" SESSIONTOKEN="$(echo $CREDJSON | jq '.Credentials.SessionToken' | sed 's/"//g')" echo echo "AccessKeyId:" echo $ACCESSKEY echo "SECRETACESSKEY:" echo $SECRETACESSKEY echo "SESSIONTOKEN:" echo $SESSIONTOKEN echo echo "Setting values as environment variables ..." export AWS_ACCESS_KEY_ID=$ACCESSKEY export AWS_SECRET_ACCESS_KEY=$SECRETACESSKEY export AWS_SESSION_TOKEN=$SESSIONTOKEN echo "Done."
Both scripts come with a help page and some user friendliness such as input validation and informative output. They can be downloaded at this GitHub repository.
Note: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN could also be added to the credentials file stored in the .aws folder. However, Amazon recommends using the environment variables. Also, there is no easy way adding the values to the credentials file via the AWS CLI.