AWS - Forced MFA and the CLI
Posted on July 20, 2022 (Last modified on July 2, 2024) • 3 min read • 444 wordsLet’s say you want to force every user to enable MFA for AWS IAM accounts. Sure, that is possible.
Now there’s just one problem:
This does not work on the CLI.
The credentials in $HOME/.aws/credentials
are “static”, they never have a aws:MultiFactorAuthPresent
flag attached.
That would mean that CLI users are either not forced to use MFA, or they just can’t use the CLI any more. Which would - for example - break terraform.
There’s a solution though.
Just get a token using aws sts get-session-token --serial-number ... --token-code ...
.
To ease this process (cause it’s really annoying) I created this function, which you can add to your .bashrc
or .zshrc
(or equivalent shell startup script).
Enjoy.
# gat = (G)et (A)ws session(T)oken
gat() {
local TOKEN_DURATION=28800 # 8h
# save existing AWS_* env vars
local APRO_BACKUP
local AKID_BACKUP
local ASAK_BACKUP
local ASET_BACKUP
APRO_BACKUP=$AWS_PROFILE
AKID_BACKUP=$AWS_ACCESS_KEY_ID
ASAK_BACKUP=$AWS_SECRET_ACCESS_KEY
ASET_BACKUP=$AWS_SESSION_TOKEN
# check if we already have a session token active
if [ -n "$AWS_SESSION_TOKEN" ] ; then
if [ "$1" != "-f" ] ; then
echo "Seems we are already using a session token, use -f to override."
return
else
echo "Session token found, but '-f' set; proceeding."
fi
fi
echo -n "AWS profile to use (ENTER for none): "
read AWS_PROFILE
if [ -z "$AWS_PROFILE" ] ; then
echo "Not using specific AWS profile."
unset AWS_PROFILE
else
echo "Using AWS profile '$AWS_PROFILE'"
export AWS_PROFILE
fi
echo -n "Enter MFA token value (ENTER to abort): "
read MFA_TOKEN
if [ -z "$MFA_TOKEN" ] ; then
echo "Abort."
return
fi
# clean env variables, but only if a session token is active
if [ -n "$AWS_SESSION_TOKEN" ] ; then
echo "Cleaning existing ENV vars (already have a session token)"
unset AWS_SESSION_TOKEN
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
fi
echo -n "Getting MFA ARN ... "
MFA_ARN=$(aws iam list-mfa-devices | jq -r '.MFADevices[0].SerialNumber')
if [ "$?" != "0" ] ; then
echo "ERROR: something failed getting the session token."
export AWS_PROFILE=$APRO_BACKUP
export AWS_ACCESS_KEY_ID=$AKID_BACKUP
export AWS_SECRET_ACCESS_KEY=$ASAK_BACKUP
export AWS_SESSION_TOKEN=$ASET_BACKUP
return
fi
echo $MFA_ARN
echo -n "Getting session token ... "
SESSION_TOKEN_JSON=$(aws sts get-session-token --serial-number $MFA_ARN --token-code $MFA_TOKEN --duration-seconds $TOKEN_DURATION)
if [ "$?" != "0" ] ; then
echo "ERROR: something failed getting the session token."
export AWS_PROFILE=$APRO_BACKUP
export AWS_ACCESS_KEY_ID=$AKID_BACKUP
export AWS_SECRET_ACCESS_KEY=$ASAK_BACKUP
export AWS_SESSION_TOKEN=$ASET_BACKUP
return
else
echo "done."
fi
echo "Setting env variables:"
export AWS_ACCESS_KEY_ID=$(echo $SESSION_TOKEN_JSON | jq -r '.Credentials.AccessKeyId')
echo " * (set) AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY=$(echo $SESSION_TOKEN_JSON | jq -r '.Credentials.SecretAccessKey')
echo " * (set) AWS_SECRET_ACCESS_KEY"
export AWS_SESSION_TOKEN=$(echo $SESSION_TOKEN_JSON | jq -r '.Credentials.SessionToken')
echo " * (set) AWS_ACCESS_KEY_ID"
# just to be sure
unset AWS_PROFILE
echo " * (*un*set) AWS_PROFILE"
echo "... done.\n\nYou should be all set now."
}
(Stripped down version available here)