Browse Source

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.
Maksym 3 years ago
parent
commit
3e6ee85a16
2 changed files with 62 additions and 50 deletions
  1. 3 3
      plugins/aws/README.md
  2. 59 47
      plugins/aws/aws.plugin.zsh

+ 3 - 3
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 `<aws:`.
+* ZSH_THEME_AWS_PREFIX: sets the prefix of the AWS_PROFILE. Defaults to `<aws:`.
 
-- ZSH_THEME_AWS_SUFFIX: sets the suffix of the AWS_PROFILE. Defaults to `>`.
+* ZSH_THEME_AWS_SUFFIX: sets the suffix of the AWS_PROFILE. Defaults to `>`.

+ 59 - 47
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
-
-      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")
-      local JSON="$(eval ${assume_cmd[@]})"
-
-      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)"
+
+  # First, if the profile has MFA configured, lets get the token and session duration
+  local mfa_opt=""
+
+  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
+
+  # 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
 
+    # 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() {