Using AWS CLI with MFA enabled everywhere is cumbersome. This attempts to make it easier.
LastPass account and CLI.
Gruntwork.io aws-auth script.
AWS CLI to interact with your AWS account assets. The whole reason we're doing this.
jq which Gruntwork.io uses to parse json within their script.
To install the script, you can either copy it manually to a location on your PATH
or use the
gruntwork-install command:
gruntwork-install --module-name 'aws-auth' --repo 'https://github.com/gruntwork-io/module-security' --tag 'v<version-number>'
WARNING! Before running the following commands, authenticate to the AWS account that contains your IAM User using your static API Access Key ID and Secret Key.
These instructions are built for LastPass but can be adopted to be used with
1Password or pass to store any static credentials so they don't sit
unencrypted on your local disk. Direct link to pass
instructions from Gruntwork.io can be found
here.
aws-auth --serial-number arn:aws:iam::123456789012:mfa/jondoe --token-code 123456
Find the Serial Number ARN by viewing your IAM User profile in the AWS web console.
aws-auth --serial-number arn:aws:iam::123456789012:mfa/jondoe --token-code 123456 --role-arn arn:aws:iam::123456789012:role/my-role
You must have the iam:AssumeRole
permission on the "primary" AWS account in order to assume an IAM Role in a "secondary"
AWS account. Furthermore, you must have the iam:AssumeRole
permission on the specific IAM Role you wish to assume or
on all resources (*
).
When finished running, the aws-auth
script will write a series of export XXX=YYY
statements to stdout
:
aws-auth --serial-number arn:aws:iam::123456789012:mfa/jondoe --token-code 123456
export AWS_ACCESS_KEY_ID='AAA'
export AWS_SECRET_ACCESS_KEY='BBB'
export AWS_SESSION_TOKEN='CCC'
To setup your AWS environment variables in one command, all you have to do is eval the result!
eval "$(aws-auth --serial-number arn:aws:iam::123456789012:mfa/jondoe --token-code 123456)"
A typical account switching workflow might be:
- Authenticate to "primary" AWS account using static credentials
- Use
aws-auth
to authenticate to "dev" account. - Authenticate to "primary" AWS account using static credentials
- Use
aws-auth
to authenticate to "prod" account.
Notice that you must re-authenticate to the "primary" AWS account before you can use aws-auth
again.
To be fair, using aws-auth
isn't really a one-liner, since you have to set your permanent AWS credentials first:
export AWS_ACCESS_KEY_ID='<PERMANENT_ACCESS_KEY>'
export AWS_SECRET_ACCESS_KEY='<PERMANENT_SECRET_KEY>'
eval $(aws-auth --serial-number arn:aws:iam::123456789011:mfa/jondoe --token-code 123456)
If you store your secrets in a CLI-friendly password manager, such as lpass, then you can reduce this even further!
First, store your permanent AWS credentials in lpass
. I'm sure there are multiple ways this can be done but I created a
custom note and stored all user account information in it.
- Open your LastPass Vault > Secure Notes > click on the (+) sign > Add Secure Note.
- Change "Note Type:" to "Add Custom Template".
- Give it a name like "AWS Security Credentials".
- Start adding fields.
- Save the template when complete.
Fields I used were:
Field Name | Field Type |
---|---|
User ID (IAM) | Text |
Account Name | Text |
Account Number | Text |
Access Key ID | Text with copy button |
Secret Access Key | Text with copy button |
MFA ARN | Text with copy button |
Once that's complete, create a second note used to for storing lines of the script.
- Open your LastPass Vault > Secure Notes > click on the (+) sign > Add Secure Note.
- Change "Note Type:" to "Add Custom Template".
- Give it a name like "Script".
- Start adding fields.
- Save the template when complete.
Fields I used were:
Field Name | Field Type |
---|---|
Param1 | Text |
Param2 | Text |
Param3 | Text |
Param4 | Text |
Create a new Secure Note using the "AWS Security Credentials" template. Store your User ID ARN, Account Name, Account Number,
Access Key ID, Secret Access Key, and MFA ARN in that. Custom templates can't be added from the lpass
cli so this has to be
done within the LastPass GUI.
$ lpass show aws-johndoe
Foldername/aws-johndoe [id: 1234567890123456]
MFA ARN: arn:aws:iam::123456789012:mfa/johndoe
Secret Access Key: JOHNDOERANDOMSECRETACCESSKEY
Access Key ID: JOHNDOEACCESSKEYID
Account Number: 123456789012
Account Name: Security
User ID (IAM): arn:aws:iam::123456789012:user/johndoe
NoteType: Custom
If you will be assuming an IAM Role ARN, put that in lpass
too:
lpass add aws-johndoe-role-arn-otheraccount
Username: eval $(AWS_ACCESS_KEY_ID=$(lpass show aws-johndoe --field "Access Key ID") AWS_SECRET_ACCESS_KEY=$(lpass show aws-johndoe --field "Secret Access Key") aws-auth --serial-number $(lpass show aws-johndoe --field "MFA ARN") --token-code "$token" --role-arn $(lpass show aws-johndoe-role-arn-otheraccount --password))
Password: arn:aws:iam::098765432109:role/role-name
Note: For the IAM Role ARNs, I'm using the default lpass
site template since I only need two fields and I can access it quickly with the CLI. The script with the --role-arn
flag is saved as Username
and the Role ARN is being saved as Password
.
Now, we can start constructing our script in lpass
that ties all of this together. Again, since this is a custom template, everything has to
be done within the LastPass console. Create a new Secure Note using the "Script" template.
In the Param1 field, copy in:
read -p "Enter your MFA token: " token
Note: The other ParamX fields were added for future use and not necessarily for this script.
$ lpass show aws-auth-security
Foldername/aws-auth-security [id: 7056520004343215957]
Param4:
Param3:
Param2: eval $(AWS_ACCESS_KEY_ID=$(lpass show aws-johndoe --field "Access Key ID") AWS_SECRET_ACCESS_KEY=$(lpass show aws-johndoe --field "Secret Access Key") aws-auth --serial-number $(lpass show aws-johndoe --field "MFA ARN") --token-code "$token")
Param1: read -p "Enter your MFA token: " token
NoteType: Custom
Now, to setup your temporary STS credentials so it is truly a one-liner!
eval "$(lpass show aws-auth-security --field Param1; lpass show aws-auth-security --field Param2)"
Example
$ eval "$(lpass show aws-auth-security --field Param1; lpass show aws-auth-security --field Param2)"
Enter your MFA token: 123456
2018-01-08 16:35:18 [INFO] [aws-auth] Getting temporary credentials and token for MFA device arn:aws:iam::123456789012:mfa/johndoe
2018-01-08 16:35:19 [INFO] [aws-auth] Success!
But that's still a lot of typing. How about we alias that and all the additional IAM Role ARNs possibilities? I keep all my aliases defined in ~/.bash_aliases
.
Note: Remember that aws-auth-otheraccount
requires we specify the --role-arn
so we can switch to that role/account. In this example, all
of that is stored in lpass
as the secret aws-johndoe-role-arn-otheraccount
. The Username
field contains the script and the Password
field
contains the Role ARN.
For every AWS account used in your organization, you'll need to create that additional secret and that BASH alias if you're going to follow along with this.
Remember to adjust your alias
scripts as neccessary.
# Authenticate to the AWS account with the User ID
alias aws-auth-security='eval "$(lpass show aws-auth-security --field Param1; lpass show aws-auth-security --field Param2)"'
# Authenticate to the AWS account with the role arn
alias aws-auth-otheraccount='eval "$(lpass show aws-auth-security --field Param1; lpass show aws-johndoe-role-arn-otheraccount --username)"'
Example
$ aws-auth-security
Enter your MFA token: 123456
2018-01-09 21:14:18 [INFO] [aws-auth] Getting temporary credentials and token for MFA device arn:aws:iam::123456789012:mfa/johndoe
2018-01-09 21:14:19 [INFO] [aws-auth] Success!
$ aws-auth-outheraccount
Enter your MFA token: 123456
2018-01-09 21:14:41 [INFO] [aws-auth] Getting temporary credentials and token for MFA device arn:aws:iam::123456789012:mfa/johndoe
2018-01-09 21:14:42 [INFO] [aws-auth] Assuming role arn:aws:iam::098765432109:role/rolename
2018-01-09 21:14:42 [INFO] [aws-auth] Success!
$ aws sts get-caller-identity
{
"UserId": "AWSRANDOMUSERID:johndoe",
"Account": "098765432109",
"Arn": "arn:aws:sts::098765432109:assumed-role/rolename/johndoe"
}
Note: the double quotes around the $()
are required.
I created a script to ease adding new accounts to lpass
and BASH aliases. It assumes that private - .monkeydotfiles is being used. Run ./add-account --help
for more information.
- LastPass with AWS, since this is being used internally as well.
- LastPass, for creating a CLI.
- Gruntwork.io, for creating the initial script and instructions.