Created
April 1, 2017 11:34
-
-
Save itay-grudev/b6836e3ddf8fb54caebf5f46ed6545c7 to your computer and use it in GitHub Desktop.
Acmetool ACME (Let's Encrypt) DNS hook using the Digital Ocean API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This is an example DNS hook script which uses the Digital Ocean API to | |
# update nameservers. The script waits until updates have propagated to all | |
# nameservers listed for a zone. The script fails if this takes more than 60 | |
# seconds by default; this timeout can be adjusted. | |
# | |
# Requirements: dig (bind-tools), jq | |
# | |
# The script is ready to use, but to use it you must create | |
# /etc/default/acme-dns-do or /etc/conf.d/acme-dns-do and set the following options: | |
# | |
# # Digital Ocean API token | |
# DIGITAL_OCEAN_API_TOKEN=0123456789abcdef | |
# | |
# # DNS synchronization timeout in seconds. Default is 60. | |
# DNS_SYNC_TIMEOUT=60 | |
# | |
# Having done this, rename it to /usr/lib[exec]/acme/hooks/dns-do. | |
# | |
# How to test this script: | |
# ./dns-do.hook challenge-dns-start example.com "" "foobar" | |
# ./dns-do.hook challenge-dns-stop example.com "" "foobar" | |
# | |
set -e | |
get_apex() { | |
local name="$1" | |
if [ -z "$name" ]; then | |
echo "$0: couldn't get apex for $name" >&2 | |
return 1 | |
fi | |
local ans="`dig +noall +answer SOA "${name}."`" | |
if [ "`echo "$ans" | grep SOA | wc -l`" == "1" -a "`echo "$ans" | grep CNAME | wc -l`" == "0" ]; then | |
APEX="$name" | |
return | |
fi | |
local sname="$(echo $name | sed 's/^[^.]\+\.//')" | |
get_apex "$sname" | |
} | |
waitns() { | |
local ns="$1" | |
for ctr in $(seq 1 "$DNS_SYNC_TIMEOUT"); do | |
[ "$(dig +short "@${ns}" TXT "_acme-challenge.${CH_HOSTNAME}." | grep -- "$CH_TXT_VALUE" | wc -l)" == "1" ] && return 0 | |
sleep 1 | |
done | |
# Best effort cleanup. | |
echo $0: timed out waiting ${DNS_SYNC_TIMEOUT}s for nameserver $ns >&2 | |
updns del || echo $0: failed to clean up records after timing out >&2 | |
return 1 | |
} | |
updns() { | |
# Create a new record | |
curl -X POST \ | |
-H "Content-Type: application/json" \ | |
-H "Authorization: Bearer ${DIGITAL_OCEAN_API_TOKEN}" \ | |
-d "{\"type\":\"TXT\",\"name\":\"_acme-challenge.${CH_HOSTNAME}.\",\"data\":\"${CH_TXT_VALUE}\"}" \ | |
"https://api.digitalocean.com/v2/domains/${APEX}/records" | |
} | |
cleardns() { | |
curl -X GET \ | |
-H "Content-Type: application/json" \ | |
-H "Authorization: Bearer ${DIGITAL_OCEAN_API_TOKEN}" \ | |
"https://api.digitalocean.com/v2/domains/${APEX}/records" | \ | |
jq ".domain_records | map(select(.type == \"TXT\")) | map(select(.name == \"_acme-challenge\")) | map(select(.data == \"${CH_TXT_VALUE}\")) | map(.id)[]" | \ | |
while read RECORD_ID; do | |
curl -X DELETE \ | |
-H "Content-Type: application/json" \ | |
-H "Authorization: Bearer ${DIGITAL_OCEAN_API_TOKEN}" \ | |
"https://api.digitalocean.com/v2/domains/${APEX}/records/${RECORD_ID}" | |
done | |
} | |
[ -e "/etc/default/acme-dns-do" ] && . /etc/default/acme-dns-do | |
[ -e "/etc/conf.d/acme-dns-do" ] && . /etc/conf.d/acme-dns-do | |
EVENT_NAME="$1" | |
CH_HOSTNAME="$2" | |
CH_TARGET_FILENAME="$3" | |
CH_TXT_VALUE="$4" | |
[ -z "$DNS_SYNC_TIMEOUT" ] && DNS_SYNC_TIMEOUT=60 | |
case "$EVENT_NAME" in | |
challenge-dns-start) | |
get_apex "$CH_HOSTNAME" | |
updns add | |
exit | |
# Wait for all nameservers to update. | |
for ns in $(dig +short NS "${APEX}."); do | |
waitns "$ns" | |
done | |
;; | |
challenge-dns-stop) | |
get_apex "$CH_HOSTNAME" | |
cleardns | |
;; | |
*) | |
exit 42 | |
;; | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment