- read

Using Bitwarden CLI to Manage Multiple AWS Credentials

Tyler Harris 104

An approach for handling AWS Credentials and STS to Assume IAM Roles in other accounts with automatic MFA using secrets stored in Bitwarden, retrieved via the Bitwarden CLI with optional persistence of encrypted BW Session Tokens so they can be reused in other ongoing sessions.

Highlights

  • Single source of truth for related secrets and environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_MFA_ARN, AWS_ARN_STS_<ACCOUNT-NAME>, etc.
  • Secrets are retrieved from the Bitwarden Vault, so they are not persisted on disk in clear text.
  • MFA / 2FA codes are retrieved and injected automatically for AWS Authentication & STS (Security Token Service) to Assume IAM Role.
  • Easy to context switch between various accounts where previous context is implicitly cleared out.
  • Bitwarden CLI Login requires username & password (or api key credentials) along with MFA to login first. Unlocking the vault to initialize a session will still require the Master Password. Includes an approach for persisting an encrypted session token for reuse in other sessions.

Usage

Quick Demo

Logging in with my user account + mfa:

# Show that no AWS environment variables are loaded.
$ printenv | grep 'AWS'
# Execute alias
$ envaws-mymfa
[INFO] [aws-auth] Getting temporary credentials and token for MFA device arn:aws:iam::<accountA>:mfa/<user>
[INFO] [aws-auth] Success!
# Output below from `aws sts get-caller-identity`
{
"UserId": "<access-key-id>",
"Account": "<accountA>",
"Arn": "arn:aws:iam::<accountA>:user/<user>"
}
# Show that the AWS environment variables are loaded.
$ printenv | cut -d= -f1 | grep 'AWS'
AWS_MFA_ARN
AWS_SECRET_ACCESS_KEY
AWS_ACCESS_KEY_ID
AWS_SESSION_TOKEN

Logging in with my user account + mfa and assuming a role within another account:

# Show that no AWS environment variables are loaded.
$ printenv | grep 'AWS'
# Execute alias
$ envaws-mymfa-accountB
[INFO] [aws-auth] Getting temporary credentials and token for MFA device arn:aws:iam::<accountA>:mfa/<user>
[INFO] [aws-auth] Assuming role arn:aws:iam::<accountB>:role/<role-name>
[INFO] [aws-auth] Success!
# Output below from `aws sts get-caller-identity`
{
"UserId": "<access-key-id>:<session-name>",
"Account": "<accountB>",
"Arn": "arn:aws:sts::<accountB>:assumed-role/<role-name>/<session-name>"
}
# Show that the AWS environment variables are loaded.
$ printenv | cut -d= -f1 | grep 'AWS'
AWS_ARN_STS_ACCOUNTB
AWS_MFA_ARN
AWS_SECRET_ACCESS_KEY
AWS_ACCESS_KEY_ID
AWS_SESSION_TOKEN

How it works?

  • Bitwarden secures the credentials as a Vault Item where Environment Variables are also stored as Custom Fields.
  • Aliases are created to pull in Custom Fields for a specific Vault Item from Bitwarden CLI, which uses envwarden.
  • Wrapper functionality added to authenticate with AWS using the Environment Variables set above with optional MFA and Assume Role capabilities using the helper scriptaws-auth .
  • (Optional) For simplifying Bitwarden CLI usage when in multiple terminal/shell sessions and when switching Environments or AWS Auth context often, I unlock my Bitwarden Vault with a small helper script I created — bwl, which will persist an encrypted session token for reuse in other sessions:
$ eval $(bwl)# Alternatively...
$ export "BW_SESSION=$(bw unlock --raw)"

Configuration

Bitwarden Vault Item

Create an Item for the AWS User and register MFA with AWS (see Bitwarden Authenticator TOTP):

  • If you don’t already, consider using a naming convention. I noticed I was getting lost with all the various accounts and contexts I have within AWS, so I adopted a naming convention of my vault items to help distinguish between personal and work (includes a wrkor <client> prefix), user accounts (iam vs root), environments or accounts (prod ,dev, stage, etc.), and users (me/personal vs someservice-account or others): E.g. wrk-aws-iam-prod-me, wrk-aws-iam-dev-me . So the pattern I try to follow is:<client/company>-aws-<iam|root>-<account>-<name>
  • Within the Vault Item, add Custom Fields for each of the related Environment Variables to be set.
    E.g. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_MFA_ARN, AWS_ARN_STS_<ACCOUNT-NAME>, BW_ITEM_NAME, etc.

Note: The BW_ITEM_NAME is the Vault Item Name containing the MFA, which will be used later to automatically fetch TOTP codes. This could be contained in some other Item if you prefer, I like to have an additional Vault Item where I append a -env suffix to the Name, which contains my Environment Variables for the related account that I want sourced in.

Shell Aliases & Functionality

The following contains content from my~/.bash_aliases, which I source in from within my ~/.bashrc:

https://gist.github.com/tdharris/afe2fdf80e70635ed44c2ea920b407db

Dependencies

  • Bitwarden & Bitwarden CLI — Open Source Password Manager.
  • envwarden — used to retrieve Custom Fields from the Bitwarden Vault and set them as Environment Variables: E.g. eval $(envwarden 'vault-item-name')
    Note: At the time of writing this, I found envwarden slow only because it would sync the bw vault every time, so I created PR#8 to include a --skip-sync flag, which works well for me in my use-case.
  • aws-auth — a small helper script which wraps the AWS CLI to simplify authentication when using MFA or Assume Role — made by gruntwork-io.
  • bwl — an optional wrapper script for bw that I created to simplify reuse of the the BW_SESSION token environment variable that is used to unlock the Bitwarden Vault in other ongoing terminal sessions by persisting it with optional encryption using either gpg or keybase . E.g. eval $(bwl)
  • aws-bw — an optional script I created to source AWS Profiles from the ~/.aws/credentials file via the AWS approach for Sourcing credentials with an external process.