doran/scripts/hetzner/configure-porkbun-dns.sh

129 lines
3.6 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
require() {
command -v "$1" >/dev/null 2>&1 || { echo "missing command: $1" >&2; exit 1; }
}
require curl
require python3
: "${PORKBUN_API_KEY:?set PORKBUN_API_KEY}"
: "${PORKBUN_SECRET_API_KEY:?set PORKBUN_SECRET_API_KEY}"
: "${BASE_DOMAIN:?set BASE_DOMAIN}"
: "${PUBLIC_DOMAIN:=$BASE_DOMAIN}"
: "${DNS_MODE:=upsert}"
api_base="https://api.porkbun.com/api/json/v3"
if [[ "$PUBLIC_DOMAIN" == "$BASE_DOMAIN" ]]; then
root_name=""
elif [[ "$PUBLIC_DOMAIN" == *".$BASE_DOMAIN" ]]; then
root_name="${PUBLIC_DOMAIN%.${BASE_DOMAIN}}"
else
echo "PUBLIC_DOMAIN must equal BASE_DOMAIN or be a subdomain of BASE_DOMAIN" >&2
exit 1
fi
if [[ -n "$root_name" ]]; then
git_name="git.$root_name"
registry_name="registry.$root_name"
else
git_name="git"
registry_name="registry"
fi
payload() {
local name="$1"
local content="$2"
printf '{"apikey":"%s","secretapikey":"%s","name":"%s","type":"A","content":"%s","ttl":"600"}' \
"$PORKBUN_API_KEY" "$PORKBUN_SECRET_API_KEY" "$name" "$content"
}
list_records() {
curl -sSf "$api_base/dns/retrieve/$BASE_DOMAIN" \
-H 'Content-Type: application/json' \
--data "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_API_KEY\"}"
}
lookup_record_id() {
local fqdn="$1"
python3 - "$fqdn" "$(list_records)" <<'PY'
import json,sys
fqdn=sys.argv[1]
data=json.loads(sys.argv[2])
for rec in data.get('records', []):
if rec.get('type') == 'A' and rec.get('name') == fqdn:
print(rec.get('id',''))
break
PY
}
upsert_a_record() {
local name="$1"
local fqdn="$BASE_DOMAIN"
[[ -n "$name" ]] && fqdn="$name.$BASE_DOMAIN"
local record_id
record_id=$(lookup_record_id "$fqdn")
local body
body=$(printf '{"apikey":"%s","secretapikey":"%s","name":"%s","type":"A","content":"%s","ttl":"600"}' \
"$PORKBUN_API_KEY" "$PORKBUN_SECRET_API_KEY" "$name" "$SERVER_IP")
if [[ -n "$record_id" ]]; then
local delete_body
delete_body=$(printf '{"apikey":"%s","secretapikey":"%s"}' "$PORKBUN_API_KEY" "$PORKBUN_SECRET_API_KEY")
curl -fsS "$api_base/dns/delete/$BASE_DOMAIN/$record_id" \
-H 'Content-Type: application/json' \
--data "$delete_body" >/dev/null
curl -fsS "$api_base/dns/create/$BASE_DOMAIN" \
-H 'Content-Type: application/json' \
--data "$body" >/dev/null
echo "updated A $fqdn -> $SERVER_IP"
else
curl -fsS "$api_base/dns/create/$BASE_DOMAIN" \
-H 'Content-Type: application/json' \
--data "$body" >/dev/null
echo "created A $fqdn -> $SERVER_IP"
fi
}
delete_a_record() {
local name="$1"
local fqdn="$BASE_DOMAIN"
[[ -n "$name" ]] && fqdn="$name.$BASE_DOMAIN"
local record_id
record_id=$(lookup_record_id "$fqdn")
if [[ -n "$record_id" ]]; then
local body
body=$(printf '{"apikey":"%s","secretapikey":"%s"}' "$PORKBUN_API_KEY" "$PORKBUN_SECRET_API_KEY")
curl -fsS "$api_base/dns/delete/$BASE_DOMAIN/$record_id" \
-H 'Content-Type: application/json' \
--data "$body" >/dev/null
echo "deleted A $fqdn"
else
echo "skipped missing A $fqdn"
fi
}
case "$DNS_MODE" in
upsert)
: "${SERVER_IP:?set SERVER_IP}"
upsert_a_record "$root_name"
upsert_a_record "$git_name"
upsert_a_record "$registry_name"
echo "porkbun dns updated for $PUBLIC_DOMAIN, git.$PUBLIC_DOMAIN, registry.$PUBLIC_DOMAIN"
;;
delete)
delete_a_record "$root_name"
delete_a_record "$git_name"
delete_a_record "$registry_name"
echo "porkbun dns cleanup finished for $PUBLIC_DOMAIN, git.$PUBLIC_DOMAIN, registry.$PUBLIC_DOMAIN"
;;
*)
echo "unsupported DNS_MODE: $DNS_MODE" >&2
exit 1
;;
esac