From 3e6ee85a161c8089955c19364728e167025a911d Mon Sep 17 00:00:00 2001 From: Maksym Date: Wed, 4 Nov 2020 21:10:22 +0000 Subject: [PATCH] fix(aws): support MFA for profiles without role to assume (#9411) Previously, the plugin only supported MFA for profiles that had a role to assume, specified in role_arn. Now, the plugin supports MFA for profiles without a role to assume. Closes #9408 * refactor(aws plugin): remove dependency on jq Previously, acp command relied on jq. Now that dependency has been removed, as well as some linter suggestions implemented. --- plugins/aws/README.md | 6 +-- plugins/aws/aws.plugin.zsh | 98 +++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/plugins/aws/README.md b/plugins/aws/README.md index 851f586dd..4c2ae96e5 100644 --- a/plugins/aws/README.md +++ b/plugins/aws/README.md @@ -3,7 +3,7 @@ This plugin provides completion support for [awscli](https://docs.aws.amazon.com/cli/latest/reference/index.html) and a few utilities to manage AWS profiles and display them in the prompt. -To use it, make sure [jq](https://stedolan.github.io/jq/download/) is installed, and add `aws` to the plugins array in your zshrc file. +To use it, add `aws` to the plugins array in your zshrc file. ```zsh plugins=(... aws) @@ -40,6 +40,6 @@ plugins=(... aws) The plugin creates an `aws_prompt_info` function that you can use in your theme, which displays the current `$AWS_PROFILE`. It uses two variables to control how that is shown: -- ZSH_THEME_AWS_PREFIX: sets the prefix of the AWS_PROFILE. Defaults to ``. +* ZSH_THEME_AWS_SUFFIX: sets the suffix of the AWS_PROFILE. Defaults to `>`. diff --git a/plugins/aws/aws.plugin.zsh b/plugins/aws/aws.plugin.zsh index 8149ba121..e6959759e 100644 --- a/plugins/aws/aws.plugin.zsh +++ b/plugins/aws/aws.plugin.zsh @@ -39,60 +39,73 @@ function acp() { return 1 fi - local exists="$(aws configure get aws_access_key_id --profile $1)" + local aws_access_key_id="$(aws configure get aws_access_key_id --profile $1)" + local aws_secret_access_key="$(aws configure get aws_secret_access_key --profile $1)" + local aws_session_token="$(aws configure get aws_session_token --profile $1)" + local mfa_serial="$(aws configure get mfa_serial --profile $1)" local role_arn="$(aws configure get role_arn --profile $1)" - local aws_access_key_id="" - local aws_secret_access_key="" - local aws_session_token="" - if [[ -n $exists || -n $role_arn ]]; then - if [[ -n $role_arn ]]; then - local mfa_serial="$(aws configure get mfa_serial --profile $1)" - local mfa_token="" - local mfa_opt="" - if [[ -n $mfa_serial ]]; then - echo "Please enter your MFA token for $mfa_serial:" - read mfa_token - echo "Please enter the session duration in seconds (900-43200; default: 3600, which is the default maximum for a role):" - read sess_duration - if [[ -z $sess_duration ]]; then - sess_duration="3600" - fi - mfa_opt="--serial-number $mfa_serial --token-code $mfa_token --duration-seconds $sess_duration" - fi - local ext_id="$(aws configure get external_id --profile $1)" - local extid_opt="" - if [[ -n $ext_id ]]; then - extid_opt="--external-id $ext_id" - fi + # First, if the profile has MFA configured, lets get the token and session duration + local mfa_opt="" - local profile=$1 - local source_profile="$(aws configure get source_profile --profile $1)" - if [[ -n $source_profile ]]; then - profile=$source_profile - fi + if [[ -n $mfa_serial ]]; then + local mfa_token="" + echo "Please enter your MFA token for $mfa_serial:" + read -r mfa_token + echo "Please enter the session duration in seconds (900-43200; default: 3600, which is the default maximum for a role):" + read -r sess_duration + if [[ -z $sess_duration ]]; then + sess_duration="3600" + fi + mfa_opt="--serial-number $mfa_serial --token-code $mfa_token --duration-seconds $sess_duration" + fi - echo "Assuming role $role_arn using profile $profile" - local assume_cmd=(aws sts assume-role "--profile=$profile" "--role-arn $role_arn" "--role-session-name "$profile"" "$mfa_opt" "$extid_opt") - local JSON="$(eval ${assume_cmd[@]})" + # Now see whether we need to just MFA for the current role, or assume a different one + local credentials_output="" + if [[ -n $role_arn ]]; then + # Means we need to assume a specified role - aws_access_key_id="$(echo $JSON | jq -r '.Credentials.AccessKeyId')" - aws_secret_access_key="$(echo $JSON | jq -r '.Credentials.SecretAccessKey')" - aws_session_token="$(echo $JSON | jq -r '.Credentials.SessionToken')" - else - aws_access_key_id="$(aws configure get aws_access_key_id --profile $1)" - aws_secret_access_key="$(aws configure get aws_secret_access_key --profile $1)" - aws_session_token="$(aws configure get aws_session_token --profile $1)" + # Check whether external_id is configured to use while assuming the role + local ext_id="$(aws configure get external_id --profile $1)" + local extid_opt="" + if [[ -n $ext_id ]]; then + extid_opt="--external-id $ext_id" fi + # Get source profile to use to assume role + local profile=$1 + local source_profile="$(aws configure get source_profile --profile "$1")" + if [[ -n $source_profile ]]; then + profile=$source_profile + fi + + echo "Assuming role $role_arn using profile $profile" + local assume_cmd=(aws sts assume-role "--profile=$profile" "--role-arn $role_arn" "--role-session-name $profile" "$mfa_opt" "$extid_opt" + "--query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' --output text | tr '\t' '\n'") + credentials_output="$(eval "${assume_cmd[@]}")" + elif [[ -n $mfa_opt ]]; then + # Means we only need to do MFA + echo "Obtaining session token for profile $profile" + local get_token_cmd=(aws sts get-session-token "--profile=$profile" "$mfa_opt" + "--query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' --output text | tr '\t' '\n'") + credentials_output="$(eval "${get_token_cmd[@]}")" + fi + + if [[ -n $credentials_output ]]; then + local credentials=("${(f)credentials_output}") + aws_access_key_id=${credentials[1]} + aws_secret_access_key=${credentials[2]} + aws_session_token=${credentials[3]} + fi + + if [[ -n $aws_access_key_id && -n $aws_secret_access_key ]]; then export AWS_DEFAULT_PROFILE=$1 export AWS_PROFILE=$1 export AWS_EB_PROFILE=$1 export AWS_ACCESS_KEY_ID=$aws_access_key_id export AWS_SECRET_ACCESS_KEY=$aws_secret_access_key [[ -z "$aws_session_token" ]] && unset AWS_SESSION_TOKEN || export AWS_SESSION_TOKEN=$aws_session_token - - echo "Switched to AWS Profile: $1"; + echo "Switched to AWS Profile: $1" fi } @@ -120,8 +133,7 @@ function aws_profiles() { function _aws_profiles() { reply=($(aws_profiles)) } -compctl -K _aws_profiles asp aws_change_access_key -compctl -K _aws_profiles acp aws_change_access_key +compctl -K _aws_profiles asp acp aws_change_access_key # AWS prompt function aws_prompt_info() {