diff --git a/dns_scripts/GoDaddy-README.txt b/dns_scripts/GoDaddy-README.txt new file mode 100644 index 0000000..d58ba73 --- /dev/null +++ b/dns_scripts/GoDaddy-README.txt @@ -0,0 +1,60 @@ +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="..." + + 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 (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. + diff --git a/dns_scripts/dns_add_godaddy b/dns_scripts/dns_add_godaddy new file mode 100755 index 0000000..dfd3b3b --- /dev/null +++ b/dns_scripts/dns_add_godaddy @@ -0,0 +1,40 @@ +#!/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 The default is +# /usr/share/getssl/dns_scripts/ +# +# 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="/usr/share/getssl/dns_scripts/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}" diff --git a/dns_scripts/dns_add_joker b/dns_scripts/dns_add_joker old mode 100644 new mode 100755 index c345a52..c100886 --- a/dns_scripts/dns_add_joker +++ b/dns_scripts/dns_add_joker @@ -29,16 +29,16 @@ SID=$(curl --silent -X POST https://dmapi.joker.com/request/login \ curl --silent -X POST https://dmapi.joker.com/request/dns-zone-get \ -H "Accept: application/json" -H "User-Agent: getssl/0.1" \ -H "application/x-www-form-urlencoded" -d "domain=${DOMAIN_ROOT}&auth-sid=${SID}" | \ - tail -n +7 >${TMPFILE} + tail -n +7 >"${TMPFILE}" ## add txt record -printf "_acme-challenge.%s. TXT 0 \"%s \" 300\n\n" "${FULLDOMAIN}" "${TOKEN}" >>${TMPFILE} +printf "_acme-challenge.%s. TXT 0 \"%s \" 300\n\n" "${FULLDOMAIN}" "${TOKEN}" >>"${TMPFILE}" ## generate encoded url data -URLDATA=$(cat ${TMPFILE} | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') +URLDATA=$(cat "${TMPFILE}" | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') ## write new zonefile to joker curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1 ## remove tempfile -rm -f ${TMPFILE} +rm -f "${TMPFILE}" diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 14896d8..13b0fc9 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -2,16 +2,38 @@ # example of script to add token to local dns using nsupdate -dnskeyfile="path/to/bla.key" -server="localhost" fulldomain="$1" token="$2" -updatefile=$(mktemp) +# VARIABLES: +# +# DNS_NSUPDATE_KEYFILE - path to a TSIG key file, if required +# DNS_NSUPDATE_GETKEY - command to execute if access to the key file requires +# some special action: mounting a disk, decrypting a file.. +# Called with the operation 'add' and action 'open" / 'close' -printf "server ${server}\n" > "${updatefile}" -printf "update add _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" >> "${updatefile}" -nsupdate -k "${dnskeyfile}" -v "${updatefile}" +if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi -rm -f "${updatefile}" + options="-k ${DNS_NSUPDATE_KEYFILE}" +fi + +# Note that blank line is a "send" command to nsupdate + +nsupdate "${options}" -v <${TMPFILE} + tail -n +7 >"${TMPFILE}" ## remove txt record -sed -i "/_acme-challenge.${FULLDOMAIN}.*${TOKEN}.*/d" ${TMPFILE} +sed -i "/_acme-challenge.${FULLDOMAIN}.*${TOKEN}.*/d" "${TMPFILE}" ## generate encoded url data -URLDATA=$(cat ${TMPFILE} | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') +URLDATA=$(cat "${TMPFILE}" | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') ## write new zonefile to joker curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1 ## remove tempfile -rm -f ${TMPFILE} +rm -f "${TMPFILE}" diff --git a/dns_scripts/dns_del_nsupdate b/dns_scripts/dns_del_nsupdate index be41a7b..e8442e3 100755 --- a/dns_scripts/dns_del_nsupdate +++ b/dns_scripts/dns_del_nsupdate @@ -1,18 +1,39 @@ #!/bin/bash -# example of script to add token to local dns using nsupdate - -dnskeyfile="path/to/bla.key" -server="localhost" +# example of script to remove token from local dns using nsupdate fulldomain="$1" token="$2" -updatefile=$(mktemp) +# VARIABLES: +# +# DNS_NSUPDATE_KEYFILE - path to a TSIG key file, if required +# DNS_NSUPDATE_GETKEY - command to execute if access to the key file requires +# some special action: dismounting a disk, encrypting a +# file... Called with the operation 'del' and action +# 'open" / 'close' + +if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi + + options="-k ${DNS_NSUPDATE_KEYFILE}" +fi + +# Note that blank line is a "send" command to nsupdate + +nsupdate "${options}" -v < "${updatefile}" -printf "update delete _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" >> "${updatefile}" +sts=$? -nsupdate -k "${dnskeyfile}" -v "${updatefile}" +if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'close' "${fulldomain}" ; then + exit $(( sts + ( $? * 10 ) )) + fi +fi -rm -f "${updatefile}" +exit ${sts} diff --git a/dns_scripts/dns_del_ovh b/dns_scripts/dns_del_ovh old mode 100644 new mode 100755 index c242de0..bf7fe09 --- a/dns_scripts/dns_del_ovh +++ b/dns_scripts/dns_del_ovh @@ -1,35 +1,35 @@ -#!/bin/bash - -domains=($(echo "$1"|sed -e 's/^\(\([a-zA-Z0-9.-]*\?\)\.\)*\([a-zA-Z0-9-]\+\.[a-zA-Z-]\+\)$/"\1" _acme-challenge.\2 \3/g')) -challenge="$2" - -# Please, do not forget to ask for your credentials at https://eu.api.ovh.com/createToken/ -# permissions needed are /domain/zone/* in GET,POST,DELETE -applicationKey="YourAK" -applicationSecret="YourAS" -consumerKey="YourCK" - -topDomain=${domains[2]} -subDomain=${domains[1]%%.} - -function send -{ - method=$1 - url=$2 - body=$3 - ts=$(date +%s) - - sign=\$1\$$(echo -n "${applicationSecret}+${consumerKey}+${method}+https://eu.api.ovh.com/1.0${url}+${body}+${ts}"|sha1sum|cut -d" " -f1) - curl -X ${method} -H "Content-Type: application/json" -H "X-Ovh-Application: ${applicationKey}" -H "X-Ovh-Timestamp: ${ts}" -H "X-Ovh-Signature: ${sign}" -H "X-Ovh-Consumer: ${consumerKey}" -d "${body}" https://eu.api.ovh.com/1.0${url} -} - -# Creation request -oldResult=$(send GET "/domain/zone/${topDomain}/record?fieldType=TXT&subDomain=${subDomain}" ""|sed -e 's/\[//' -e 's/\]//') - -for num in ${oldResult//,/ } -do - send DELETE "/domain/zone/${topDomain}/record/${num}" "" -done - -# Refresh request -send POST /domain/zone/${topDomain}/refresh "" +#!/bin/bash + +domains=($(echo "$1"|sed -e 's/^\(\([a-zA-Z0-9.-]*\?\)\.\)*\([a-zA-Z0-9-]\+\.[a-zA-Z-]\+\)$/"\1" _acme-challenge.\2 \3/g')) +#challenge="$2" + +# Please, do not forget to ask for your credentials at https://eu.api.ovh.com/createToken/ +# permissions needed are /domain/zone/* in GET,POST,DELETE +applicationKey="YourAK" +applicationSecret="YourAS" +consumerKey="YourCK" + +topDomain=${domains[2]} +subDomain=${domains[1]%%.} + +function send +{ + method=$1 + url=$2 + body=$3 + ts=$(date +%s) + + sign=\$1\$$(echo -n "${applicationSecret}+${consumerKey}+${method}+https://eu.api.ovh.com/1.0${url}+${body}+${ts}"|sha1sum|cut -d" " -f1) + curl -X "${method}" -H "Content-Type: application/json" -H "X-Ovh-Application: ${applicationKey}" -H "X-Ovh-Timestamp: ${ts}" -H "X-Ovh-Signature: ${sign}" -H "X-Ovh-Consumer: ${consumerKey}" -d "${body}" "https://eu.api.ovh.com/1.0${url}" +} + +# Creation request +oldResult=$(send GET "/domain/zone/${topDomain}/record?fieldType=TXT&subDomain=${subDomain}" ""|sed -e 's/\[//' -e 's/\]//') + +for num in ${oldResult//,/ } +do + send DELETE "/domain/zone/${topDomain}/record/${num}" "" +done + +# Refresh request +send POST "/domain/zone/${topDomain}/refresh" "" diff --git a/dns_scripts/dns_godaddy b/dns_scripts/dns_godaddy new file mode 100644 index 0000000..4443bd4 --- /dev/null +++ b/dns_scripts/dns_godaddy @@ -0,0 +1,418 @@ +#!/bin/bash + +# Copyright (2017) Timothe Litt litt at acm _dot org + +VERSION="1.0.1" +PROG="`basename $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:t:qv' opt; do + case $opt in + d) DEBUG="Y" ;; + j) JSON="$OPTARG" ;; + k) GODADDY_KEY="$OPTARG" ;; + s) GODADDY_SECRET="$OPTARG" ;; + t) TRACE="$OPTARG" ;; + q) VERB= ;; + v) echo "dns_godaddy version $VERSION"; exit 0 ;; + *) + cat <&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" +[ -n "$GODADDY_TFILE" ] && TRACE="$GODADDY_TFILE" + +# 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 "$PROG: $op $domain $name \"$data\" $ttl" >&2 + +# Authorization header has secret and key + +authhdr="Authorization: sso-key $GODADDY_KEY:$GODADDY_SECRET" + +if [ -n "$TRACE" ]; then + function timestamp { local tm="`LC_TIME=C date '+%T.%N'`" + local class="$1"; shift + echo "${tm:0:15} ** ${class}: $*" >>"$TRACE" + } + timestamp 'Info' "$PROG" "V$VERSION" 'Starting new protocol trace' + timestamp 'Args' "$@" + curl --help | grep -q -- --trace-time && CURL_TFLAGS="--trace-time" # 7.14.0 + function curl { + command curl ${CURL_TFLAGS} --trace-ascii % "$@" 2>>"$TRACE" + } + [ -n "$VERB" ] && echo "Appending protocol trace to $TRACE" +fi + +[ -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 <&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 + 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 --config - "$url" <&2 + exit $sts + fi + [ -n "$DEBUG" ] && cat >&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 +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 +