🗝
summary refs log tree commit diff
path: root/porkbun.sh
diff options
context:
space:
mode:
authormia <mia@mia.jetzt>2024-06-08 22:56:14 -0700
committermia <mia@mia.jetzt>2024-06-08 22:56:14 -0700
commit5016065f2c1b959e578bc18cfcab226db970b401 (patch)
tree28b0826702f391651ebbf65d15ff06cf3f2227f5 /porkbun.sh
downloadcertificates-5016065f2c1b959e578bc18cfcab226db970b401.tar.gz
certificates-5016065f2c1b959e578bc18cfcab226db970b401.zip
initial commit
Diffstat (limited to 'porkbun.sh')
-rwxr-xr-xporkbun.sh109
1 files changed, 109 insertions, 0 deletions
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