Use AWS Roles and Secure Your Access Keys with MFA

I hope by now everyone who uses AWS web console has enabled MFA. That means for both root account and for all IAM users.

I think that alone is not enough. We all use api-tokens (Access Keys) attached to our users to be able to use different cli tools from aws-cli to Terraform. This api-tokens are not protected by MFA and they can be. Also their access does no have any expiry and again they can have.

Just imagine you do a series of mistakes and someone gets your access key credentials. You are done.

You can reduce that risk by using AWS STS and assuming roles. It is easy to setup and you can protect your access by MFA. As a bonus you will also have a hard-limit of maximum one hour for any role you assume before you need to authenticate again.

You need to have your aws-cli installed. Then using web-console create your temporary account that will do the rest of setup.

First create an account. You only need to give it Programmatic access
For now give that user a full access to IAM so it can do the rest of setup for you

At the end copy your credentials into your aws config. It is normally at ~/.aws/credentials.

I have the rest of the code at my github.com/kaveh/aws-setup.I assume you will use it or you just review and change that code to what you like.

RolesList group

This will be the group which allows listing of current roles for users. After setup is complete, users can list available roles and try to assume them if they have enough access.

$ cat << EOF  > roles_list.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1483900802000",
"Effect": "Allow",
"Action": [
"iam:GetRoles",
"iam:GetRolePolicy",
"iam:ListRoles"
],
"Resource": [
"arn:aws:iam::*"
]
}
]
}
EOF
$ aws iam create-group --group-name roles_list
$ aws iam put-group-policy --group-name roles_list --policy-document file://roles_list.json --policy-name roles_list

User

For each user you need these commands

$ aws iam create-user --user-name myuser
$ aws iam add-user-to-group --user-name myuser --group-name roles_list
# Now you will create a profile for your user to access aws console through web. First generate a skeleton to fill and then create the profile.
$ aws iam create-login-profile --generate-cli-skeleton > create-login-profile.json
$ aws iam create-login-profile --cli-input-json file://create-login-profile.json

Now share the temporary access credentials with your user. You should already force MFA policy for all your accounts. So user needs to reset his password and enable MFA. Then user can create a access key.

Next you need to setup your roles and ask user to assume them using his access-key and MFA.

Roles

For each role you need an “assume policy document”. This policy will say who is trusted to assume the role. We will also enforce presence of MFA for assuming the role.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::999999999999:user/myuser"
]
},
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}

Save that file in current directory as supervisor.json.

Now create a “supervise” role:

$ aws iam create-role --role-name supervise --assume-role-policy-document file:///supervisor.json
$ aws iam attach-role-policy --role-name supervise --policy-arn arn:aws:iam::aws:policy/IAMFullAccess

That is all. You can continue to create new roles and add trusted users to assume them in your “assume-role-policy-document” similar to supervisor.json.

How to Use Roles

Now your devops team need to assume these roles. They should have their aws-cli setup and their MFA enabled.

I have a simple script as a helper. Using it you can list and assume roles.

$ source <(curl -s https://raw.githubusercontent.com/kavehmz/dev/master/home/share/aws_assumerole.sh)
$ assume list 
arn:aws:iam::999999999999:role/dailyoperation
arn:aws:iam::999999999999:role/supervise
$ assume supervise myuser 647895
Assuming role of arn:aws:iam::999999999999:role/supervise until [2016-12-29T18:44:20Z]

From last command onward whatever I do will have “supervise” access. But this time ONLY for one hour and if I want to assume the role I need to have a MFA code as you see.

# Wrong 2FA and my Token is useless
$ assume supervise myuser 111111
An error occurred (AccessDenied) when calling the AssumeRole operation: MultiFactorAuthentication failed with invalid MFA one time pass code.
Fail to assume the role

This is a just a sample of what you can do. I have some simple setup here:

You get the idea. Implement it preferably using Terraform or another standard method.

That all.

Stay safe.