From 5f5e6cd179e06758401d7ff09a5bf36cabe3d246 Mon Sep 17 00:00:00 2001 From: Ansel Horn Date: Wed, 17 Jul 2024 15:12:35 -0400 Subject: [PATCH] ddns-scripts: add support for Porkbun Extends DDNS support for the Porkbun v3 JSON API with a custom update script and service configuration. See: https://porkbun.com/api/json/v3/documentation Depends on cURL (with SSL) for transport. Porkbun authentication API keys and secret keys are passed through the ddns-scripts "username" and "password" variables, respectively. As Porkbun DNS is currently backed by Cloudflare, also support ddns-scripts "rec_id" variable for specific record targeting. Signed-off-by: Ansel Horn --- net/ddns-scripts/Makefile | 37 ++++ .../files/usr/lib/ddns/update_porkbun_v3.sh | 162 ++++++++++++++++++ .../share/ddns/default/porkbun.com-v3.json | 9 + 3 files changed, 208 insertions(+) create mode 100644 net/ddns-scripts/files/usr/lib/ddns/update_porkbun_v3.sh create mode 100644 net/ddns-scripts/files/usr/share/ddns/default/porkbun.com-v3.json diff --git a/net/ddns-scripts/Makefile b/net/ddns-scripts/Makefile index 6233377d0e..103130558d 100644 --- a/net/ddns-scripts/Makefile +++ b/net/ddns-scripts/Makefile @@ -292,6 +292,22 @@ define Package/ddns-scrtips-one/description endef +define Package/ddns-scripts-porkbun + $(call Package/ddns-scripts/Default) + TITLE:=Extension for porkbun.com API v3 + DEPENDS:=ddns-scripts +curl + PROVIDES:=ddns-scripts_porkbun.com-v3 +endef + +define Package/ddns-scripts-porkbun/description + Dynamic DNS Client scripts extension for porkbun.com API v3 (require curl) + It requires: + "option username" to be a Porkbun API key + "option password" to be the corresponding Porkbun API secret key + "option domain" to be the FQDN for which to configure DDNS +endef + + define Build/Configure endef @@ -369,6 +385,7 @@ define Package/ddns-scripts-services/install rm $(1)/usr/share/ddns/default/transip.nl.json rm $(1)/usr/share/ddns/default/ns1.com.json rm $(1)/usr/share/ddns/default/one.com.json + rm $(1)/usr/share/ddns/default/porkbun.com-v3.json endef @@ -685,6 +702,25 @@ exit 0 endef +define Package/ddns-scripts-porkbun/install + $(INSTALL_DIR) $(1)/usr/lib/ddns + $(INSTALL_BIN) ./files/usr/lib/ddns/update_porkbun_v3.sh \ + $(1)/usr/lib/ddns + + $(INSTALL_DIR) $(1)/usr/share/ddns/default + $(INSTALL_DATA) ./files/usr/share/ddns/default/porkbun.com-v3.json \ + $(1)/usr/share/ddns/default/ +endef + +define Package/ddns-scripts-porkbun/prerm +#!/bin/sh +if [ -z "$${IPKG_INSTROOT}" ]; then + /etc/init.d/ddns stop +fi +exit 0 +endef + + $(eval $(call BuildPackage,ddns-scripts)) $(eval $(call BuildPackage,ddns-scripts-services)) $(eval $(call BuildPackage,ddns-scripts-utils)) @@ -704,3 +740,4 @@ $(eval $(call BuildPackage,ddns-scripts-pdns)) $(eval $(call BuildPackage,ddns-scripts-transip)) $(eval $(call BuildPackage,ddns-scripts-ns1)) $(eval $(call BuildPackage,ddns-scripts-one)) +$(eval $(call BuildPackage,ddns-scripts-porkbun)) diff --git a/net/ddns-scripts/files/usr/lib/ddns/update_porkbun_v3.sh b/net/ddns-scripts/files/usr/lib/ddns/update_porkbun_v3.sh new file mode 100644 index 0000000000..037a4aa6e7 --- /dev/null +++ b/net/ddns-scripts/files/usr/lib/ddns/update_porkbun_v3.sh @@ -0,0 +1,162 @@ +# +# Distributed under the terms of the GNU General Public License (GPL) version 2.0 +# 2024 Ansel Horn +# +# Script for DDNS support via Porkbun's v3 API for the OpenWRT ddns-scripts package. +# +# Will attempt to create a new or edit an existing A or AAAA record for the +# given domain and subdomain. Existing CNAME and ALIAS records WILL NOT BE +# EDITED OR DELETED! "username" and "password" configurations should be set to +# Porkbun API key and secret key, respectively. +# +# Porkbun API documentation: +# https://porkbun.com/api/json/v3/documentation#DNS%20Create%20Record +# + +# Source JSON parser +. /usr/share/libubox/jshn.sh + +# Set API base URL +# Porkbun has warned it may change API hostname in the future: +# https://porkbun.com/api/json/v3/documentation#apiHost +__API="https://api.porkbun.com/api/json/v3" + +# Check availability of cURL with SSL +[ -z "$CURL" ] && [ -z "$CURL_SSL" ] && write_log 14 "cURL with SSL support required! Please install" + +# Validate configuration +[ -z "$domain" ] && write_log 14 "Service section not configured correctly! Missing 'domain'" +[ -z "$username" ] && write_log 14 "Service section not configured correctly! Missing 'username'" +[ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing 'password'" + +# Split FQDN into domain and subdomain(s) +__DOMAIN_REGEX='^\(\(.*\)\.\)\?\([^.]\+\.[^.]\+\)$' +echo $domain | grep "$__DOMAIN_REGEX" > /dev/null || write_log 14 "Invalid domain! Check 'domain' config" +__DOMAIN=$(echo $domain | sed -e "s/$__DOMAIN_REGEX/\3/") +__SUBDOMAIN=$(echo $domain | sed -e "s/$__DOMAIN_REGEX/\2/") + +# Determine IPv4 or IPv6 address and record type +if [ "$use_ipv6" -eq 1 ]; then + expand_ipv6 "$__IP" __ADDR + __TYPE="AAAA" +else + __ADDR="$__IP" + __TYPE="A" +fi + + +# Inject authentication into API request JSON payload +function json_authenticate() { + json_add_string "apikey" "$username" + json_add_string "secretapikey" "$password" +} + +# Make Porkbun API call +# $1 - Porkbun API endpoint +# $2 - request JSON payload +function api_call() { + local response url + url="$__API/$1" + write_log 7 "API endpoint URL: $url" + write_log 7 "API request JSON payload: $2" + response=$($CURL --data "$2" "$url") + write_log 7 "API response JSON payload: $response" + echo "$response" + + +# Check Porkbun API response status +function json_check_status() { + local status + json_get_var status "status" + [ "$status" == "SUCCESS" ] || write_log 14 "API request failed!" +} + +# Review DNS record and, if it is the record we're looking for, get its id +function callback_review_record() { + local id name type + json_select "$2" + json_get_var id "id" + json_get_var name "name" + json_get_var type "type" + [ "$name" == "$domain" -a "$type" == "$__TYPE" ] && echo "$id" + json_select .. +} + +# Retrieve all DNS records, find the first appropriate A/AAAA record, and get its id +function find_existing_record_id() { + local request response + json_init + json_authenticate + request=$(json_dump) + response=$(api_call "/dns/retrieve/$__DOMAIN" "$request") + json_load "$response" + json_check_status + json_for_each_item callback_review_record "records" +} + +# Create a new A/AAAA record +function create_record() { + local request response + json_init + json_authenticate + json_add_string "name" "$__SUBDOMAIN" + json_add_string "type" "$__TYPE" + json_add_string "content" "$__ADDR" + request=$(json_dump) + response=$(api_call "/dns/create/$__DOMAIN" "$request") + json_load "$response" + json_check_status +} + +# Retrieve an existing record and get its content +# $1 - record id to retrieve +function retrieve_record_content() { + local content request response + json_init + json_authenticate + request=$(json_dump) + response=$(api_call "/dns/retrieve/$__DOMAIN/$1" "$request") + json_load "$response" + json_check_status + json_select "records" + json_select 1 + json_get_var content "content" + echo "$content" +} + +# Edit an existing A/AAAA record +# $1 - record id to edit +function edit_record() { + local request response + json_init + json_authenticate + json_add_string "type" "$__TYPE" + json_add_string "content" "$__ADDR" + request=$(json_dump) + response=$(api_call "/dns/edit/$__DOMAIN/$1" "$request") + json_load "$response" + json_check_status +} + + +# Try to identify an appropriate existing DNS record to update +if [ -z $rec_id]; then + write_log 7 "Retrieving DNS $__TYPE record" + __ID=$(find_existing_record_id) +else + write_log 7 "Using user-supplied DNS record id: $rec_id" + __ID=$rec_id +fi + +# Create or update DNS record with current IP address +if [ -z "$__ID" ]; then + write_log 7 "Creating new DNS $__TYPE record" + create_record +else + write_log 7 "Updating existing DNS $__TYPE record" + if [ "$(retrieve_record_content $__ID)" == "$__ADDR" ]; then + write_log 7 "Skipping Porkbun-unsupported forced noop update" + else + edit_record "$__ID" + fi +fi diff --git a/net/ddns-scripts/files/usr/share/ddns/default/porkbun.com-v3.json b/net/ddns-scripts/files/usr/share/ddns/default/porkbun.com-v3.json new file mode 100644 index 0000000000..69509c191a --- /dev/null +++ b/net/ddns-scripts/files/usr/share/ddns/default/porkbun.com-v3.json @@ -0,0 +1,9 @@ +{ + "name": "porkbun.com-v3", + "ipv4": { + "url": "update_porkbun_v3.sh" + }, + "ipv6": { + "url": "update_porkbun_v3.sh" + } +} -- 2.30.2