Merge e1f60f6509ade7367fecb61d4fc93fd5270806ae into ac0cdcf70bf3fedc126925a05d11396a5cc8e280

This commit is contained in:
Mal Graty 2018-08-10 10:17:27 +00:00 committed by GitHub
commit 0f1995472a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 334 additions and 214 deletions

34
acme.sh
View File

@ -11,7 +11,7 @@ PROJECT="https://github.com/Neilpang/$PROJECT_NAME"
DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME" DEFAULT_INSTALL_HOME="$HOME/.$PROJECT_NAME"
_SCRIPT_="$0" _SCRIPT_="$0"
_SUB_FOLDERS="dnsapi deploy" _SUB_FOLDERS="lib dnsapi deploy"
LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory" LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory"
LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory" LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory"
@ -1633,15 +1633,15 @@ _post() {
_debug "_CURL" "$_CURL" _debug "_CURL" "$_CURL"
if [ "$needbase64" ]; then if [ "$needbase64" ]; then
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" -H "$_H6" -H "$_H7" -H "$_H8" -H "$_H9" --data "$body" "$_post_url" | _base64)"
else else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" -H "$_H6" -H "$_H7" -H "$_H8" -H "$_H9" --data "$body" "$_post_url" | _base64)"
fi fi
else else
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" -H "$_H6" -H "$_H7" -H "$_H8" -H "$_H9" --data "$body" "$_post_url")"
else else
response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" -H "$_H6" -H "$_H7" -H "$_H8" -H "$_H9" --data "$body" "$_post_url")"
fi fi
fi fi
_ret="$?" _ret="$?"
@ -1661,29 +1661,29 @@ _post() {
if [ "$needbase64" ]; then if [ "$needbase64" ]; then
if [ "$httpmethod" = "POST" ]; then if [ "$httpmethod" = "POST" ]; then
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
else else
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
fi fi
else else
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
else else
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
fi fi
fi fi
else else
if [ "$httpmethod" = "POST" ]; then if [ "$httpmethod" = "POST" ]; then
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
else else
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
fi fi
else else
if [ "$_postContentType" ]; then if [ "$_postContentType" ]; then
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
else else
response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
fi fi
fi fi
fi fi
@ -1726,9 +1726,9 @@ _get() {
fi fi
_debug "_CURL" "$_CURL" _debug "_CURL" "$_CURL"
if [ "$onlyheader" ]; then if [ "$onlyheader" ]; then
$_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url" $_CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" -H "$_H6" -H "$_H7" -H "$_H8" -H "$_H9" "$url"
else else
$_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$url" $_CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" -H "$_H6" -H "$_H7" -H "$_H8" -H "$_H9" "$url"
fi fi
ret=$? ret=$?
if [ "$ret" != "0" ]; then if [ "$ret" != "0" ]; then
@ -1748,9 +1748,9 @@ _get() {
fi fi
_debug "_WGET" "$_WGET" _debug "_WGET" "$_WGET"
if [ "$onlyheader" ]; then if [ "$onlyheader" ]; then
$_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null "$url" 2>&1 | sed 's/^[ ]*//g' $_WGET --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null "$url" 2>&1 | sed 's/^[ ]*//g'
else else
$_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url" $_WGET --user-agent="$USER_AGENT" --header "$_H9" --header "$_H8" --header "$_H7" --header "$_H6" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
fi fi
ret=$? ret=$?
if [ "$ret" = "8" ]; then if [ "$ret" = "8" ]; then

View File

@ -275,3 +275,17 @@ acme.sh --deploy -d haproxy.example.com --deploy-hook haproxy
``` ```
The path for the PEM file will be stored with the domain configuration and will be available when renewing, so that deploy will happen automatically when renewed. The path for the PEM file will be stored with the domain configuration and will be available when renewing, so that deploy will happen automatically when renewed.
## 11. Deploy the cert to AWS ACM
Ensure your access key owner or role has a polcy attached that allows the
actions `acm:ListCertificates` and `acm:ImportCertificate`. Role credentials
will be picked up automatically from EC2 instances and ECS containers, in other
cases you must set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your
environment.
```sh
export AWS_ACM_REGIONS="us-east-1,us-west-2"
acme.sh --deploy -d ftp.example.com --deploy-hook aws_acm
```

79
deploy/aws_acm.sh Normal file
View File

@ -0,0 +1,79 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to Amazon Certificate Manager.
#returns 0 means success, otherwise error.
# shellcheck source=lib/aws.sh
. "$LE_WORKING_DIR/lib/aws.sh"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
aws_acm_deploy() {
_cdomain="$1" _ckey="$2" _ccert="$3" _cca="$4" _cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_regions="${AWS_ACM_REGIONS:-$(_readdomainconf Aws_Acm_Regions)}"
if [ -z "$_regions" ]; then
_err "no ACM regions to use when deploying $_cdomain"
return 1
fi
_savedomainconf Aws_Acm_Regions "$_regions"
_ret=0
for _region in $(printf %s "$_regions" | tr ',' ' '); do
_debug _region "$_region"
_arn="$(_get_arn "$_cdomain" "$_region")"
_debug2 _arn "$_arn"
_json="{$(_fmt_json \
CertificateArn "$_arn" \
Certificate "$(_base64 <"$_ccert")" \
CertificateChain "$(_base64 <"$_cfullchain")" \
PrivateKey "$(_base64 <"$_ckey")"
)}"
_secure_debug2 _json "$_json"
if ! _aws acm ImportCertificate "$_region" "$_json" >/dev/null; then
_err "unable to deploy $_cdomain to ACM in $_region"
_ret=2
fi
done
return $_ret
}
_get_arn() {
_page='"MaxItems": 20'
_next="$_page"
while [ "$_next" ]; do
resp="$(_aws acm ListCertificates "$2" "{$_next,$_page}")"
[ "$?" -eq 0 ] || return 2
printf %s "$resp" \
| _normalizeJson \
| tr '{}' '\n' \
| grep -F "\"DomainName\":\"$1\"" \
| _egrep_o "arn:aws:acm:$2:[^\"]+" \
| grep "^arn:aws:acm:$2:"
[ "$?" -eq 0 ] && return
_next="$(printf %s "$resp" | _egrep_o '"NextToken":"[^"]+"')"
_debug3 _next "$_next"
done
return 1
}
_fmt_json() {
while [ "$#" -gt 1 ]; do
[ "$2" ] && printf '"%s":"%s"\n' "$1" "$2"
shift 2
done | paste -sd ','
}

View File

@ -7,11 +7,11 @@
#This is the Amazon Route53 api wrapper for acme.sh #This is the Amazon Route53 api wrapper for acme.sh
AWS_HOST="route53.amazonaws.com"
AWS_URL="https://$AWS_HOST"
AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API" AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API"
# shellcheck source=lib/aws.sh
. "$LE_WORKING_DIR/lib/aws.sh"
######## Public functions ##################### ######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@ -19,27 +19,12 @@ dns_aws_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}" if ! _aws_auth; then
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
_use_container_role || _use_instance_role
fi
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
_err "You don't specify aws route53 api key id and and api key secret yet." _err "You don't specify aws route53 api key id and and api key secret yet."
_err "Please create your key and try again. see $(__green $AWS_WIKI)" _err "Please create your key and try again. see $(__green $AWS_WIKI)"
return 1 return 1
fi fi
#save for future use, unless using a role which will be fetched as needed
if [ -z "$_using_role" ]; then
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
fi
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
@ -50,7 +35,8 @@ dns_aws_add() {
_debug _domain "$_domain" _debug _domain "$_domain"
_info "Geting existing records for $fulldomain" _info "Geting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then response="$(_aws r53 GET "/2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT")"
if [ "$?" -gt 0 ]; then
return 1 return 1
fi fi
@ -70,7 +56,8 @@ dns_aws_add() {
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords>$_resource_record<ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>" _aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords>$_resource_record<ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then response="$(_aws r53 POST "/2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml")"
if [ "$?" -eq 0 ] && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "txt record updated success." _info "txt record updated success."
return 0 return 0
fi fi
@ -83,13 +70,6 @@ dns_aws_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
_use_container_role || _use_instance_role
fi
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
@ -100,7 +80,8 @@ dns_aws_rm() {
_debug _domain "$_domain" _debug _domain "$_domain"
_info "Geting existing records for $fulldomain" _info "Geting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then response="$(_aws r53 GET "/2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT")"
if [ "$?" -gt 0 ]; then
return 1 return 1
fi fi
@ -114,7 +95,8 @@ dns_aws_rm() {
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords>$_resource_record</ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>" _aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords>$_resource_record</ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then response="$(_aws r53 POST "/2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml")"
if [ "$?" -eq 0 ] && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "txt record deleted success." _info "txt record deleted success."
return 0 return 0
fi fi
@ -130,7 +112,8 @@ _get_root() {
i=2 i=2
p=1 p=1
if aws_rest GET "2013-04-01/hostedzone"; then response="$(_aws r53 GET '/2013-04-01/hostedzone')"
if [ "$?" -eq 0 ]; then
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug2 "Checking domain: $h" _debug2 "Checking domain: $h"
@ -139,7 +122,8 @@ _get_root() {
_debug "IsTruncated" _debug "IsTruncated"
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)" _nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug "NextMarker" "$_nextMarker" _debug "NextMarker" "$_nextMarker"
if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then response="$(_aws r53 GET '/2013-04-01/hostedzone' "marker=$_nextMarker")"
if [ "$?" -eq 0 ]; then
_debug "Truncated request OK" _debug "Truncated request OK"
i=2 i=2
p=1 p=1
@ -173,168 +157,3 @@ _get_root() {
fi fi
return 1 return 1
} }
_use_container_role() {
# automatically set if running inside ECS
if [ -z "$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" ]; then
_debug "No ECS environment variable detected"
return 1
fi
_use_metadata "169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
}
_use_instance_role() {
_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
_debug "_url" "$_url"
if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then
_debug "Unable to fetch IAM role from instance metadata"
return 1
fi
_aws_role=$(_get "$_url" "" 1)
_debug "_aws_role" "$_aws_role"
_use_metadata "$_url$_aws_role"
}
_use_metadata() {
_aws_creds="$(
_get "$1" "" 1 \
| _normalizeJson \
| tr '{,}' '\n' \
| while read -r _line; do
_key="$(echo "${_line%%:*}" | tr -d '"')"
_value="${_line#*:}"
_debug3 "_key" "$_key"
_secure_debug3 "_value" "$_value"
case "$_key" in
AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;;
SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;;
Token) echo "AWS_SESSION_TOKEN=$_value" ;;
esac
done \
| paste -sd' ' -
)"
_secure_debug "_aws_creds" "$_aws_creds"
if [ -z "$_aws_creds" ]; then
return 1
fi
eval "$_aws_creds"
_using_role=true
}
#method uri qstr data
aws_rest() {
mtd="$1"
ep="$2"
qsr="$3"
data="$4"
_debug mtd "$mtd"
_debug ep "$ep"
_debug qsr "$qsr"
_debug data "$data"
CanonicalURI="/$ep"
_debug2 CanonicalURI "$CanonicalURI"
CanonicalQueryString="$qsr"
_debug2 CanonicalQueryString "$CanonicalQueryString"
RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
_debug2 RequestDate "$RequestDate"
#RequestDate="20161120T141056Z" ##############
export _H1="x-amz-date: $RequestDate"
aws_host="$AWS_HOST"
CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
SignedHeaders="host;x-amz-date"
if [ -n "$AWS_SESSION_TOKEN" ]; then
export _H3="x-amz-security-token: $AWS_SESSION_TOKEN"
CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
SignedHeaders="${SignedHeaders};x-amz-security-token"
fi
_debug2 CanonicalHeaders "$CanonicalHeaders"
_debug2 SignedHeaders "$SignedHeaders"
RequestPayload="$data"
_debug2 RequestPayload "$RequestPayload"
Hash="sha256"
CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
_debug2 CanonicalRequest "$CanonicalRequest"
HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
_debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
Algorithm="AWS4-HMAC-SHA256"
_debug2 Algorithm "$Algorithm"
RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
_debug2 RequestDateOnly "$RequestDateOnly"
Region="us-east-1"
Service="route53"
CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request"
_debug2 CredentialScope "$CredentialScope"
StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
_debug2 StringToSign "$StringToSign"
kSecret="AWS4$AWS_SECRET_ACCESS_KEY"
#kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
_secure_debug2 kSecret "$kSecret"
kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
_secure_debug2 kSecretH "$kSecretH"
kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
_debug2 kDateH "$kDateH"
kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
_debug2 kRegionH "$kRegionH"
kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
_debug2 kServiceH "$kServiceH"
kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)"
_debug2 kSigningH "$kSigningH"
signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
_debug2 signature "$signature"
Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
_debug2 Authorization "$Authorization"
_H2="Authorization: $Authorization"
_debug _H2 "$_H2"
url="$AWS_URL/$ep"
if [ "$qsr" ]; then
url="$AWS_URL/$ep?$qsr"
fi
if [ "$mtd" = "GET" ]; then
response="$(_get "$url")"
else
response="$(_post "$data" "$url")"
fi
_ret="$?"
_debug2 response "$response"
if [ "$_ret" = "0" ]; then
if _contains "$response" "<ErrorResponse"; then
_err "Response error:$response"
return 1
fi
fi
return "$_ret"
}

208
lib/aws.sh Normal file
View File

@ -0,0 +1,208 @@
#!/usr/bin/env sh
# usage: _aws <svc> <svcargs...>
#
# services:
#
# ACM: _aws acm <rpc> <region> [json]
# _aws acm ListCertificates us-east-1 '{"MaxItems": 2}'
#
# R53: _aws r53 <verb> <path> [query] [xml]
# _aws r53 GET /2013-04-01/hostedzone maxitems=2
_aws() {
_svc="$1" # _args=...
shift
if ! _aws_auth; then
return 255
fi
n="$(printf '\nn')" n="${n%n}"
"_aws_svc_$_svc" "$@"
}
# private
# services
_aws_svc_acm() {
_rpc="$1" _region="$2" _json="$3"
_empty='{}'
_rpc="x-amz-target:CertificateManager.$_rpc"
_type='content-type:application/x-amz-json-1.1'
_aws_wrap '"__type":' \
POST "acm.$_region.amazonaws.com" '/' '' "$_region/acm" \
"$_rpc$n$_type" "${_json:-$_empty}"
}
_aws_svc_r53() {
_verb="$1" _path="$2" _query="$3" _xml="$4"
_aws_wrap '<ErrorResponse' \
"$_verb" 'route53.amazonaws.com' "$_path" "$_query" 'us-east-1/route53' \
'' "$_xml"
}
# core
_aws_wrap() {
_check="$1" # _args=...
shift
_resp="$(_aws_req4 "$@")"
_ret="$?"
_debug2 _resp "$_resp"
if [ "$_ret" -eq 0 ] && _contains "$_resp" "$_check"; then
_err "Response error: $_resp"
return 1
fi
printf %s "$_resp"
return "$_ret"
}
_aws_req4() {
_verb="$1" _host="$2" _path="$3" _query="$4" _svc="$5" _hdrs="$6" _data="$7"
_debug _verb "$_verb"
_debug _host "$_host"
_debug _path "$_path"
_debug _query "$_query"
_debug _svc "$_svc"
_debug _hdrs "$_hdrs"
_debug _data "$_data"
_date="$(date -u +%Y%m%dT%H%M%SZ)"
_debug2 _date "$_date"
_hdrs="host:$_host${n}x-amz-date:$_date$n$_hdrs"
if [ "$AWS_SESSION_TOKEN" ]; then
_hdrs="$_hdrs${n}x-amz-security-token:$AWS_SESSION_TOKEN"
fi
_hdrs="$(printf %s "$_hdrs" | sort | sed '/^$/d')$n"
_debug2 _hdrs "$_hdrs"
_keys="$(
printf %s "$_hdrs" | while read -r _hdr; do
printf '%s\n' "${_hdr%%:*}"
done | paste -sd ';'
)"
_debug2 _keys "$_keys"
_scope="$(printf %s "$_date" | cut -c 1-8)/$_svc/aws4_request"
_debug2 _scope "$_scope"
_hash='sha256'
_debug3 _hash "$_hash"
_algo='AWS4-HMAC-SHA256'
_debug3 _algo "$_algo"
_bdy="$(printf %s "$_data" | _digest "$_hash" hex)"
_debug2 _bdy "$_bdy"
_req="$_verb$n$_path$n$_query$n$_hdrs$n$_keys$n$_bdy"
_debug2 _req "$_req"
_req="$(printf %s "$_req" | _digest "$_hash" hex)"
_debug2 _req "$_req"
_str="$_algo$n$_date$n$_scope$n$_req"
_debug2 _str "$_str"
_sig="$(printf %s "AWS4$AWS_SECRET_ACCESS_KEY" | _hex_dump | tr -d ' ')"
_secure_debug2 _sig "$_sig"
for _step in $(printf %s "$_scope" | tr '/' ' ') "$_str"; do
_debug2 _step "$_step"
_sig="$(printf %s "$_step" | _hmac "$_hash" "$_sig" hex)"
_debug2 _sig "$_sig"
done
_cred="$AWS_ACCESS_KEY_ID/$_scope"
_auth="$_algo Credential=$_cred, SignedHeaders=$_keys, Signature=$_sig"
_debug2 _auth "$_auth"
_url="https://$_host$_path"
if [ "$_query" ]; then
_url="$_url?$_query"
fi
unset i
while read -r _line; do
i=$((i + 1))
eval "_H$i=\"\$_line\"; _debug2 _H$i \"\$_H$i\""
done <<-END
authorization:$_auth
$_hdrs
END
case "$(printf %s "$_verb" | tr '[:upper:]' '[:lower:]')" in
get) _get "$_url" ;;
post) _post "$_data" "$_url" ;;
*) _err '_aws only supports get and post' ;;
esac
}
# credentials
_aws_auth() {
_aws_auth_environment || _aws_auth_container_role || _aws_auth_instance_role
}
_aws_auth_environment() {
AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
return 1
fi
if [ -z "$_aws_using_role" ]; then
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
fi
}
_aws_auth_container_role() {
# automatically set if running inside ECS
if [ -z "$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" ]; then
_debug 'no ECS environment variable detected'
return 1
fi
_aws_auth_metadata "169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
}
_aws_auth_instance_role() {
_url='http://169.254.169.254/latest/meta-data/iam/security-credentials/'
_debug _url "$_url"
_aws_role=$(_get "$_url" '' 1)
if [ "$?" -gt 0 ]; then
_debug 'unable to fetch IAM role from instance metadata'
return 1
fi
_debug _aws_role "$_aws_role"
_aws_auth_metadata "$_url$_aws_role"
}
_aws_auth_metadata() {
_url="$1"
_aws_creds="$(
_get "$_url" "" 1 \
| _normalizeJson \
| tr '{,}' '\n' \
| while read -r _line; do
_key="$(printf %s "${_line%%:*}" | tr -d '"')" _value="${_line#*:}"
_debug3 _key "$_key"
_secure_debug3 _value "$_value"
case "$_key" in
AccessKeyId) printf '%s\n' "AWS_ACCESS_KEY_ID=$_value" ;;
SecretAccessKey) printf '%s\n' "AWS_SECRET_ACCESS_KEY=$_value" ;;
Token) printf '%s\n' "AWS_SESSION_TOKEN=$_value" ;;
esac
done \
| paste -sd' ' -
)"
_secure_debug _aws_creds "$_aws_creds"
if [ -z "$_aws_creds" ]; then
return 1
fi
eval "$_aws_creds"
_aws_using_role=true
}