Browse Source

Support AWS Route53 domain API

Support AWS Route53 domain API
neil 8 years ago
parent
commit
2e7cbfcff5
5 changed files with 245 additions and 10 deletions
  1. 1 1
      README.md
  2. 23 6
      acme.sh
  3. 18 2
      dnsapi/README.md
  4. 202 0
      dnsapi/dns_aws.sh
  5. 1 1
      dnsapi/dns_me.sh

+ 1 - 1
README.md

@@ -253,7 +253,7 @@ You don't have to do anything manually!
 1. CloudXNS.com API
 1. CloudXNS.com API
 1. GoDaddy.com API
 1. GoDaddy.com API
 1. OVH, kimsufi, soyoustart and runabove API
 1. OVH, kimsufi, soyoustart and runabove API
-1. AWS Route 53, see: https://github.com/Neilpang/acme.sh/issues/65
+1. AWS Route 53
 1. PowerDNS.com API
 1. PowerDNS.com API
 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
 1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
    (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
    (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)

+ 23 - 6
acme.sh

@@ -329,6 +329,18 @@ _h2b() {
   done
   done
 }
 }
 
 
+#hex string
+_hex() {
+  _str="$1"
+  _str_len=${#_str}
+  _h_i=1
+  while [ "$_h_i" -le "$_str_len" ]; do
+    _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")"
+    printf "%02x" "'$_str_c"
+    _h_i="$(_math "$_h_i" + 1)"
+  done
+}
+
 #options file
 #options file
 _sed_i() {
 _sed_i() {
   options="$1"
   options="$1"
@@ -426,23 +438,23 @@ _digest() {
 
 
 }
 }
 
 
-#Usage: hashalg  secret  [outputhex]
-#Output Base64-encoded hmac
+#Usage: hashalg  secret_hex  [outputhex]
+#Output binary hmac
 _hmac() {
 _hmac() {
   alg="$1"
   alg="$1"
-  hmac_sec="$2"
+  secret_hex="$2"
   outputhex="$3"
   outputhex="$3"
 
 
-  if [ -z "$hmac_sec" ]; then
+  if [ -z "$secret_hex" ]; then
     _usage "Usage: _hmac hashalg secret [outputhex]"
     _usage "Usage: _hmac hashalg secret [outputhex]"
     return 1
     return 1
   fi
   fi
 
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
     if [ "$outputhex" ]; then
     if [ "$outputhex" ]; then
-      openssl dgst -"$alg" -hmac "$hmac_sec" | cut -d = -f 2 | tr -d ' '
+      openssl dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" | cut -d = -f 2 | tr -d ' '
     else
     else
-      openssl dgst -"$alg" -hmac "$hmac_sec" -binary | _base64
+      openssl dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary
     fi
     fi
   else
   else
     _err "$alg is not supported yet"
     _err "$alg is not supported yet"
@@ -3601,6 +3613,11 @@ _initconf() {
 #PDNS_Token=\"0123456789ABCDEF\"
 #PDNS_Token=\"0123456789ABCDEF\"
 #PDNS_Ttl=60
 #PDNS_Ttl=60
 
 
+#######################
+#Amazon Route53:
+#AWS_ACCESS_KEY_ID=XXXXXXXXXX
+#AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX
+
     " >"$ACCOUNT_CONF_PATH"
     " >"$ACCOUNT_CONF_PATH"
   fi
   fi
 }
 }

+ 18 - 2
dnsapi/README.md

@@ -185,7 +185,23 @@ acme.sh --issue --dns dns_me -d example.com -d www.example.com
 The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 
 
 
 
-# 10. Use custom API
+## 10. Use Amazon Route53 domain API
+
+https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API
+
+```
+export  AWS_ACCESS_KEY_ID=XXXXXXXXXX
+export  AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX
+```
+
+To issue a cert:
+```
+acme.sh --issue --dns dns_aws -d example.com -d www.example.com
+```
+
+The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+# 11. Use custom API
 
 
 If your API is not supported yet, you can write your own DNS API.
 If your API is not supported yet, you can write your own DNS API.
 
 
@@ -202,6 +218,6 @@ acme.sh --issue --dns dns_myapi -d example.com -d www.example.com
 For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
 For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
 
 
 
 
-## 11. Use lexicon DNS API
+## 12. Use lexicon DNS API
 
 
 https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
 https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api

+ 202 - 0
dnsapi/dns_aws.sh

@@ -0,0 +1,202 @@
+#!/usr/bin/env sh
+
+#
+#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#AWS_SECRET_ACCESS_KEY="xxxxxxx"
+
+#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"
+
+########  Public functions #####################
+
+#Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_aws_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  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 "Please create you key and try again. see $(__green $AWS_WIKI)"
+    return 1
+  fi
+
+  _saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
+  _saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+  _debug _domain_id "$_domain_id"
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _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><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
+    _info "txt record updated sucess."
+    return 0
+  fi
+
+  return 1
+}
+
+#fulldomain
+dns_aws_rm() {
+  fulldomain=$1
+
+}
+
+####################  Private functions bellow ##################################
+
+_get_root() {
+  domain=$1
+  i=2
+  p=1
+
+  if aws_rest GET "2013-04-01/hostedzone"; then
+    _debug "response" "$response"
+    while true; do
+      h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+      if [ -z "$h" ]; then
+        #not valid
+        return 1
+      fi
+
+      if _contains "$response" "<Name>$h.</Name>"; then
+        hostedzone="$(echo "$response" | _egrep_o "<HostedZone>.*<Name>$h.</Name>.*</HostedZone>")"
+        _debug hostedzone "$hostedzone"
+        if [ -z "$hostedzone" ]; then
+          _err "Error, can not get hostedzone."
+          return 1
+        fi
+        _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*</Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
+        if [ "$_domain_id" ]; then
+          _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+          _domain=$h
+          return 0
+        fi
+        return 1
+      fi
+      p=$i
+      i=$(_math "$i" + 1)
+    done
+  fi
+  return 1
+}
+
+#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" ##############
+
+  _H1="x-amz-date: $RequestDate"
+
+  aws_host="$AWS_HOST"
+  CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
+  _debug2 CanonicalHeaders "$CanonicalHeaders"
+
+  SignedHeaders="host;x-amz-date"
+  _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" ############################
+
+  _debug2 kSecret "$kSecret"
+
+  kSecretH="$(_hex "$kSecret")"
+  _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 "aws4_request%s" | _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"
+
+  _H3="Authorization: $Authorization"
+  _debug _H3 "$_H3"
+
+  url="$AWS_URL/$ep"
+
+  if [ "$mtd" = "GET" ]; then
+    response="$(_get "$url")"
+  else
+    response="$(_post "$data" "$url")"
+  fi
+
+  _ret="$?"
+  if [ "$_ret" = "0" ]; then
+    if _contains "$response" "<ErrorResponse"; then
+      _err "Response error:$response"
+      return 1
+    fi
+  fi
+
+  return "$_ret"
+}

+ 1 - 1
dnsapi/dns_me.sh

@@ -124,7 +124,7 @@ _me_rest() {
   _debug "$ep"
   _debug "$ep"
 
 
   cdate=$(date -u +"%a, %d %b %Y %T %Z")
   cdate=$(date -u +"%a, %d %b %Y %T %Z")
-  hmac=$(printf "%s" "$cdate" | _hmac sha1 "$ME_Secret" 1)
+  hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(_hex "$ME_Secret")" hex)
 
 
   _H1="x-dnsme-apiKey: $ME_Key"
   _H1="x-dnsme-apiKey: $ME_Key"
   _H2="x-dnsme-requestDate: $cdate"
   _H2="x-dnsme-requestDate: $cdate"