Walkway harness on a Redwood tree in Redwoods Tree Walk, Rotorua

It is a good practice to use temporary credentials in AWS instead of permanent IAM generated credentials that are long term and potentially leaked. AWS has different services that issue temporary credentials with given permission policies such as IAM Instance Profiles for EC2 instances, Lambda Execution Roles, ECS Task Roles, and IAM Roles Anywhere for those cases that require providing trusted access permissions to a process running outside of AWS.

One such way is to use the GenerateSessionToken API action in AWS Security Token Service to generate a pair of temporary credentials with an expiry date. Doing so allows using AWS credentials without having to hardcode long lived IAM credentials and worry about the credentials getting leaked.

However, there’s a caveat when it comes to using credentials generated from GenerateSessionToken action. Unless the API action is executed with the MFA token (for MFA enabled users), the generated credentials cannot be used to invoke any IAM API calls. This would put a blocker for any use case you had for running (ex:) IaC scripts with the temporary credentials.

I came across this scenario when using the popular aws-vault tool generate sessions with temporary credentials. aws-vault also uses GenerateSessionToken, but it depends on the AWS configuration file ($HOME/.aws/config) to provide the MFA configuraiton to it. Without the MFA device serial and the token generation process defined in the config, it was generating credentials that couldn’t perform any of the IAM API operations I needed.

One of the best ways to enable strong second factor authentication in AWS is to use a Yubikey device. However, the second factor authentication protocols supported by the AWS web console isn’t the same as the one supported by the AWS CLI and the SDKs. AWS web console supports FIDO-U2F, while the CLI and SDKs, based on the configuration, supports OATH-TOTP.

To get this working on the AWS CLI (and other tools that use the standard AWS SDKs), couple of steps need to be executed.

  1. Setup the CLI tool to generate the OATH-TOTP from the Yubikey
  2. Configure AWS configuration to point the MFA generation process to the OATH-TOTP generation tool

Prerequisites

  1. Supported Yubikey device
  2. AWS CLI or SDK based script
  3. AWS credentials with proper permissions to invoke GetSessionToken

The following assumes an Ubuntu Linux environment, however other Linux distributions will also have the dependencies required for the Yubikey CLI tool.

Installing ykman

ykman is the Yubico provided CLI key management tool. Installing this on Ubuntu is straightforward.

# Install the dependencies needed
sudo apt update && \
  sudo apt install pcscd libpcsclite-dev swig

# Install ykman
pip3 install yubikey-manager

Configuring MFA with OATH-TOTP

This step involves the following.

  1. Registering a new Virtual MFA device in AWS IAM
  2. Usig the Secret Key used in the process to register a new oath account in ykman
  3. Configuring AWS config to use ykman as the command to generate the OATH-TOTP MFA

Navigate to the AWS IAM console and select the user that needs to register the MFA device for CLI authentication. Select the Security Credentials tab and then on Assign MFA Device button. Select Authenticator App as the device and input a device name. Note down this name as we’ll be using it to register the device on the ykman end.

Adding a Virtual MFA device

Proceed to the next step. AWS provides a QR code for an authenticator app to use, however for this use case, what’s useful is the secret key. Copy the secret key for the next step.

Retrieving the secret key

Open the terminal and execute the following command using the appropriate values for account_id and the device_name (ex: ykman_test).

ykman oath accounts add -t arn:aws:iam::account_id:mfa/device_id

ykman will ask to input the (base32 encoded) secret key. Note that the value copied from the AWS console is already base32 encoded so just directly paste the value. This also specifies that the Yubikey should be touched to generate the key by using the -t flag.

Registering the device in ykman

Now two consecurtive OATH-TOTP codes should be generated and added to the console to finalise the device registration. However, instead of doing this immediately, I’ve experienced success by waiting a while (~5 minutes) for some unknown reason. I’m guessing this is related to the time window being populated someway, however whenever I’d immediately generate tokens, AWS would report them as invalid.

To generate a OATH-TOTP code, run the following command, again replacing account_id and device_id with proper values. Wait 30 seconds before the next token is generated since the time slice configuration is usually set to 30 seconds.

ykman oath accounts code --single arn:aws:iam::account_id:mfa/device_id

Generating 2 consecutive TOTP codes

Now that the device is registered in ykman and AWS, it can be directly used when invoking GetSessionToken action.

For the CLI command get-session-token to use MFA in the process (in an AWS context with proper permissions) the flags --serial-number and --token-code have to be used, which will trigger the touch request. Not providing these two flags will generate credentials without MFA.

The MFA device serial can be copied from the AWS IAM console.

MFA device ARN in the AWS IAM console

aws sts get-session-token --serial-number arn:aws:iam::account_id:mfa/device_id  \
  --token-code $(ykman oath accounts code --single arn:aws:iam::account_id:mfa/device_id)

Running get-session-token with MFA directives

Additionally, for AssumeRole operation, the MFA device and the code generation command to use can be provided through the AWS config file. To do this add mfa_serial and mfa_process directives under the proper profile in $HOME/.aws/config file (replacing account_id and device_id with proper values).

[profile ccl-test]
region=ap-southeast-2
mfa_serial=arn:aws:iam::account_id:mfa/device_id
mfa_process=ykman oath accounts code --single arn:aws:iam::account_id:mfa/device_id

That’s it! That’s the entire process.

Now whenever temporary credentials are generated through AssumeRole, the AWS SDK credentials provider will use ykman with the above command to trigger a Yubikey touch to generate an OATH-TOTP token, ensuring the temporary credentials are generated with MFA involved.