Secure your local AWS Credentials with AWS Vault
In this post, I’ll go through the process of setting up IAM users according to AWS best practices and show how to secure the Access and Secret keys on our local machines to prevent them from various attack vectors. The main tool I will be using is called the AWS Vault. I’ll also show how to make using this tool easier with my setup of iTerm2 and oh-my-zsh.
When you configure your IAM user using the AWS CLI, it stores your credentials in plain text on your machine by default in an \.aws
directory on your home directory. This is common knowledge and can be exploited in cases where your computer gets compromised and all of this can happen without your knowledge. With your credentials, an attacker can use them to do almost anything in your AWS account based on the permissions set on the associated IAM user.
AWS best practices states that you should never use your main account (the account that you used to sign up for AWS) to do any work. Instead, you should create an IAM admin user and give admin access needed for administrative tasks. Also other IAM users and roles should be created with limited access scoped to the tasks performed by these users and roles. This recommendation is widely practiced throughout the industry, but most engineers leave their credentials stored in plain text on their computers. Remember that these IAM admin accounts have full access to your AWS account and in the wrong hands can cause a lot of damage.
Attack vectors can include a stolen laptop, malware that you are not aware of in your computer, and a compromised npm package just to name a few. Each of these threats increases with each new member you add to your team.
AWS Vault securely stores and access AWS credentials in our local setup using our operating system’s secure keystore. It then generates temporary credentials from the keystore and exposes it to our shell and applications, making sure we do not store these credentials in plain text.
AWS Vault Installation
Use this link to find the installation instructions of AWS Vault for your operating system. I use OS X as my primary operating system and the command for my system is:
brew install --cask aws-vault
.
Setup a New Profile
After creating a new IAM user in the AWS Management console, you are presented with a screen with both the Access key ID and Secret access key. These credentials are shown only once and you cannot retrieve them from the AWS console after this step. It is at this point where we will use the AWS Vault tool to securely store these access credentials on our own machine.
Run the following command to create a new profile for a user we will call s3-read-user
. This is an example where we are simulating the process of creating an IAM user that has READ ONLY access to S3. The policies you associate with the IAM user should reflect the level of access you want to give to your users. Always use the principle of least privilege access:
# Use the values from the AWS console for the prompts below:
$ aws-vault add s3-read-user
Enter Access Key ID: aaaaaaa-aaaaaa
Enter Secret Key: bbbbbbb-bbbbbbb
If this is your first time using the AWS Vault tool, it will prompt you to setup a new password for the aws-vault keystore.
The above command sets up a new profile. Visiting the ~/.aws
directory and opening the config
file, you see the profile name listed. But when you open up the credentials
file, it is empty. Previously, it listed the Access and Secret key stored in plain text. The AWS Vault tool stored our credentials encrypted in the OS’s keystore instead of storing it in plain text inside the credentials
file.
Using the AWS Vault: Basic Commands
Now that we have our profile setup using AWS Vault, there are several new commands we need to know and use when we want to interact with our AWS account from the CLI.
The first one is the exec
command which will create temporary credentials that are passed to other commands such as the AWS CLI command. In our case, suppose we have a couple of S3 buckets in our account and want to list all of them, execute the following command passing in the profile name s3-read-user
:
$ aws-vault exec s3-read-user -- aws s3 ls
account_bucket-a
account_bucket-b
account_bucket-c
We can also use the AWS Vault tool to login to an IAM account that is setup with AWS Console login access using the following command:
$ aws-vault login <iam-login-account>
To show a list of all profiles in your computer, including ones that were not stored with the AWS Vault tool, run the following command:
$ aws-vault list
There are other commands to remove profiles, rotate credentials, clear temporary credentials from the secure keystore and more. I believe the above ones should be enough to get you started. Also this tool works with Roles and MFA (multi-factor authentication).
Integration with ZSH and oh-my-zsh
I use the iTerm2 terminal configured with the ZSH shell. I also use the oh-my-zsh framework for my ZSH shell. This should work in any terminal that uses ZSH not just iTerm2. So any Unix-like operating system, MacOS, Linux, BSD and WSL2 on Windows. ‘Oh My Zsh’ is an open source community-driven framework for managing your ZSH configurations. Think of it as a community supported alias package for your terminal.
‘Oh My Zsh’ supports plugins and the plugin we will add is called zsh-aws-vault. It provides many convenient aliases to use when running commands that interact with the configured AWS profiles.
The first step in installing the above plugin is to clone and download it to the plugins directory:
$ cd ~/.oh-my-zsh/custom/plugins
$ git clone [email protected]:blimmer/zsh-aws-vault.git
Next, edit your ~/.zshrc
file and add the new plugin to the plugins line. We will be adding a plugin for AWS Vault. So edit your plugins line to look something like this: (Here, I’m adding AWS Vault in addition to git and dotenv plugins):
plugins=(
git
dotenv
zsh-aws-vault
)
Usage with zsh-aws-vault
Here are the aliases that are added after you install the plugin for zsh:
Alias | Expression |
---|---|
av | aws-vault |
ave | aws-vault exec |
avl | aws-vault login |
avll | aws-vault login -s |
avli | aws-vault login in private browsing window |
avs | aws-vault server |
avsh | aws-vault exec $1 — zsh |
avp | list aws config / role ARNs |
We will use the following command to run the example above where we listed all S3 buckets in our account:
$ ave s3-read-user -- aws s3 ls
account_bucket-a
account_bucket-b
account_bucket-c
Sometimes you might not want to type extra commands each time you want to interact with your AWS account through the CLI. In such cases use the nifty avsh
command to create a shell for a given profile. Then any command you run in that shell will use the associated profile. Using the example above, we can create a shell with the s3-read-user
profile, then run any command without using the ave
or aws-vault
prefixes like we did above:
$ avsh s3-read-user
Now any command you run in that shell will use the associated profile from the previous command:
$ aws s3 ls
account_bucket-a
account_bucket-b
account_bucket-c
If you run the env
command at this point, you will see all the AWS environment variables created in your terminal. You can use the exit
command to go back to your regular shell and remove these variables from your shell environment.
$ env | grep AWS
AWS_VAULT=s3-read-user
AWS_DEFAULT_REGION=us-east-1
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=%%%
AWS_SECRET_ACCESS_KEY=%%%
AWS_SESSION_TOKEN=%%%
AWS_SECURITY_TOKEN=%%%
AWS_SESSION_EXPIRATION=2020-12-29T11:00:00Z
To exit from the temporary shell and back to your normal shell without the AWS environment variables, use the exit command:
$ exit
Add current AWS Profile to Terminal Prompt
The above recommendation and tools to secure AWS credentials works great and with practice can easily become second nature to anyone who uses it consistently. The only issue I have with this setup is that when we have populated our shell with AWS environment variables, we cannot easily tell which profile is currently being used. To fix this issue, the zsh-aws-vault plugin for ‘Oh My Zsh’ has a prompt function called prompt_aws_vault_segment
. Adding this function to your shell prompt will show the current profile whose credentials are in your terminal environment variables.
In my setup, I use a theme that comes with ‘Oh My Zsh’ called miloshadzic.zsh-theme
located in ~/.oh-my-zsh/themes
. To modify the prompt exposed by the theme, I copied it to the custom themes folder in ~/.oh-my-zsh/custom/themes
, and modified the theme file adding a right prompt by adding the following to the end of that file:
RPROMPT='$(prompt_aws_vault_segment)'
Now, when I use the avsh
command, I can tell which profile is being used in the shell by looking at the right prompt. This is very useful and helps me avoid using the wrong profile when running commands in my AWS account.
Rotate Access and Secret Keys
Since the access keys are long-term keys, it is recommended to rotate them periodically. The AWS Console will start showing you warnings after a while if your keys have not been rotated. AWS Vault takes the pain out of rotating keys with this simple command:
$ aws-vault rotate s3-read-user -n
# If using the plugin, use the av alias
$ av rotate s3-read-user -n
Usage with AWS Toolkit for Visual Studio Code
The AWS Toolkit for VSCode is an extension used for working with AWS Services in VSCode. It can make calls to your AWS account and run AWS CLI commands for you. However, it discovers your profiles and uses secrets from the ~/.aws/credential
file. Now if you remember, we don’t have anything in that file because AWS Vault has all our secrets encrypted in our keystore. Hence this tool cannot use our profiles to make authenticated calls to AWS.
However, the AWS CLI config supports sourcing credentials directly from an external process, using credential_process
. We can use this to expose our master credentials (instead of the AWS Vault STS credentials). These credentials can then be used by the AWS Toolkit extension. Add the credential_process
underneath the profile in the ~/.aws/config
file.
[profile s3-read-user]
credential_process = aws-vault exec s3-read-user --json
Now, when you open VSCode with the Toolkit extension installed, you can run the AWS: Connect to AWS
command and select the s3-read-user
profile. The AWS Vault utility will ask for your keystore password and open the profile for use in VSCode.
Note that if you’re using credential_process
in your config you should not use aws-vault exec
on the command line to execute commands directly. The AWS SDK executes aws-vault
for you when you supply a profile with your AWS CLI commands. You can still create a temporary shell with your profile using the avsh <profile>
command and use it to run your AWS CLI commands.
Caveats To Know
AWS Vault issues temporary credentials and these are restricted from using some STS and IAM APIs. To overcome these restrictions, AWS requires these session tokens to be MFA authenticated. To enable MFA for a profile, specify the mfa_serial
in ~/.aws/config
.
If you don’t want to setup MFA, another way to bypass these restrictions is to use the --no-session
flag when running commands that interact with IAM. Use the above flag if you get an error that states the security token is invalid (The security token included in the request is invalid (Service: AmazonIdentityManagement; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 37b49179-ca46-433a-bdaa-044cd5a36c88; Proxy: null)
). Use the following command to pass the --no-session
tag before executing your commands:
# Deploying infra using the AWS CDK.
$ aws-vault exec s3-read-user --no-session -- npx cdk deploy
# Using the zsh-aws-vault plugin
$ ave s3-read-user --no-session -- npx cdk deploy