🗝
summary refs log tree commit diff
path: root/porkbun.sh
blob: 1daae2e5a237387a226822a36d11b7ffa239de44 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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