Skip to content

Instantly share code, notes, and snippets.

@guss77
Last active October 6, 2023 09:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guss77/01f095623a1d2fd00869784554d3e1a5 to your computer and use it in GitHub Desktop.
Save guss77/01f095623a1d2fd00869784554d3e1a5 to your computer and use it in GitHub Desktop.
#!/bin/bash -e
verbosity=info
tokfile="/tmp/tok-$$"
domlist_pfx="/tmp/doms-$$-"
doctl_tokens=(
# Place all your DO personal access tokens here in this array as simple text, preferably one per line
)
function log() {
local level="$1" dolog=
shift
local message="$*"
case "$verbosity" in
debug) dolog=1;;
info) [ "$level" == "debug" ] || dolog=1;;
warn) [ "$level" == "debug" -o "$level" == "info" ] || dolog=1;;
error) [ "$level" == "error" ] && dolog=1;;
esac
[ -n "$dolog" ] && logger -st "$(basename $0)" "$message"
}
for level in debug info warn error; do
eval "function $level() { log $level \"\$*\"; }"
done
function doctl() {
[ -f $tokfile ] || return 1
/app/auth/doctl -t $(cat $tokfile) "$@"
}
function cache_domain_auth() {
for token in ${doctl_tokens[@]}; do
echo $token > $tokfile
doctl compute domain list --no-header | cut -d' ' -f1 > ${domlist_pfx}${token}
done
}
function auth_domain() {
local zoneid="$1" host_name="$2" token="$3"
doctl compute domain records create "${zoneid}" \
--record-name "${host_name%.$zoneid}" --record-type TXT --record-data "${token}" \
--record-ttl 30 >&2
}
function clear_token() {
local zoneid="$1" host_name="$2" token="$3"
doctl compute domain records list "${zoneid}" | grep TXT | fgrep "${host_name%.$zoneid} " | while read rid nop; do
doctl compute domain records delete "${zoneid}" "$rid" -f
done
}
function get_ns() {
local domain="$1"
dig ns "$domain" | awk '$1!~/^;/&&$4=="NS"{print$5}' | head -n1
}
function get_tokens() {
local domain="$1" host_name="$2"
dig @$(get_ns "$domain") txt "$host_name" | awk '$1!~/^;/&&$4=="TXT"{print substr($0, index($0,$5))}'
}
function wait_for_record() {
local domain="$1" host_name="$2" token="$3"
start=$(date +%s)
count=0
while ! (get_tokens "$domain" "$host_name" | grep -q '"'"$token"'"'); do
debug "Still waiting for $host_name from $domain $(( $(date +%s) - $start ))"
sleep 2
count=$(( $count + 1 ))
if [ "$count" -gt 30 ]; then
error "Timedout verifying token '$3' on $host_name from $domain"
exit 2
fi
done
sleep 15 # wait for DNS propagation time. Ideally we won't need to do that, or at least only as the last challenge
# but certbot doesn't send promised $CERTBOT_REMAINING_CHALLENGES nor supports --manual-propagation-seconds option
}
function find_zone() {
local lookup="$1"
for token in ${doctl_tokens[@]}; do
for zone in $(cat ${domlist_pfx}${token}); do
debug "Chceking $lookup against DigitalOcean zone $zone"
[ "${lookup%$zone}" == "$lookup" ] && continue # certbot domain is not in this zone
echo $token > $tokfile
echo $zone
return
done
done
error "Failed to find valid zone for $lookup!"
return 1
}
function cleanup() {
for token in ${doctl_tokens[@]}; do rm -f ${domlist_pfx}${token}; done
rm -f $tokfile
}
cache_domain_auth
find_zone "$CERTBOT_DOMAIN" | while read zoneid; do
if [[ -v CERTBOT_AUTH_OUTPUT ]]; then # cleanup
info "Cleaning up ACME challenge for $CERTBOT_DOMAIN in $zoneid" >&2
clear_token "$zoneid" "_acme-challenge.$CERTBOT_DOMAIN" "$CERTBOT_VALIDATION"
else # auth
info "Generating ACME challenge for $CERTBOT_DOMAIN in $zoneid"
auth_domain "$zoneid" "_acme-challenge.$CERTBOT_DOMAIN" "$CERTBOT_VALIDATION"
wait_for_record "$zoneid" "_acme-challenge.$CERTBOT_DOMAIN" "$CERTBOT_VALIDATION"
fi
done
info "Done with $CERTBOT_DOMAIN"
cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment