Skip to content

Instantly share code, notes, and snippets.

@RichardBronosky
Last active October 9, 2019 04:47
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 RichardBronosky/4cdd0b462f6fc6a8700d1d6b7650e363 to your computer and use it in GitHub Desktop.
Save RichardBronosky/4cdd0b462f6fc6a8700d1d6b7650e363 to your computer and use it in GitHub Desktop.
A wrapper around https://github.com/remind101/assume-role that feeds in the MFA and caches.
:<<'DOCS'
* This script is a wrapper around https://github.com/remind101/assume-role that feeds
in the MFA and caches the token.
* Either: Place this file at ~/.aws/assume-role use it in ~/.bash_profile with:
[[ -f ~/.aws/assume-role ]] && source ~/.aws/assume-role
* Or: You can copy-paste the next 5 lines to get and use this script:
curl -sLo ~/.aws/assume-role https://gist.githubusercontent.com/RichardBronosky/4cdd0b462f6fc6a8700d1d6b7650e363/raw/assume-role
cat >> ~/.bash_profile <<'EOF'
[[ -f ~/.aws/assume-role ]] && source ~/.aws/assume-role
EOF
* Requires TOTP-Generator python package https://pypi.org/project/TOTP-Generator/
* This offers adequate security because it uses https://pypi.org/project/keyring/#what-is-python-keyring-lib
* To install TOTP-Generator:
[[ -x $(which pip3) ]] || brew install python # get Python3/pip only if you don't have it
pip3 install totp-generator
totp_generator -a
* The functions below assume the name of the service to be: aws
* The TOTP secret is your MFA secret that is usually only shown as a QR Code
* You may have to remove your existing MFA and generate another as follows:
IAM > Users > username > Security credentials (tab) > Assigned MFA device
\- Manage > Remove
\- Manage > Virtual MFA device > Show secret key
DOCS
# A wrapper around assume-role that passes in MFA and caches env vars to /tmp/aws-role-*.sh
# If the 2nd arg is a `-` the cached env vars are used.
# If no arguments are passed, the profiles are listed.
ar(){
local service=aws
if [[ ${#} < 1 ]]; then
cat <<'EOF'
Usage: ar <profile> [-] [env]
Passing the "-" end causes this script to use your cached token rather than requesting a new one.
It's faster, but more importantly allows you to use for-loops without getting errors due to using
"one time passwords" more than once, as a consequence of the speed.
Passing literal "env" (with or without "-") causes output to be given in envFile format that is
appropriate for VSCode launch.json and `npm i dotenv`. This is done without sourcing the temp
file.
EOF
# Show all roles/profiles in ~/.aws/config
awk -F '[:/ ]' '/^ *;/{next} /^.profile/{gsub("]",""); printf("\nprofile: %s\n", $2)} /role_arn/{printf(" account: %s\n role: %s\n", $5, $7)}' ~/.aws/config
local brk=1
if [[ -n ${ASSUMED_ROLE:-} ]]; then
[[ brk -eq 0 ]] || echo
echo "Current role: ${ASSUMED_ROLE}"
brk=0
fi
local account="$(aws sts get-caller-identity --query Account --output text 2>/dev/null)"
[[ brk -eq 0 ]] || echo
brk=0
if [[ -n "${account}" ]]; then
echo "Current account: ${account}"
else
echo "error: Credentials have expired."
fi
return 1
fi
local role=${1}
local temp=/tmp/aws-role-${role}
# Skip updating the temp file if `-` is passed as arg 2
if [[ ${2:-} == - ]]; then
shift
else
totp_generator -s ${service} | \
assume-role -duration 12h ${role} \
1>${temp}.1 \
2>${temp}.2
local status=${?}
if ( exit ${status} ); then
mv ${temp}.1 ${temp}.sh
rm -f ${temp}.2
else
cat ${temp}.2 | sed 's/MFA code: //'
[[ ${DEBUG:-0} -eq 0 ]] && rm -f ${temp}.[12]
echo "ar failed"
return 128
fi
fi
# If `env` is passed as arg 2 (or 3 when arg 2 is -) output var definitions in env file format `KEY=value`
if [[ ${2:-} == env ]]; then
sed -E '/^[[:space:]]*export/!d; s/[[:space:]]*export[[:space:]]*//; s/"//g' < ${temp}.sh
else
source ${temp}.sh
export ASSUMED_ACCOUNT="$(aws sts get-caller-identity --query Account --output text)"
fi
}
# copies MFA to clipboard and outputs it
# optionally takes an argument to override the default service of `aws`
mfa(){
local service=${1:-aws}
local secs=$(date +%-S)
if [[ -n ${NOCOPY:-} ]]; then
copy(){ cat; }
else
copy(){ pbcopy; pbpaste; }
fi
if [[ -n ${NOERR:-} ]]; then
err(){ cat; }
else
err(){ cat >/dev/stderr; }
fi
((secs=30-$secs)); [[ $secs -eq 0 ]] && ((secs=30)); [[ $secs -lt 0 ]] && ((secs=30+$secs))
totp_generator -s $service | copy
echo "Expires in: $secs" | err
}
unaws(){
unset ASSUMED_ROLE ASSUMED_ACCOUNT AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SECURITY_TOKEN AWS_SESSION_TOKEN
}
export -f ar mfa unaws
# vim: ft=sh et sw=2 ts=2 sts=2
# This creates a macOS application binary (named MFA) that can be triggered via Spotlight.
# Here are 2 options for how to create the binary:
#
# 1.
# At the Terminal, run:
# osacompile -o /Applications/MFA.app mfa_copied.applescript
#
# 2.
# Open with Script Editor and Export as Application with the name MFA in the /Applications folder
# Yes, this is a convoluted way to display a notification, but osacompiled apps can't display notifications
do shell script "
msg=\"$(NOERR=1 bash -l -c mfa)\"
osascript -e 'display notification \"'\"$msg\"'\" with title \"MFA\" subtitle \"has been sent to your clipboard\" sound name \"Glass\"'
"
# This creates a macOS application binary (named MFA) that can be triggered via Spotlight.
# Here are 2 options for how to create the binary:
#
# 1.
# At the Terminal, run:
# osacompile -o /Applications/MFA.app mfa_typed.applescript
#
# 2.
# Open with Script Editor and Export as Application with the name MFA in the /Applications folder
# Yes, this is a convoluted way to display a notification, but osacompiled apps can't display notifications
do shell script "
bash -l -c 'sleep 1; type_totp.py; sleep 15'
"
#!/usr/bin/env python3
from pynput.keyboard import Controller
from totp_generator.core_utils import KeyringTotpGenerator
keyboard = Controller()
keyring_generator = KeyringTotpGenerator()
keyboard.type(keyring_generator.get_totp_code('aws'))
@RichardBronosky
Copy link
Author

Currently uses a duration of 12h for everything. I'm tempted to detect bad durations with grep 'durationSeconds.*failed' and extract the maximum duration with awk '{print $NF}' and cache it to a new file. Then I'd use those values if present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment