get-aws-login 4.8 KB

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