Setup AWS CLI
The goal of setting up secure users to interact with the AWS CLI is for tokens to be temporary; permanent tokens run the risk of being leaked.
To do this, you will need to start by creating an IAM user. This user will need to have an MFA device assigned to it and at least one policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RequireMFA",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
This policy will deny all action requests to all resources unless the authentication session includes MFA.
Note
In AWS IAM, when you attach multiple policies to a user that have conflicting permissions, the permissions are evaluated using an explicit deny, implicit deny, and explicit allow mechanism.
- Explicit Deny: If there's an explicit deny in any policy, it takes precedence, and the action is denied.
- Explicit Allow: If there's no explicit deny, an explicit allow in any policy will permit the action.
- Implicit Deny: If there's no explicit deny or allow, the action is implicitly denied.
So at this point, this user has zero access to anything in the account - good! You can now add any additional policies; accesses that you want the user to have after they authenticate with MFA.
Next you want save this bash script to your machine (replace the values in curly brackets):
#!/bin/bash
REGION="us-west-2"
USER="{IAM User Name}"
MFA_DEVICE_ARN="{IAM User MFA Device ARN}"
TEMP_PROFILE_NAME="{Desired Profile Name}"
DURATION="900" # Optional; you can specify the duration in seconds
# Get the temporary session token and credentials
CREDENTIALS=$(aws \
sts get-session-token \
--profile $USER \
--serial-number $MFA_DEVICE_ARN \
--token-code $1 \
--duration-seconds $DURATION)
# Extract the credentials
ACCESS_KEY=$(echo $CREDENTIALS | jq -r '.Credentials.AccessKeyId')
SECRET_KEY=$(echo $CREDENTIALS | jq -r '.Credentials.SecretAccessKey')
SESSION_TOKEN=$(echo $CREDENTIALS | jq -r '.Credentials.SessionToken')
# Update or create profile in ~/.aws/credentials
aws configure set aws_access_key_id "$ACCESS_KEY" --profile "$TEMP_PROFILE_NAME"
aws configure set aws_secret_access_key "$SECRET_KEY" --profile "$TEMP_PROFILE_NAME"
aws configure set aws_session_token "$SESSION_TOKEN" --profile "$TEMP_PROFILE_NAME"
# Update or create profile in ~/.aws/config
if grep -q "^\[profile $TEMP_PROFILE_NAME\]" ~/.aws/config; then
# Profile exists, update the region
sed -i "/^\[profile $TEMP_PROFILE_NAME\]/,/^\[profile\]/ s/^region = .*/region = $REGION/" ~/.aws/config
else
# Profile doesn't exist, append to file
echo "[profile $TEMP_PROFILE_NAME]
region = $REGION" >> ~/.aws/config
fi
echo "Temporary profile $TEMP_PROFILE_NAME created or updated successfully."
When you run this script, you want to pass it the MFA Token code as an argument, and it will generate a session token and temporary credentials.
Now when you run AWS CLI commands, you can just specify --profile {Desired Profile Name} and it will use the temporary credentials, which use the policies attached to the user, but are not MFA authenticated.
Note
Pro Tip: Instead of attaching the "Deny All" policy to the permission of the user, you should set it as the permission boundary policy because it delegates maximum control and restrict permissions
Note
Double Pro Tip: Instead of attaching the "Deny All" policy to every user, you can utilize AWS Organizations and create a Service Control Policy (SCP) and apply it at the account-level. This way it's automatically applied to all users created, with no chance of mistake. That way all users MUST authenticate with MFA to have access to any resources.
Here is an example of the Double Pro Tip SCP, which excludes the root account