get-aws-login 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. #!/usr/bin/env bash
  2. unset LD_LIBRARY_PATH
  3. function log {
  4. if [ -t 1 ]; then
  5. echo "$@" >&2
  6. fi
  7. }
  8. function prompt_password {
  9. if command -v kdialog >/dev/null; then
  10. kdialog --password Password
  11. else
  12. zenity --password --timeout=60
  13. fi
  14. }
  15. function prompt_mfa {
  16. if command -v kdialog >/dev/null; then
  17. kdialog --inputbox MFA
  18. else
  19. zenity --entry --text=MFA
  20. fi
  21. }
  22. # ignore existing credentials
  23. unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
  24. output="eval"
  25. duration=900
  26. use_cache=1
  27. eval set -- "$(getopt -o p:d:o: --long profile:,duration:,output: -- "$@")"
  28. while true; do
  29. case "$1" in
  30. -p|--profile)
  31. profile=$2
  32. shift 2
  33. ;;
  34. -d|--duration)
  35. if [ -n "$2" ]; then
  36. duration=$2
  37. use_cache=0
  38. fi
  39. shift 2
  40. ;;
  41. -o|--output)
  42. if [ -n "$2" ]; then
  43. output=$2
  44. fi
  45. shift 2
  46. ;;
  47. --)
  48. shift
  49. break
  50. ;;
  51. *)
  52. echo "Internal error!"
  53. exit 1
  54. esac
  55. done
  56. cache_key="aws-$profile"
  57. cache_gpg="$XDG_RUNTIME_DIR/$cache_key.gpg"
  58. function get_from_keepassxc {
  59. keepassxc-cli show -q "$KEEPASS_FILE" "$KEEPASS_AWS_ENTRY" -a "$1" <<< "$2"
  60. }
  61. function get_role_arn {
  62. if ! aws configure get "profile.$profile.source_role_arn"; then
  63. log "source_role_arn not set for profile $profile"
  64. log "run aws configure set profile.$profile.source_role_arn <role arn>"
  65. return 1
  66. fi
  67. }
  68. function get_cached_long_term_credentials {
  69. if [ ! -f "$cache_gpg" ]; then
  70. log "No cached long-term credentials, requesting new"
  71. return 1
  72. fi
  73. if ! cached=$(gpg --batch -d --passphrase-file <(echo "$password") "$cache_gpg" 2>/dev/null); then
  74. log "Error getting cached long-term credentials"
  75. return 2
  76. fi
  77. expiration=$(date -d "$(jq -r '.Credentials.Expiration' <<< "$cached")" +%s)
  78. if [ "$expiration" -lt "$(date +%s)" ]; then
  79. log "Cached long-term credentials expired, requesting new"
  80. return 1
  81. fi
  82. echo "$cached"
  83. }
  84. function get_long_term_credentials {
  85. local duration
  86. local password
  87. duration="$1"
  88. if ! password=$(prompt_password 2> /dev/null); then
  89. log "Failed to get password"
  90. return 1
  91. fi
  92. credentials=$(get_cached_long_term_credentials)
  93. retcode=$?
  94. if [ $retcode -eq 2 ]; then
  95. exit 1
  96. elif [ $retcode -eq 1 ]; then
  97. if ! role_arn=$(get_role_arn); then
  98. return 1
  99. fi
  100. if ! AWS_ACCESS_KEY_ID=$(get_from_keepassxc UserName "$password"); then
  101. log "Failed to open vault"
  102. exit 1
  103. fi
  104. if ! AWS_SECRET_ACCESS_KEY=$(get_from_keepassxc Password "$password"); then
  105. log "Failed to open vault"
  106. exit 1
  107. fi
  108. export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
  109. export AWS_PROFILE=default
  110. mfa_serial_number=$(aws configure get mfa_serial)
  111. if ! mfa=$(prompt_mfa 2> /dev/null); then
  112. log "Failed to get MFA"
  113. return 1
  114. fi
  115. if credentials=$(aws sts assume-role --serial-number "$mfa_serial_number" --token-code "$mfa" --role-arn "$role_arn" --role-session-name "$(hostname)" --duration-seconds "$duration"); then
  116. gpg --batch -c --passphrase-file <(echo "$password") <<< "$credentials" > "$cache_gpg"
  117. else
  118. return 1
  119. fi
  120. fi
  121. echo "$credentials"
  122. }
  123. function get_cached_short_term_credentials {
  124. if [ "$use_cache" -eq 0 ]; then
  125. return 1
  126. fi
  127. keyctl pipe "%user:$cache_key" 2>/dev/null
  128. }
  129. function get_short_term_credentials {
  130. if ! credentials=$(get_cached_short_term_credentials); then
  131. if ! role_arn=$(get_role_arn); then
  132. return 1
  133. fi
  134. if ! long_term_credentials="$(get_long_term_credentials 43200)"; then
  135. return 1
  136. fi
  137. AWS_ACCESS_KEY_ID=$(jq -r .Credentials.AccessKeyId <<<"$long_term_credentials")
  138. AWS_SECRET_ACCESS_KEY=$(jq -r .Credentials.SecretAccessKey <<<"$long_term_credentials")
  139. AWS_SESSION_TOKEN=$(jq -r .Credentials.SessionToken <<<"$long_term_credentials")
  140. export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
  141. if credentials=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$(hostname)" --duration-seconds "$duration"); then
  142. keyctl padd user "$cache_key" @s <<<"$credentials" >/dev/null
  143. keyctl timeout "%user:$cache_key" "$duration" >/dev/null
  144. else
  145. return 1
  146. fi
  147. fi
  148. echo "$credentials"
  149. }
  150. while keyctl read '%user:lock' >/dev/null 2>&1; do
  151. sleep 1
  152. done
  153. keyctl add user lock lock @s >/dev/null 2>&1
  154. function finish {
  155. keyctl revoke '%user:lock' 2>&1
  156. }
  157. trap finish EXIT
  158. if [ "$duration" -gt 3600 ]; then
  159. if ! credentials=$(get_long_term_credentials "$duration"); then
  160. exit 1
  161. fi
  162. else
  163. if ! credentials=$(get_short_term_credentials); then
  164. exit 1
  165. fi
  166. fi
  167. if [ "$output" = "credential_process" ]; then
  168. jq '.Credentials | .Version=1' <<< "$credentials"
  169. else
  170. jq <<< "$credentials" -r '.Credentials | @sh "
  171. export AWS_ACCESS_KEY_ID=\(.AccessKeyId)
  172. export AWS_SECRET_ACCESS_KEY=\(.SecretAccessKey)
  173. export AWS_SESSION_TOKEN=\(.SessionToken)"'
  174. fi