Skip to content

Instantly share code, notes, and snippets.

@ei-grad
Created February 27, 2024 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ei-grad/3a7e4dad8ef5ff8871fc1ccd9a00bdb7 to your computer and use it in GitHub Desktop.
Save ei-grad/3a7e4dad8ef5ff8871fc1ccd9a00bdb7 to your computer and use it in GitHub Desktop.
Maintain AWS temporary credentials with Yubikey as MFA and `pass` to store static secret key
#!/usr/bin/env python
"""
This script is used to refresh the session token for the AWS CLI.
It maintains credentials in the AWS_SHARED_CREDENTIALS_FILE file, and
refreshes the session token when it is about to expire.
It relies on the following tools:
- pass: a password manager
- ykman: a Yubikey manager
Required environment variables:
- AWS_SHARED_CREDENTIALS_FILE: the path to the credentials file
- AWS_ACCESS_KEY_ID: the access key id
- PASS_AWS_SECRET_ACCESS_KEY: the pass entry for the secret access key
- AWS_MFA_SERIAL: the MFA serial number
- YUBIKEY_MFA_ACCOUNT: the name of the Yubikey MFA account
"""
import os
import sys
import logging
from datetime import datetime, timedelta, timezone
from subprocess import check_output
import boto3
aws_shared_credentials_file = os.environ['AWS_SHARED_CREDENTIALS_FILE']
try:
expiration = datetime.fromisoformat(
open(aws_shared_credentials_file).readline().split(' ', 1)[-1].strip(),
)
# if more than 1 hour left, exit
if expiration - datetime.now(timezone.utc) > timedelta(hours=1):
sys.exit(0)
except Exception as e:
logging.error(f"can't read expiration from {aws_shared_credentials_file}: {e}")
pass
sts = boto3.client(
'sts',
aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
aws_secret_access_key=check_output([
'pass', 'show', os.environ['PASS_AWS_SECRET_ACCESS_KEY'],
]).decode('utf-8').strip(),
region_name='us-west-2',
)
mfa_serial = os.environ['AWS_MFA_SERIAL']
token_code = check_output([
'ykman', 'oath', 'accounts', 'code',
'-s', os.environ['YUBIKEY_MFA_ACCOUNT'],
]).decode('utf-8').strip()
try:
response = sts.get_session_token(
SerialNumber=mfa_serial,
TokenCode=token_code,
)
except Exception as e:
logging.error(f"can't get session token: {e}")
sys.exit(1)
with open(aws_shared_credentials_file, 'w') as f:
f.write("""# {Expiration}
[default]
aws_access_key_id = {AccessKeyId}
aws_secret_access_key = {SecretAccessKey}
aws_session_token = {SessionToken}
""".format(**response['Credentials']))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment