diff --git a/dns_scripts/00GoDaddy-README.txt b/dns_scripts/00GoDaddy-README.txt deleted file mode 100644 index 9973556..0000000 --- a/dns_scripts/00GoDaddy-README.txt +++ /dev/null @@ -1,63 +0,0 @@ -Using GoDaddy DNS for LetsEncrypt domain validation. - -Quick guide to setting up getssl for domain validation of -GoDaddy DNS domains. - -There are two prerequisites to using getssl with GoDaddy DNS: - -1) Obtain an API access key from developer.godaddy.com - At first sign-up, you will be required to take a "test" key. - This is NOT what you need. Accept it, then get a "Production" - key. At this writing, there is no charge - but you must have - a GoDaddy customer account. - - You must get the API key for the account which owns the domain - that you want to get certificates for. If the domains that you - manage are owned by more than one account, get a key for each. - - The access key consists of a "Key" and a "Secret". You need - both. - -2) Obtain JSON.sh - https://github.com/dominictarr/JSON.sh - -With those in hand, the installation procedure is: - -1) Put JSON.sh in the getssl DNS scripts directory - Default: /usr/share/getssl/dns_scripts - -2) Open your config file (the global file in ~/.getssl/getssl.cfg - or the per-account file in ~/.getssl/example.net/getssl.cfg - -3) Set the following options: - VALIDATE_VIA_DNS="true" - DNS_ADD_COMMAND="/usr/share/getssl/dns_scripts/dns_add_godaddy" - DNS_DEL_COMMAND="/usr/share/getssl/dns_scripts/dns_del_godaddy" - # The API key for your account/this domain - export GODADDY_KEY="..." GODADDY_SECRET="..." - # The base domain name(s) in which the challege records are stored - # E.g. if www.example.net is in the example.net zone: - export GODADDY_BASE="example.com example.net" - - 4) Set any other options that you wish (per the standard - directions.) Use the test CA to make sure that - everything is setup correctly. - -That's it. getssl example.net will now validate with DNS. - -To trace record additions and removals, run getssl as -GODADDY_TRACE=Y getssl example.net - -There are additional options, which are documented in the -*godaddy" files and dns_godaddy -h. - -Copyright (C) 2017, 2018 Timothe Litt litt at acm _dot org - -This sofware may be freely used providing this notice is included with -all copies. The name of the author may not be used to endorse -any other product or derivative work. No warranty is provided -and the user assumes all responsibility for use of this software. - -Report any issues to https://github.com/tlhackque/getssl/issues. - -Enjoy. - diff --git a/dns_scripts/GoDaddy-README.txt b/dns_scripts/GoDaddy-README.txt index d58ba73..54db3b8 100644 --- a/dns_scripts/GoDaddy-README.txt +++ b/dns_scripts/GoDaddy-README.txt @@ -34,6 +34,8 @@ With those in hand, the installation procedure is: DNS_DEL_COMMAND="/usr/share/getssl/dns_scripts/dns_del_godaddy" # The API key for your account/this domain export GODADDY_KEY="..." GODADDY_SECRET="..." + # If you have been using GODADDY_BASE previously, then it is no + longer necessary. The base domain will automatically be determined. 4) Set any other options that you wish (per the standard directions.) Use the test CA to make sure that diff --git a/dns_scripts/dns_add_godaddy b/dns_scripts/dns_add_godaddy index f9be745..0baf312 100755 --- a/dns_scripts/dns_add_godaddy +++ b/dns_scripts/dns_add_godaddy @@ -38,4 +38,4 @@ export GODADDY_KEY export GODADDY_SECRET export GODADDY_BASE -$GODADDY_SCRIPT -q add "${fulldomain}" "_acme-challenge.${fulldomain}." "${token}" +$GODADDY_SCRIPT -q add "_acme-challenge.${fulldomain}." "${token}" diff --git a/dns_scripts/dns_del_godaddy b/dns_scripts/dns_del_godaddy index 4a3228f..692dff8 100755 --- a/dns_scripts/dns_del_godaddy +++ b/dns_scripts/dns_del_godaddy @@ -36,4 +36,4 @@ export GODADDY_KEY export GODADDY_SECRET export GODADDY_BASE -$GODADDY_SCRIPT -q del "${fulldomain}" "_acme-challenge.${fulldomain}." "${token}" +$GODADDY_SCRIPT -q del "_acme-challenge.${fulldomain}." "${token}" diff --git a/dns_scripts/dns_godaddy b/dns_scripts/dns_godaddy index 0d41e6a..9e81584 100755 --- a/dns_scripts/dns_godaddy +++ b/dns_scripts/dns_godaddy @@ -2,7 +2,7 @@ # Copyright (C) 2017,2018 Timothe Litt litt at acm _dot org -VERSION="1.0.3" +VERSION="2.0" PROG="`basename $0`" # This script is used to update TXT records in GoDaddy DNS server @@ -42,8 +42,8 @@ while getopts 'dhj:k:s:t:qv' opt; do *) cat <&2 exit 3 fi -domain="$2" -domain="${domain%'.'}" -if [ -z "$domain" ]; then - echo "'domain' parameter is required, see -h" >&2 - exit 3 -fi -name="$3" +name="$2" if [ -z "$name" ]; then echo "'name' parameter is required, see -h" >&2 exit 3 fi ! [[ "$name" =~ [.]$ ]] && name="${name}.${domain}." -data="$4" +data="$3" if [ -z "$data" ]; then echo "'data' parameter is required, see -h" >&2 exit 3 @@ -207,28 +195,92 @@ fi [ -n "$DEBUG" ] && echo "$authhdr" >&2 -if [ "$op" = "add" ]; then - # May need to retry due to zone cuts - - while [[ "$domain" =~ [^.]+\.[^.]+ ]]; do - - reqname="$name" - # The API doesn't trim the base domain from the name (it used to) - # If specified, remove any listed base. - if [ -n "$GODADDY_BASE" ]; then - for GDB in $GODADDY_BASE; do - gdb="`echo "$GDB" | sed -e's/\\.$//;s/\\./\\\\./g;'`" - gdb="^(.+)\\.$gdb\\.?$" - if [[ "$name" =~ $gdb ]]; then - reqname="${BASH_REMATCH[1]}" - break; - fi - done - else - eval 'reqname="$''{name%'"'.$domain.'}"'"' +#strip off the last period +if [[ "$name" =~ ^.+\. ]]; then + name=${name%?} +fi + +reqdomain= +reqname= + +# GoDaddy REST API URL is in the format /v1/domains/{domain}/records/{type}/{name} +# for adding/updating (PUT) or deleting (DELETE) a record. The API will support up +# to three segments in domain names (ex. mydomain.com and www.mydomain.com) +# in order to determine which domain the API call will affect (both mydomain.com and +# www.mydomain.com will result in the modification of the mydomain.com domain. Any +# more than three segments (ex. sub.something.mydomain.com will result in +# the API throwing a MISMATCH_FORMAT error. +# +# Examples +# 1. If mydomain.com was provided to this script as the domain parameter, and +# _acme-challengemydomain.com was provided as the name, then the URL +# /v1/domains/mydomain.com/records/TXT/_acme-challenge will be used which +# +# 2. If www.mydomain.com was provided to this script as the domain parameter, +# and _acme-challenge.www.mydomain.com was provided as the name, then the +# URL /v1/domains/mydomain.com/records/TXT/_acme-challenge.www will be used. + +# Determine the domain and the name to use for the API the URL +# The name parameter given to us is in the format challenge.domain. +# (ex _acme-challenge.mydomain.com. - note the trailing period). We will just +# use the name given us to determine the domain + +while [[ "$name" =~ ^([^.]+)\.([^.]+.*) ]]; do + if [ -n "${reqname}" ]; then reqname="${reqname}."; fi + reqname="${reqname}${BASH_REMATCH[1]}" + testdomain="${BASH_REMATCH[2]}" + name=$testdomain + if [[ ! "$name" =~ [^.]+\.[^.]+ ]]; then + exit 1 + fi + + url="$API/$testdomain" + + [ -n "$DEBUG" ] && echo "Looking for domain ${testdomain}" + + response="$(curl -i -s -X GET --config - "${url}" <&2 <&2 + exit $sts fi - url="$API/$domain/records/TXT/$reqname" + if echo "$response" | grep -q '^HTTP/.* 200 '; then + [ -n "$DEBUG" ] && echo "Found domain ${testdomain}" + reqdomain=${testdomain} + break + fi + + code="`echo "$response" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" + if [ "$code" = 'NOT_FOUND' ]; then + continue + fi +done + + +if [ -z "$reqdomain" ] || [ -z "$reqname" ]; then + echo "Unable to determine domain or RR name" >&2 + exit 3 +fi + + + +if [ "$op" = "add" ]; then + + url="$API/$reqdomain/records/TXT/$reqname" request='[{"data":"'$data'","ttl":'$ttl'}]' [ -n "$DEBUG" ] && cat >&2 <&2 - exit $sts - fi - if ! echo "$result" | grep -q '^HTTP/.* 200 '; then - code="`echo "$result" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" - msg="`echo "$result" | grep '"message":' | sed -e's/^.*"message":"//; s/\".*$//'`" - if [ "$code" = "DUPLICATE_RECORD" ]; then - if [ -n "$VERB" ]; then - echo "$msg in $domain" >&2 - fi - exit 0 # Duplicate record is still success - fi - if [ "$code" = 'UNKNOWN_DOMAIN' ]; then - if [[ "$domain" =~ ^([^.]+)\.([^.]+\.[^.]+.*) ]]; then - [ -n "$DEBUG" ] && \ - echo "$domain unknown, trying ${BASH_REMATCH[2]}" >&2 - domain="${BASH_REMATCH[2]}" - continue; - fi + if [ $sts -ne 0 ]; then + echo "curl error $sts adding record" >&2 + exit $sts + fi + if ! echo "$result" | grep -q '^HTTP/.* 200 '; then + code="`echo "$result" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" + msg="`echo "$result" | grep '"message":' | sed -e's/^.*"message":"//; s/\".*$//'`" + if [ "$code" = "DUPLICATE_RECORD" ]; then + if [ -n "$VERB" ]; then + echo "$msg in $domain" >&2 fi - echo "Request failed $msg" >&2 - exit 1 + exit 0 # Duplicate record is still success fi - [ -n "$VERB" ] && echo "$domain: added $name $ttl TXT \"$data\"" >&2 - exit 0 - done + echo "Request failed $msg" >&2 + exit 1 + fi + [ -n "$VERB" ] && echo "$reqdomain: added $reqname $ttl TXT \"$data\"" >&2 + exit 0 + fi -# ----- Delete - -# There is no delete API -# But, it is possible to replace all TXT records. -# -# So, first query for all TXT records - -# May need to retry due to zone cuts - -while [[ "$domain" =~ [^.]+\.[^.]+ ]]; do - url="$API/$domain/records/TXT" - [ -n "$DEBUG" ] && echo "Query for TXT records to: $url" >&2 +if [ "$op" = "del" ]; then + url="$API/$reqdomain/records/TXT/$reqname" + [ -n "$DEBUG" ] && echo "Deleting challenge TXT records at: $url" >&2 - current="$(curl -i -s -X GET --config - "$url" <&2 @@ -312,132 +348,14 @@ Response $current -------- EOF - if ! echo "$current" | grep -q '^HTTP/.* 200 '; then + + if ! echo "$current" | grep -q '^HTTP/.* 204 '; then code="`echo "$current" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" msg="`echo "$current" | grep '"message":' | sed -e's/^.*"message":"//; s/\".*$//'`" - if [ "$code" = "UNKNOWN_DOMAIN" ]; then - if [[ "$domain" =~ ^([^.]+)\.([^.]+\.[^.]+.*) ]]; then - [ -n "$DEBUG" ] && echo \ - "$domain unknown, trying ${BASH_REMATCH[2]}" >&2 - domain="${BASH_REMATCH[2]}" - continue; - fi - fi echo "Request failed $msg" >&2 exit 1 fi - # Remove headers - - current="$(echo "$current" | sed -e'0,/^\r*$/d')" - break -done - - # The zone cut is known, so the replace can't fail due to UNKNOWN domain - -if [ "$current" = '[]' ]; then # No TXT records in zone - [ -n "$VERB" ] && echo "$domain: $name TXT \"$data\" does not exist" >&2 - [ -n "$DEBUG" ] && echo "No TXT records in $domain" >&2 - exit 1 # Intent was to change, so error status -fi - -[ -n "$DEBUG" ] && echo "Response is valid" - -# Prepare request to replace TXT RRSET - -# Parse JSON and select only the record structures, which are [index] { ...} -current="$(echo "$current" | $JSON | sed -n -e'/^\[[0-9][0-9]*\]/{ s/^\[[0-9][0-9]*\]//; p}')" -base="$current" - -[ -n "$DEBUG" ] && cat >&2 <&2 - exit 1 # Intent was to change DNS, so this is an error -fi - -# Remove whitespace and insert needed commmas -# -fmtnew="$new" -new=$(echo "$new" | sed -e"s/}/},/g; \$s/},/}/;" | tr -d '\t\n') - -if [ -z "$new" ]; then - [ -n "$VERB" ] && echo "Replacing last TXT record with a dummy (see -h)" >&2 - new='{"type":"TXT","name":"_dummy.record_","data":"_This record is not used_","ttl":601}' - dummy="t" - TAB=$'\t' - fmtnew="${TAB}$new" - if [ "$fmtnew" = "$base" ]; then - [ -n "$VERB" ] && echo "This tool can't delete a placeholder when it is the only TXT record" >&2 - exit 0 # Not really success, but retrying won't help. - fi + [ -n "$VERB" ] && echo "$reqdomain: deleted $reqname TXT \"$data\"" >&2 + exit 0 fi - -request="[$new]" - -[ -n "$DEBUG" ] && cat >&2 <&2 <&2 <&2 - exit $sts -fi -if ! echo "$result" | grep -q '^HTTP/.* 200 '; then - result="$(echo "$result" | sed -e'0,/^\r*$/d')" - code="`echo "$result" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" - msg="`echo "$result" | grep '"message":' | sed -e's/^.*"message":"//; s/\".*$//'`" - echo "Request failed $msg" >&2 - exit 1 -fi - -if [ -n "$VERB" ]; then - if [ -n "$dummy" ]; then - echo "$domain: replaced $name TXT \"$data\" with a placeholder" >&2 - else - echo "$domain: deleted $name TXT \"$data\"" >&2 - fi -fi -exit 0 -