From 5016065f2c1b959e578bc18cfcab226db970b401 Mon Sep 17 00:00:00 2001 From: mia Date: Sat, 8 Jun 2024 22:56:14 -0700 Subject: initial commit --- porkbun.sh | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 porkbun.sh (limited to 'porkbun.sh') diff --git a/porkbun.sh b/porkbun.sh new file mode 100755 index 0000000..1daae2e --- /dev/null +++ b/porkbun.sh @@ -0,0 +1,109 @@ +# dns hook for https://github.com/dehydrated-io/dehydrated +# shellcheck shell=bash + +# some code taken from: +# https://github.com/silkeh/pdns_api.sh +# https://github.com/spfguru/dehydrated4googlecloud + + +function porkbun-request() { + local endpoint="$1" json="$2" + + url="https://porkbun.com/api/json/v3/$endpoint" + body=$(echo "$json" | jq '.apikey = "'"$porkbun_api_key"'" | .secretapikey = "'"$porkbun_secret_key"'"') + resp=$(curl -X POST -sSL "$url" -H content-type:application/json --data "$body") + status=$(echo "$resp" | jq -r '.status') + if [ "$status" != "SUCCESS" ]; then + >&2 echo " ! Request to $url returned $status: $(echo "$resp" | jq -r '.message')" + exit 1 + fi + echo "$resp" +} + +function join() { local IFS="$1"; shift; echo "$*"; } + +function split-domain() { + local domain="${1}" + local all + + mapfile -t all < <(porkbun-request 'domain/listAll' '{}' | jq -r '.domains | map(.domain) | join(" ")') + + IFS='.' read -ra parts <<< "${domain}" + for (( i=${#parts[@]}-1; i>=0; i-- )); do + step="$(join . "${parts[@]:i}")" + # shellcheck disable=SC2128 # nuh uh + for check in $all; do + if [ "$step" = "$check" ]; then + echo -e "$step\n$(join . "${parts[@]:0:i}")" + return + fi + done + done + exit 1 +} + +function deploy_challenge() { + local domain="${1}" token_value="${3}" + echo " = Deploying challenge for $domain" + + mapfile -t split < <(split-domain "$domain") + challenge="_acme-challenge.${split[1]}" + porkbun-request "dns/create/${split[0]}" '{ + "name": "'"$challenge"'", + "type": "TXT", + "content": "'"$token_value"'" + }' > /dev/null + + echo " = Waiting for record to propogate" + for nameserver in $(dig "${split[0]}" NS +short); do + echo " = Checking NS $nameserver" + verified=false + while [ $verified = false ]; do + result="$(dig +short "_acme-challenge.$domain" txt "@$nameserver")" + while IFS= read -r line; do + line="${line//'"'/''}" + if [ "$line" = "$token_value" ]; then + verified=true + break + fi + done <<< "$result" + [ $verified = false ] && sleep 1 + done + done + + echo " = Deployed" +} + +function clean_challenge() { + local domain="${1}" token_value="${3}" + echo " = Cleaning up challenge for $domain" + + mapfile -t split < <(split-domain "$domain") + mapfile -t ids < <(porkbun-request "dns/retrieve/${split[0]}" '{}' \ + | jq -r '.records + | map(select(.name == "_acme-challenge.'"$domain"'" and .type == "TXT" and .content == "'"$token_value"'")) + | map(.id) + | join(" ")') + if [ "${#ids[@]}" != 0 ]; then + # shellcheck disable=SC2128 # nuh uh + for id in $ids; do + porkbun-request "dns/delete/${split[0]}/$id" '{}' > /dev/null + done + fi +} + +function config-get() { + local name="$1" + bash -c "source ~/.keys/sysconf.sh && echo \$$name" +} + +HANDLER="$1"; shift +if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge)$ ]]; then + porkbun_api_key=$(config-get PORKBUN_API_KEY) + porkbun_secret_key=$(config-get PORKBUN_SECRET_KEY) + if [ -z "$porkbun_api_key$porkbun_secret_key" ]; then + echo "missing porkbun secrets" + exit 1 + fi + "$HANDLER" "$@" +fi -- cgit 1.4.1