AWS CLI + MFA...

Eén van de eerste dingen waar AWS op hamert bij het aanmaken van een account is het activeren van Multi Factor Authentication. Middels een policy is het ook af te dwingen bij IAM users. En da’s fijn voor Console toegang. Edoch… CLI access is met MFA niet altijd even makkelijk. Tenzij…

Tenzij een tijdelijke sessie wordt opgezet middels aws sts get-session-token --serial-number arn-of-the-mfa-device --token-code code-from-token zoals hier uitgelegd wordt. De verkregen output kan dan weer als variable gezet worden in je shell zodat daarna naar hartewens aws commando’s zijn af te vuren voor ongeveer 12 uur.

Maar dat kan natuurlijk makkelijker. Ik maakte al gebruik van een handige profile switcher, gepost door Carl Nordenfelt de deze blog.jayway.com. Naast de al bestaande aliassen zoals Carl beschreven heeft, heb ik twee aliassen toegevoegd aan ~/.bash_profile:


alias awsmfa='_awsMFA'
alias mfatl='_mfaTimeLeft'

MFA Checken / zetten

Met de eerste is te controleren of voor een profiel al een MFA sessie is opgezet. De syntax is eenvoudig:

awsmfa <profielnaam>

De functie die hiermee aangeroepen wordt controleert eerst of het desbetreffende IAM account MFA heeft geconfigureerd. Da’s mamelijk best handig om door te kunnen gaan. Vervolgens wordt gecontroleerd of er al een sessie actief is door te kijken wat de resterende sessietijd is. Mocht deze tijd groter zijn dan 0 seconden, wordt het bestaande mfa_profiel geladen middels functie _awsSwitchProfile. Indien de sessietijd verstreken of afwezig is, zal om een MFA token gevraagd worden en zal een nieuwe sessie worden opgezet. De verkregen variabelen worden in een mfa_profiel gestopt, waarna dit profiel geladen zal worden.

Resterende tijd

Een sessie heeft een levensduur tussen de 15 minuten en 36 uur, met 12 uur als default. In onderstaand script hanteer ik de default, maar mocht je deze liever aangepast willen hebben, voeg dan de optie --duration-seconds <seconden> toe met je voorkeurstijd in seconden aan het aws sts get-session-token commando in de variable sts_info. Eenmaal een sessie opgezet is de resteren tijd op te vragen met het volgende:

mfatl

Prompt

Om het helemaal mooi te maken kunnen een aantal variabelen in de prompt verwerkt worden:

PS1='\h:\W \u\[\e[1;34m\].${AWS_PROFILE}\[\e[1;33m\].${AWS_REGION}\[\e[1;32m\].$(mfatl)\[\e[00m\]\$ '

Het script

Hieronder het oorspronkelijke sessie-switch script van Carl met her en der wat aanpassingen en uitgebreidingen van mijzelf. Have fun!


$ cat ~/._awsAliases
#!/bin/bash
function _awsListAll() {

    credentialFileLocation=${AWS_SHARED_CREDENTIALS_FILE};
    if [ -z $credentialFileLocation ]; then
        credentialFileLocation=~/.aws/credentials
    fi

    while read line; do
        if [[ $line == "["* ]]; then echo "$line"; fi;
    done < $credentialFileLocation;

    mfaFileLocation=~/.aws/mfa
};

function _awsSwitchProfile() {
   if [ -z $1 ]; then  echo "Usage: awsp profilename"; return; fi

   exists="$(aws configure get aws_access_key_id --profile $1)"
   if [[ -n $exists ]]; then
       export AWS_DEFAULT_PROFILE=$1;
       export AWS_PROFILE=$1;
       export AWS_REGION=$(aws configure get region --profile $1);
       ## Export STS_EXP to speed up _mfaTimeLeft, even when empty.
       export STS_EXP=$(aws configure get aws_session_token_exiration --profile $1)
       echo "..Switched to AWS Profile: $1";
       #aws configure list
   fi
};
function _mfaTimeLeft() {
  if [ ! -z $1 ]; then
    STS_EXP=$1
  fi
  if [[ -n $STS_EXP ]];then
      current_date=$(date -u +%s)
      expiration_date=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" ${STS_EXP} +%s)
      time_left="$(((expiration_date-current_date)/60))"
      #time_left="$(((expiration_date-current_date)))"
  else
    time_left=""
  fi
  echo "$time_left"
};
function _awsMFA() {
  if [ -z $1 ]; then  echo "..Usage: awsmmfa profilename"; return; fi
  ## Strip MFA from $1, as only normal profile names are expected.
  if [[ $1 == *"mfa_"* ]];then
    profile=$(echo $1| awk -F "mfa_" '{print $2}')
  else
    profile=$1
  fi
  ## check existence of requested profile
  exists="$(aws configure get aws_access_key_id --profile ${profile})"
  if [[ -n $exists ]]; then
    ## get MFA ARN from AWS
    mfa_info=$(aws iam list-mfa-devices --profile ${profile})
    ## verify account has mfa setup:
    if [[ -n $mfa_info ]]; then
      ## Create new mfa profile for profilename
      mfa_user=$(echo "$mfa_info"|awk -F '["]' '/UserName/{print $4}')
      mfa_arn=$(echo "$mfa_info"|awk -F '["]' '/SerialNumber/{print $4}')
      mfa_profile=$(echo "mfa_${profile}")
      ## get expiration date from configfile
      sts_expire=$(aws configure get aws_session_token_exiration --profile $mfa_profile)
      ## calculate time_left (see top of file)
      time_left=$(_mfaTimeLeft $sts_expire )
      if [[ $time_left -gt "0" ]];then
        ## if > 0 switch profile to mfa_profile
        echo "..Existing token is present and still valid"
      else
        ## create / update aws config mfa_profile
        ## read MFA token
        echo "..No valid token found"
        echo "..Please enter MFA token for user ${mfa_user}:"
        read mfa_token
        ## use token to get STS token. Note duration ranges from 900 to 129600 seconds, defaults to 43200 (12hours)
        #sts_info=$(aws sts get-session-token --serial-number ${mfa_arn} --token-code ${mfa_token} --duration-seconds 900 --profile ${profile})
        sts_info=$(aws sts get-session-token --serial-number ${mfa_arn} --token-code ${mfa_token} --profile ${profile})
        ## validate success
        if [[ -n $sts_info ]]; then
          aws configure set mfa_user ${mfa_user} --profile $mfa_profile
          aws configure set region $(aws configure get region --profile ${profile}) --profile $mfa_profile
          aws configure set aws_access_key_id $(echo "$sts_info" | awk -F '["]' '/AccessKeyId/{print $4}') --profile $mfa_profile
          aws configure set aws_secret_access_key $(echo "$sts_info" | awk -F '["]' '/SecretAccessKey/{print $4}') --profile $mfa_profile
          aws configure set aws_session_token $(echo "$sts_info" | awk -F '["]' '/SessionToken/{print $4}') --profile $mfa_profile
          aws configure set aws_session_token_exiration $(echo "$sts_info" | awk -F '["]' '/Expiration/{print $4}') --profile $mfa_profile
        fi
      fi
      ## Time to set MFA profile:
      _awsSwitchProfile $mfa_profile
    else
      echo "..No MFA setup for ${profile}!"
      return
    fi
  else
    echo "..Config ${profile} does not exist!"
  fi
}