Created
February 27, 2024 18:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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