Advertisement
bearer

cloudflare-dns-hook.sh

Aug 15th, 2016
361
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 9.08 KB | None | 0 0
  1. #!/bin/bash
  2. #updated 20180118 new link/name for letsencrypt.sh->dehydrated and new startup_hook.
  3. #use with:    wget https://github.com/lukas2511/dehydrated/raw/master/dehydrated
  4. #download as: wget http://pastebin.com/raw/e3NyNV49 -O cloudflare-dns-hook.sh && sed -i 's/\r$//' cloudflare-dns-hook.sh
  5. #use as: ./dehydrated -c -t dns-01 -k ./cloudflare-dns-hook.sh  -d example.com -d www.internal.example.com
  6.  
  7. #cloudflare api credentials: (uncomment!).
  8. #email=user@example.com
  9. #key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  10. #can cache zoneid if using single domain
  11. #zoneid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  12.  
  13. parse_domain() {
  14.   local fqdn=${1}
  15.   domain= subdomain=
  16.   domainregex='([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,})$'
  17.   #regex from http://stackoverflow.com/a/30007882
  18.   if [[ $fqdn =~ $domainregex ]]; then
  19.     domain="${BASH_REMATCH[1]}"
  20.     subdomain=${1%.$domain}
  21.     if [[ "$domain" == "$subdomain" ]]; then
  22.       subdomain=
  23.     fi
  24.   else
  25.     echo "   unable to determine registered domain for FQDN $fqdn"
  26.     exit 1
  27.   fi
  28.   echo "   Domain: $domain Subdomain: $subdomain"
  29. }
  30.  
  31. query_zoneid() {
  32.   local domain=${1}
  33.   zoneid=
  34.   echo "   Querying for zone id for $1"
  35.   json=`curl -X GET "https://api.cloudflare.com/client/v4/zones?name=$domain&status=active&page=1&per_page=20&order=status&direction=desc&match=all" \
  36.   -H "X-Auth-Email: $email" \
  37.   -H "X-Auth-Key: $key" \
  38.   -H "Content-Type: application/json" 2> /dev/null`
  39.   regex="\"id\":\"([0-9a-f]{32})\",\"name\":\"$domain\""
  40.   if [[ $json =~ $regex ]]; then
  41.       zoneid="${BASH_REMATCH[1]}"
  42.       echo $domain = $zoneid
  43.   else
  44.       echo "   Unable to determine id for domain $domain"
  45.       echo $json
  46.       exit 1
  47.   fi
  48. }
  49.  
  50. create_record() {
  51.   local zoneid=${1} type=${2} subdomain=${3} challenge=${4}
  52.   echo "   Creating $type $subdomain = $challenge"
  53.   json=`curl -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \
  54.   -H "X-Auth-Email: $email" \
  55.   -H "X-Auth-Key: $key" \
  56.   -H "Content-Type: application/json" \
  57.   --data "{\"type\":\"$type\",\"name\":\"$subdomain\",\"content\":\"$challenge\",\"ttl\":120}" 2> /dev/null`
  58.   regex="\"success\":true"
  59.   if [[ $json =~ $regex ]]; then
  60.       echo "   Success! New Record $type $subdomain = $challenge"
  61.   else
  62.       echo "   Unable to create record $type $subdomain = $challenge"
  63.       echo $json
  64.       exit 1
  65.   fi
  66. }
  67.  
  68. query_recordid() {
  69.   local zoneid=${1} type=${2} fqdn=${3} challenge=${4}
  70.   recordid=
  71.   echo "   Querying record id for $type $fqdn = $challenge"
  72.   json=`curl -X GET "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records?type=$type&name=$fqdn&content=$challenge&page=1&per_page=20&order=type&direction=desc&match=all" \
  73.   -H "X-Auth-Email: $email" \
  74.   -H "X-Auth-Key: $key" \
  75.   -H "Content-Type: application/json" 2> /dev/null`
  76.   regex="\"id\":\"([0-9a-f]{32})\",\"type\":\"$type\",\"name\""
  77.   if [[ $json =~ $regex ]]; then
  78.       recordid="${BASH_REMATCH[1]}"
  79.       #echo "   Success Zone id=$zoneid, Record id=$recordid"
  80.   else
  81.       echo "   Unable to determinerecord id for $type $subdomain = $challenge"
  82.       echo $json
  83.       exit 1
  84.   fi
  85. }
  86.  
  87. delete_record() {
  88.   local zoneid=${1} recordid=${2} type=${3} fqdn=${4} challenge=${5}
  89.   json=`curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$recordid" \
  90.   -H "X-Auth-Email: $email" \
  91.   -H "X-Auth-Key: $key" \
  92.   -H "Content-Type: application/json" 2> /dev/null`
  93.   regex="\"success\":true"
  94.   if [[ $json =~ $regex ]]; then
  95.       echo "   Success! Deleted $type $fqdn = $challenge"
  96.   else
  97.       echo "   Unable to delete record $recordid $recordid $type $fqdn = $challenge"
  98.       echo $json
  99.       exit 1
  100.   fi
  101. }
  102.  
  103. deploy_challenge() {
  104.     local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
  105.     local fqdn=$DOMAIN
  106.  
  107.     # This hook is called once for every domain that needs to be
  108.     # validated, including any alternative names you may have listed.
  109.     #
  110.     # Parameters:
  111.     # - DOMAIN
  112.     #   The domain name (CN or subject alternative name) being
  113.     #   validated.
  114.     # - TOKEN_FILENAME
  115.     #   The name of the file containing the token to be served for HTTP
  116.     #   validation. Should be served by your web server as
  117.     #   /.well-known/acme-challenge/${TOKEN_FILENAME}.
  118.     # - TOKEN_VALUE
  119.     #   The token value that needs to be served for validation. For DNS
  120.     #   validation, this is what you want to put in the _acme-challenge
  121.     #   TXT record. For HTTP validation it is the value that is expected
  122.     #   be found in the $TOKEN_FILENAME file.
  123.     [ -z "$email" ] || [ -z "$key" ] && echo "($0) Error! Need to set email and key variables" && exit 1
  124.     parse_domain $fqdn
  125.     [ -z "$zoneid" ] && query_zoneid $domain
  126.     challengename=$([ -z "$subdomain"  ] && echo "_acme-challenge" || echo "_acme-challenge.$subdomain")
  127.     create_record $zoneid TXT $challengename $TOKEN_VALUE
  128.     secs=30
  129.     sleep $secs
  130. }
  131.  
  132. clean_challenge() {
  133.     local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
  134.     local fqdn=$DOMAIN
  135.  
  136.     # This hook is called after attempting to validate each domain,
  137.     # whether or not validation was successful. Here you can delete
  138.     # files or DNS records that are no longer needed.
  139.     #
  140.     # The parameters are the same as for deploy_challenge.
  141.     [ -z "$email" ] || [ -z "$key" ] && echo "($0) Error! Need to set email and key variables" && exit 1
  142.     parse_domain $fqdn
  143.     [ -z "$zoneid" ] && query_zoneid $domain
  144.     #API required the whole fqdn for searching, but only host/subdomain for creating. odd.
  145.     challengename=_acme-challenge.$fqdn
  146.     query_recordid $zoneid TXT $challengename $TOKEN_VALUE
  147.     delete_record $zoneid $recordid TXT $challengename $TOKEN_VALUE
  148. }
  149.  
  150. deploy_cert() {
  151.     local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
  152.  
  153.     # This hook is called once for each certificate that has been
  154.     # produced. Here you might, for instance, copy your new certificates
  155.     # to service-specific locations and reload the service.
  156.     #
  157.     # Parameters:
  158.     # - DOMAIN
  159.     #   The primary domain name, i.e. the certificate common
  160.     #   name (CN).
  161.     # - KEYFILE
  162.     #   The path of the file containing the private key.
  163.     # - CERTFILE
  164.     #   The path of the file containing the signed certificate.
  165.     # - FULLCHAINFILE
  166.     #   The path of the file containing the full certificate chain.
  167.     # - CHAINFILE
  168.     #   The path of the file containing the intermediate certificate(s).
  169.     # - TIMESTAMP
  170.     #   Timestamp when the specified certificate was created.
  171. }
  172.  
  173. unchanged_cert() {
  174.     local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
  175.  
  176.     # This hook is called once for each certificate that is still
  177.     # valid and therefore wasn't reissued.
  178.     #
  179.     # Parameters:
  180.     # - DOMAIN
  181.     #   The primary domain name, i.e. the certificate common
  182.     #   name (CN).
  183.     # - KEYFILE
  184.     #   The path of the file containing the private key.
  185.     # - CERTFILE
  186.     #   The path of the file containing the signed certificate.
  187.     # - FULLCHAINFILE
  188.     #   The path of the file containing the full certificate chain.
  189.     # - CHAINFILE
  190.     #   The path of the file containing the intermediate certificate(s).
  191. }
  192.  
  193. invalid_challenge() {
  194.     local DOMAIN="${1}" RESPONSE="${2}"
  195.  
  196.     # This hook is called if the challenge response has failed, so domain
  197.     # owners can be aware and act accordingly.
  198.     #
  199.     # Parameters:
  200.     # - DOMAIN
  201.     #   The primary domain name, i.e. the certificate common
  202.     #   name (CN).
  203.     # - RESPONSE
  204.     #   The response that the verification server returned
  205. }
  206.  
  207. request_failure() {
  208.     local STATUSCODE="${1}" REASON="${2}" REQTYPE="${3}"
  209.  
  210.     # This hook is called when an HTTP request fails (e.g., when the ACME
  211.     # server is busy, returns an error, etc). It will be called upon any
  212.     # response code that does not start with '2'. Useful to alert admins
  213.     # about problems with requests.
  214.     #
  215.     # Parameters:
  216.     # - STATUSCODE
  217.     #   The HTML status code that originated the error.
  218.     # - REASON
  219.     #   The specified reason for the error.
  220.     # - REQTYPE
  221.     #   The kind of request that was made (GET, POST...)
  222. }
  223.  
  224. startup_hook() {
  225.   # This hook is called before the cron command to do some initial tasks
  226.   # (e.g. starting a webserver).
  227.  
  228.   [ -z "$email" ] || [ -z "$key" ] && echo "($0) Error! Need to set email and key variables" && exit 1
  229.   echo -n "   Testing Cloudflare API for $email..."
  230.   json=`curl -X GET "https://api.cloudflare.com/client/v4/user" \
  231.   -H "X-Auth-Email: $email" \
  232.   -H "X-Auth-Key: $key" \
  233.   -H "Content-Type: application/json" 2> /dev/null`
  234.   regex="\"success\":true"
  235.   if [[ $json =~ $regex ]]; then
  236.       echo " Success!"
  237.   else
  238.       echo " Failure!!"
  239.           echo $json
  240.           exit 1
  241.   fi
  242. }
  243.  
  244. exit_hook() {
  245.   # This hook is called at the end of the cron command and can be used to
  246.   # do some final (cleanup or other) tasks.
  247.  
  248.   :
  249. }
  250.  
  251.  
  252. HANDLER="$1"; shift
  253. if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|startup_hook|exit_hook)$ ]]; then
  254.   "$HANDLER" "$@"
  255. fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement