Browse Source

Initial version of GoDaddy DNS validation support.

Uses the GoDaddy "Developer" API to add and remove the ACME challenge
records.
pull/289/head
Timothe Litt 9 years ago
parent
commit
f2a4f6bfa8
6 changed files with 549 additions and 0 deletions
  1. +3
    -0
      .gitattributes
  2. +9
    -0
      .gitignore
  3. +68
    -0
      dns_scripts/00GoDaddy-README.txt
  4. +39
    -0
      dns_scripts/dns_add_godaddy
  5. +37
    -0
      dns_scripts/dns_del_godaddy
  6. +393
    -0
      dns_scripts/dns_godaddy

+ 3
- 0
.gitattributes View File

@ -0,0 +1,3 @@
# Files not to include in .zip/.tar.gz archives
#
.git* export-ignore

+ 9
- 0
.gitignore View File

@ -0,0 +1,9 @@
*~
*#
*.swp
*.tmp
*.bak
*.tdy
*.tar.gz
*.orig
JSON.sh

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

@ -0,0 +1,68 @@
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) Create a "myscripts" directory under ~/.getssl/
2) Put JSON.sh in "myscripts"
3) Copy (or softlink from the distribution directory) the
following files to "myscripts":
dns_godaddy dns_add_godaddy dns_del_godaddy
None of these files need to be customized.
4) Open your config file (the global file in ~/.getssl/getssl.cfg
or the per-account file in ~/.getssl/example.net/getssl.cfg
5) Set the following options:
VALIDATE_VIA_DNS="true"
DNS_ADD_COMMAND="/path/to/myscripts/dns_add_godaddy"
DNS_DEL_COMMAND="/path/to/myscripts/dns_del_godaddy"
# The API key for your account/this domain
export GODADDY_KEY="..." GODADDY_SECRET="..."
Note that ~user/ probably won't work in the path.
6) 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 (2017) 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.

+ 39
- 0
dns_scripts/dns_add_godaddy View File

@ -0,0 +1,39 @@
#!/bin/bash
# Copyright (2017) Timothe Litt litt at acm _dot org
# Add token to GoDaddy dns using dns_godaddy
# You do not have to customize this script.
#
# Obtain the Key and Secret from https://developer.godaddy.com/getstarted
# You must obtain a "Production" key - NOT the "Test" key you're required
# to get first.
#
# Obtain JSON.sh from https://github.com/dominictarr/JSON.sh
# Place it in (or softlink it to) the same directory as $GODADDY_SCRIPT,
# or specify its location with GODADDY_JSON
#
# Define GODADDY_KEY and GO_DADDY_SECRET in your account or domain getssl.cfg
#
# See GoDaddy-README.txt for complete instructions.
fulldomain="$1"
token="$2"
[ -z "$GODADDY_SCRIPT" ] && GODADDY_SCRIPT="~/.getssl/myscripts/dns_godaddy"
[[ "$GODADDY_SCRIPT" =~ ^~ ]] && \
eval 'GODADDY_SCRIPT=`readlink -nf ' $GODADDY_SCRIPT '`'
if [ ! -x "$GODADDY_SCRIPT" ]; then
echo "$GODADDY_SCRIPT: not found. Please install, softlink or set GODADDY_SCRIPT to its full path"
echo "See GoDaddy-README.txt for complete instructions."
exit 3
fi
# JSON.sh is not (currently) used by add
export GODADDY_KEY
export GODADDY_SECRET
$GODADDY_SCRIPT -q add ${fulldomain} "_acme-challenge.${fulldomain}." "${token}"

+ 37
- 0
dns_scripts/dns_del_godaddy View File

@ -0,0 +1,37 @@
#!/bin/bash
# Copyright (2017) Timothe Litt litt at acm _dot org
# Remove token from GoDaddy dns using dns_godaddy
# You do not have to customize this script.
#
# Obtain the Key and Secret from https://developer.godaddy.com/getstarted
# You must obtain a "Production" key - NOT the "Test" key you're required
# to get first.
#
# Obtain JSON.sh from https://github.com/dominictarr/JSON.sh
# Place it in (or softlink it to) the same directory as $GODADDY_SCRIPT,
# or specify its location with GODADDY_JSON
#
# Define GODADDY_KEY and GO_DADDY_SECRET in your account or domain getssl.cfg
#
# See GoDaddy-README.txt for complete instructions.
fulldomain="$1"
token="$2"
[ -z "$GODADDY_SCRIPT" ] && GODADDY_SCRIPT="~/.getssl/myscripts/dns_godaddy"
[[ "$GODADDY_SCRIPT" =~ ^~ ]] && \
eval 'GODADDY_SCRIPT=`readlink -nf ' $GODADDY_SCRIPT '`'
if ! [ -x "$GODADDY_SCRIPT" ]; then
echo "$GODADDY_SCRIPT: not found. Please install, softlink or set GODADDY_SCRIPT to its full path"
echo "See GoDaddy-README.txt for complete instructions."
exit 3
fi
export GODADDY_KEY
export GODADDY_SECRET
$GODADDY_SCRIPT -q del ${fulldomain} "_acme-challenge.${fulldomain}." "${token}"

+ 393
- 0
dns_scripts/dns_godaddy View File

@ -0,0 +1,393 @@
#!/bin/bash
# Copyright (2017) Timothe Litt litt at acm _dot org
VERSION="1.0.0"
# This script is used to update TXT records in GoDaddy DNS server
# It depends on JSON.sh from https://github.com/dominictarr/JSON.sh
# Place it in (or softlink it to) the same directory as this script,
# or specify its location with GODADDY_JSON
#
# See the usage text below, 00GoDaddy-README.txt, dns_add_godaddy
# and dns_del_godaddy for additional information.
#
# It 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.
#
# Bug reports are welcome at https://github.com/tlhackque/getssl/issues.
API='https://api.godaddy.com/v1/domains'
APISIGNUP='https://developer.godaddy.com/getstarted'
GETJSON='https://github.com/dominictarr/JSON.sh'
VERB="y"
DEBUG="$GODADDY_DEBUG"
[ -z "$JSON" ] && JSON="$GODADDY_JSON"
[ -z "$JSON" ] && JSON="`dirname $0`/JSON.sh"
while getopts 'dhj:k:s:qv' opt; do
case $opt in
d) DEBUG="Y" ;;
j) JSON="$OPTARG" ;;
k) GODADDY_KEY="$OPTARG" ;;
s) GODADDY_SECRET="$OPTARG" ;;
q) VERB= ;;
v) echo "dns_godaddy version $VERSION"; exit 0 ;;
*)
cat <<EOF
Usage
`basename $0` [-d -h -j JSON -k:KEY -s:SECRET -q] add domain name data [ttl]
`basename $0` [-d -h -j JSON -k:KEY -s:SECRET -q] del domain name data
Add or delete TXT records from GoDaddy DNS
Obtain the Key and Secret from $APISIGNUP
You must obtain a "Production" key - NOT the "Test" key you're required
to get first.
With getssl, this script is called from the dns_add_godaddy and
dns_del_godaddy wrapper scripts.
Arguments:
add - add the specified record to the domain
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.
data is the record data, e.g. "myverificationtoken"
ttl is optional, and defaults to the GoDaddy minimum of 600.
If it is necessary to turn on debugging externally, define
GODADDY_DEBUG="y" (any non-null string will do).
For minimal trace output (to override -q), define GODADDY_TRACE="y".
Options
-d Provide debugging output - all requests and responses
-h This help.
-j: Location of JSON.sh Default `dirname $0`/JSON.sh, or
the GODADDY_JSON variable.
-k: The GoDaddy API key Default from GODADDY_KEY
-s: The GoDaddy API secret Default from GODADDY_SECRET
-q Quiet - omit normal success messages,
All ourput, except for this help text, is to stderr.
Environment variables
GODADDY_JSON location of the JSOH.sh script
GODADDY_KEY default API key
GODADDY_SCRIPT location of this script, default location of JSON.sh
GODADDY_SECRET default API secret
GODADDY_TRACE forces -q off if true
BUGS
Due to a limitation of the gOdADDY API, deleting the last TXT record
would be too risky for my taste. So in that case, I replace it with
_dummy.record_.domain. TXT "Ihis record is not used". This record is
not automatically deleted by this script, though it's perfectly OK to
do so manually. (Via another mechanism, or if it's no longer the last.)
This really shouldn't happen often, since most domains have at least
one TXT record - for SPF, tracking APIs, etc.
Report any issues to https://github.com/tlhackque/getssl/issues
EOF
exit 0
;;
esac
done
shift $((OPTIND-1))
# Check for JSON -- required for delete, but if records are added,
# we assume they'll be deleted later & don't want to strand them.
[[ "$JSON" =~ ^~ ]] && \
eval 'JSON=`readlink -nf ' $JSON '`'
if [ ! -x "$JSON" ]; then
cat <<EOF >&2
$0: requires JSON.sh as "$JSON"
The full path to JSON.sh can be specified with -j, or the
GODADDY_JSON environment variable.
You can obtain a copy from $GETJSON
Then place or softlink it to $JSON or set GODADDY_JSON.
EOF
exit 2
fi
if [ -z "$GODADDY_KEY" ] || [ -z "$GODADDY_SECRET" ]; then
echo "GODADDY_KEY and GODADDY secret must be defined" >&2
exit 3
fi
[ -n "$DEBUG" ] && VERB="y"
[ -n "$GODADDY_TRACE" ] && VERB="Y"
# Get parameters & validate
op="$1"
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"
if [ -z "$name" ]; then
echo "'name' parameter is required, see -h" >&2
exit 3
fi
! [[ "$name" =~ [.]$ ]] && name="${name}.${domain}."
data="$4"
if [ -z "$data" ]; then
echo "'data' parameter is required, see -h" >&2
exit 3
fi
if [ "$op" = 'del' ]; then
ttl=
elif [ -z "$5" ]; then
ttl="600" # GoDaddy minimum TTL is 600
elif ! [[ "$5" =~ ^[0-9]+$ ]]; then
echo "TTL $5 is not numeric" >&2
exit 3
elif [ $5 -lt 600 ]; then
[ -n "$VERB" ] && \
echo "$5 is less than GoDaddy minimum of 600; increased to 600" >&2
ttl="600"
else
ttl="$5"
fi
# --- Done with parameters
[ -n "$DEBUG" ] && \
echo "`basename $0`: $op $domain $name \"$data\" $ttl" >&2
# Authorization header has secret and key
# N.B. These will appear in a 'ps' listing since curl only allows
# headers to be provided on the command line.
authhdr="Authorization: sso-key $GODADDY_KEY:$GODADDY_SECRET"
[ -n "$DEBUG" ] && echo "$authhdr" >&2
if [ "$op" = "add" ]; then
# May need to retry due to zone cuts
while [[ "$domain" =~ [^.]+\.[^.]+ ]]; do
url="$API/$domain/records/TXT/$name"
request='{"data":"'$data'","ttl":'$ttl'}'
[ -n "$DEBUG" ] && cat >&2 <<EOF
Add request to: $url
--------
$request"
--------
EOF
result="$(curl -i -s -X PUT -H "$authhdr" \
-H "Content-Type: application/json" \
-d "$request" "$url")"
sts=$?
[ -n "$DEBUG" ] && cat >&2 <<EOF
Result:
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
fi
echo "Request failed $msg" >&2
exit 1
fi
[ -n "$VERB" ] && echo "$domain: added $name $ttl TXT \"$data\"" >&2
exit 0
done
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
current="$(curl -i -s -X GET -H "$authhdr" "$url")"
sts=$?
if [ $sts -ne 0 ]; then
echo "curl error $sts for query" >&2
exit $sts
fi
[ -n "$DEBUG" ] && cat >&2 <<EOF
Response
--------
$current
--------
EOF
if ! echo "$current" | grep -q '^HTTP/.* 200 '; 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.
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"')"'
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
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 -H "$authhdr" \
-H "Content-Type: application/json" \
-d "$request" "$url")"
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