Last active
January 23, 2025 23:00
-
-
Save inkblot/aed97400ee9cc741e09e0b00bbb4ce4c to your computer and use it in GitHub Desktop.
Authenticate to vault using IAM instance profile credentials in bash using curl, openssl, and jq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| _SELF="${0##*/}" | |
| _HERE="$(dirname $(realpath ${0}))" | |
| function aws_instance_profile_arn() { | |
| curl -s http://169.254.169.254/2019-10-01/meta-data/iam/info | jq -r .InstanceProfileArn | |
| } | |
| function aws_instance_profile_name() { | |
| aws_instance_profile_arn | sed -e 's/.*\///' | |
| } | |
| function aws_credentials() { | |
| curl -s http://169.254.169.254/2019-10-01/meta-data/iam/security-credentials/$(aws_instance_profile_name) | |
| } | |
| function aws_access_key_id() { | |
| aws_credentials | jq -r .AccessKeyId | |
| } | |
| function aws_secret_access_key() { | |
| aws_credentials | jq -r .SecretAccessKey | |
| } | |
| function aws_session_token() { | |
| aws_credentials | jq -r .Token | |
| } | |
| function aws_environment() { | |
| cat <<EOS | |
| export AWS_ACCESS_KEY_ID=$(aws_access_key_id) | |
| export AWS_SECRET_ACCESS_KEY=$(aws_secret_access_key) | |
| export AWS_SESSION_TOKEN=$(aws_session_token) | |
| EOS | |
| } | |
| if [ "${_SELF}" = "aws-credentials.sh" ]; then | |
| set -eu | |
| aws_environment | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| function AWS4_HMAC_SHA256() { | |
| openssl dgst -sha256 -hex -mac HMAC -macopt "$1" 2>/dev/null | awk '{print $2}' | |
| } | |
| function AWS4_SHA256() { | |
| openssl dgst -sha256 -hex 2>/dev/null | awk '{print $2}' | |
| } | |
| function AWS4_BASE64() { | |
| openssl base64 -A | |
| } | |
| function AWS4_SIGN() { | |
| local kSecret="AWS4$1" | |
| local kDate=$(printf '%s' "$2" | AWS4_HMAC_SHA256 "key:${kSecret}") | |
| local kRegion=$(printf '%s' "$3" | AWS4_HMAC_SHA256 "hexkey:${kDate}") | |
| local kService=$(printf '%s' "$4" | AWS4_HMAC_SHA256 "hexkey:${kRegion}") | |
| local kSigning=$(printf '%s' "aws4_request" | AWS4_HMAC_SHA256 "hexkey:${kService}") | |
| local signedString=$(printf '%s' "$5" | AWS4_HMAC_SHA256 "hexkey:${kSigning}") | |
| printf '%s' "${signedString}" | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| _SELF="${0##*/}" | |
| _HERE="$(dirname $(realpath ${0}))" | |
| function parse_url() { | |
| local url _url __url proto uphp user_pass host_port user pass host port path query_string | |
| url="${1}" | |
| proto="${url%://*}" # proto: everything up to the first '://' | |
| # There should definitely be a proto, neither empty nor equal to url | |
| if [ "${proto}" == "" -o "${proto}" == "$url" ]; then | |
| echo "Invalid url: ${url}" >&2 | |
| return 1 | |
| fi | |
| _url="${url:$((3 + ${#proto}))}" # _url: url minus "${proto}://" | |
| uphp="${_url%%/*}" # uphp (User-Password-Host-Port): everything up to the first '/' in _url | |
| __url="${_url:$((${#uphp} + 1))}" # __url: _url minus uphp | |
| user_pass="${uphp%%@*}" | |
| if [ "${user_pass}" == "${uphp}" ]; then | |
| user_pass="" | |
| host_port="${uphp}" | |
| else | |
| host_port="${uphp#*@}" | |
| fi | |
| user="${user_pass%%:*}" # user: everything up to the first ':' in user_pass | |
| if [ "${user}" == "${user_pass}" ]; then | |
| pass="" | |
| else | |
| pass="${user_pass#*:}" # pass: everything after the first ':' in user_pass, but only if there was a ':' | |
| fi | |
| host="${host_port%%:*}" # host: everything up to the first ':' in host_port | |
| if [ "${host}" == "${host_port}" ]; then | |
| port="80" | |
| else | |
| port="${host_port#*:}" # port: everything after the first ':' in host_port, but only if there was a ':' | |
| fi | |
| path="${__url%%\?*}" # path: everything up to the first '?' in __url | |
| if [ "${path}" == "${__url}" ]; then | |
| query_string="" | |
| else | |
| query_string="${__url%*\?}" # query_string: everything after the first '?' in __url, but only if there was a '?' | |
| fi | |
| echo "{"\ | |
| "\"proto\": \"${proto}\","\ | |
| "\"host\": \"${host}\","\ | |
| "\"port\": ${port},"\ | |
| "\"username\": \"${user}\","\ | |
| "\"password\": \"${pass}\","\ | |
| "\"path\": \"${path}\","\ | |
| "\"query_string\": \"${query_string}\""\ | |
| "}" | |
| } | |
| if [ "${_SELF}" = "parse-url.sh" ]; then | |
| set -eu | |
| parse_url "$@" | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| _SELF="${0##*/}" | |
| _HERE="$(dirname $(realpath ${0}))" | |
| source "${_HERE}/parse-url.sh" | |
| source "${_HERE}/aws-credentials.sh" | |
| source "${_HERE}/aws4-sign.sh" | |
| function vault_iam_auth() { | |
| local vault_role="$1" | |
| local vault_addr="${2:-${VAULT_ADDR:-http://vault.example.internal:8200}}" | |
| local vault_ca_cert="${3:-${VAULT_CA_CERT:-}}" | |
| local aws_access_key_id="$(aws_access_key_id)" | |
| local aws_secret_access_key="$(aws_secret_access_key)" | |
| local aws_session_token="$(aws_session_token)" | |
| local vault_host=$(parse_url "${vault_addr}" | jq -r .host) | |
| local auth_type="AWS4-HMAC-SHA256" | |
| local amz_date=$(TZ=Z date +%Y%m%dT%H%M%SZ) | |
| local content_type="application/x-www-form-urlencoded charset=utf-8" | |
| local iam_request_body="Action=GetCallerIdentity&Version=2011-06-15" | |
| local iam_request_host="sts.amazonaws.com" | |
| local iam_request_url="https://${iam_request_host}/" | |
| local signed_headers="content-type;host;x-amz-date;x-amz-security-token;x-vault-aws-iam-server-id" | |
| local canonical_request="$(cat <<EOR | |
| POST | |
| / | |
| content-type:${content_type} | |
| host:${iam_request_host} | |
| x-amz-date:${amz_date} | |
| x-amz-security-token:${aws_session_token} | |
| x-vault-aws-iam-server-id:${vault_host} | |
| ${signed_headers} | |
| $(printf '%s' "${iam_request_body}" | AWS4_SHA256) | |
| EOR | |
| )" | |
| local credential_scope="${amz_date:0:8}/us-east-1/sts/aws4_request" | |
| local signed_string="$(cat <<EOS | |
| ${auth_type} | |
| ${amz_date} | |
| ${credential_scope} | |
| $(printf '%s' "${canonical_request}" | AWS4_SHA256) | |
| EOS | |
| )" | |
| #printf 'signed_string: $%s^\n\n' "${signed_string}" | |
| local signature=$(AWS4_SIGN "${aws_secret_access_key}" "${amz_date:0:8}" "us-east-1" "sts" "${signed_string}") | |
| local authorization="${auth_type} Credential=${aws_access_key_id}/${credential_scope}, SignedHeaders=${signed_headers}, Signature=${signature}" | |
| local iam_request_headers="$(cat <<EOH | |
| { | |
| "Content-Type": ["${content_type}"], | |
| "Host": ["${iam_request_host}"], | |
| "X-Amz-Date": ["${amz_date}"], | |
| "X-Amz-Security-Token": ["${aws_session_token}"], | |
| "X-Vault-AWS-IAM-Server-Id": ["${vault_host}"], | |
| "Authorization": ["${authorization}"] | |
| } | |
| EOH | |
| )" | |
| local data="$(cat <<EOJ | |
| { | |
| "role": "${vault_role}", | |
| "iam_http_request_method": "POST", | |
| "iam_request_url": "$(printf '%s' "${iam_request_url}" | AWS4_BASE64)", | |
| "iam_request_body": "$(printf '%s' "${iam_request_body}" | AWS4_BASE64)", | |
| "iam_request_headers": "$(printf '%s' "${iam_request_headers}" | AWS4_BASE64)" | |
| } | |
| EOJ | |
| )" | |
| curl -s ${vault_ca_cert:+--cacert ${vault_ca_cert}} --request POST --data "$(jq -c . <<<"${data}")" "${vault_addr}/v1/auth/aws/login" | |
| } | |
| if [ "${_SELF}" = "vault-iam-auth.sh" ]; then | |
| set -eu | |
| vault_iam_auth "$@" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome. Nice work!