Browse Source

GoDaddy Updates

- Support for domains longer than 3 segments (ex. 1.www.mydomain.com).
- No need for GODADDY_BASE.  Domain will be automatically determined from full RR name.
- Update to delete challenge records.
pull/735/head
Steve Kennedy 4 years ago
parent
commit
13f7cc9d9a
5 changed files with 120 additions and 263 deletions
  1. +0
    -63
      dns_scripts/00GoDaddy-README.txt
  2. +2
    -0
      dns_scripts/GoDaddy-README.txt
  3. +1
    -1
      dns_scripts/dns_add_godaddy
  4. +1
    -1
      dns_scripts/dns_del_godaddy
  5. +116
    -198
      dns_scripts/dns_godaddy

+ 0
- 63
dns_scripts/00GoDaddy-README.txt View File

@ -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.

+ 2
- 0
dns_scripts/GoDaddy-README.txt View File

@ -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


+ 1
- 1
dns_scripts/dns_add_godaddy View File

@ -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}"

+ 1
- 1
dns_scripts/dns_del_godaddy View File

@ -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}"

+ 116
- 198
dns_scripts/dns_godaddy View File

@ -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 <<EOF
Usage
$PROG [-dt -h -j JSON -k:KEY -s:SECRET -q] add domain name data [ttl]
$PROG [-dt -h -j JSON -k:KEY -s:SECRET -q] del domain name data
$PROG [-dt -h -j JSON -k:KEY -s:SECRET -q] add name data [ttl]
$PROG [-dt -h -j JSON -k:KEY -s:SECRET -q] del name data
Add or delete TXT records from GoDaddy DNS
@ -59,8 +59,6 @@ Arguments:
del - remove the specified record from the domain
domain is the domain name, e.g. example.org
name is the DNS record name to add, e.g. _acme-challenge.example.org.
Note that trailing '.' is significant in DNS.
@ -73,9 +71,6 @@ Arguments:
For minimal trace output (to override -q), define GODADDY_TRACE="y".
Options
-b Domain name(s) in which challenge records are stored
E.g. often, www.example.net is stored in example.net.
Default from GODADDY_BASE
-d Provide debugging output - all requests and responses
-h This help.
-j: Location of JSON.sh Default `dirname $0`/JSON.sh, or
@ -88,7 +83,6 @@ Options
All output, except for this help text, is to stderr.
Environment variables
GODADDY_BASE Domain name(s) in which challenge records are stored
GODADDY_JSON location of the JSOH.sh script
GODADDY_KEY default API key
GODADDY_SCRIPT location of this script, default location of JSON.sh
@ -149,19 +143,13 @@ if ! [[ "$op" =~ ^(add|del)$ ]]; then
echo "Operation must be \"add\" or \"del\"" >&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}" <<EOF
header = "Content-Type: application/json"
header = "$authhdr"
EOF
)"
sts=$?
[ -n "$DEBUG" ] && cat >&2 <<EOF
Response:
curl status = $sts
--------
$response
--------
EOF
if [ $sts -ne 0 ]; then
echo "curl error $sts querying domain" >&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 <<EOF
@ -252,54 +304,38 @@ curl status = $sts
$result
--------
EOF
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
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" <<EOF
header = "$authhdr"
current="$(curl -i -s -X DELETE --config - "$url" <<EOF
header = "$authhdr"
EOF
)"
sts=$?
if [ $sts -ne 0 ]; then
echo "curl error $sts for query" >&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 <<EOF
Old TXT RRSET:
$current
EOF
# Remove the desired record. The name must be relative. Order varies.
eval 'name="$''{name%'"'.$domain.'}"'"'
match="$(printf '"name":"%s","data":"%s","ttl":' "$name" "$data")"
cmd="$(printf 'echo %s%s%s | grep -v %s%s%s' "'" "$current" "'" "'" "$match" "'")"
eval 'new="$('"$cmd"')"'
match="$(printf '"data":"%s","name":"%s","ttl":' "$data" "$name")"
cmd="$(printf 'echo %s%s%s | grep -v %s%s%s' "'" "$current" "'" "'" "$match" "'")"
eval 'new="$('"$cmd"')"'
if [ "$new" = "$base" ]; then
[ -n "$VERB" ] && echo "$domain: $name TXT \"$data\" does not exist" >&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 <<EOF
New TXT RRSET will be
$fmtnew
EOF
url="$API/$domain/records/TXT"
[ -n "$DEBUG" ] && cat >&2 <<EOF
Replace (delete) request to: $url
--------
$request
--------
EOF
result="$(curl -i -s -X PUT -d "$request" --config - "$url" <<EOF
header = "Content-Type: application/json"
header = "$authhdr"
EOF
)"
sts=$?
[ -n "$DEBUG" ] && cat >&2 <<EOF
Result:
curl status = $sts
--------
$result
--------
EOF
if [ $sts -ne 0 ]; then
echo "curl error $sts deleting record" >&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

Loading…
Cancel
Save