Skip to content

Instantly share code, notes, and snippets.

@levid0s
Last active April 23, 2024 14:51
Show Gist options
  • Save levid0s/72e00bec62a900b6c65d3eee6ec4fe13 to your computer and use it in GitHub Desktop.
Save levid0s/72e00bec62a900b6c65d3eee6ec4fe13 to your computer and use it in GitHub Desktop.
OpenWRT DDNS Porkbun Updater
set -e
set -u
__usage="
$0
OpenWRT DDNS updater ('ddns-scripts') script for the Porkbun API.
The script has been extended to support running it directly (eg. via cron) without having to install the \`ddns-scripts\` opkg package.
Suggested script path: /usr/bin/ddns-update-porkbun.sh
Usage (via DDNS updater):
- Set the DDNS service provider to '-- custom --'
- Custom update-script: Path to this script
- Complete 'Lookup Hostname', 'Domain', 'Username' and 'Password' fields.
Usage directly #1:
Export the following envs before running the script:
\$hostname - Lookup hostname, the FQDN of the ddns record, eg. \`myhost.subdomain.mydomain.com\`
\$domain - The root of the porkbun domain, eg. \`mydomain.com\`
\$username - Porkbun API username ('apikey')
\$password - Porkbun API secret ('secretapikey')
\$__IP (optional) - The actual public IP. If not set, will be looked it up form icanhazip.com.
\$REGISTERED_IP (optional) - The last registered IP. If not set, it will be looked up using nslookup at 1.1.1.1.
Usage directly #2:
ddns-update-porkbun.sh <domain> <hostname> <username> <password>
eg:
ddns-update-porkbun.sh mydomain.com myhost.subdomain.mydomain.com pk1_b7..f8cd sk1_65..135f
"
if [[ "${1-}" == '-h' || "${1-}" == '--help' ]]; then
echo "$__usage"
exit 0
fi
if [[ -z "${__IP-}" ]]; then
# The script was started outside of the OpenWRT DDNS updater, so we need to exit when there are no IP changes, instead of triggering a forced DNS update.
manual_script_run='true'
[[ -z "${domain-}" && -n "${1-}" ]] && domain="$1"
[[ -z "${hostname-}" && -n "${2-}" ]] && hostname="$2"
[[ -z "${username-}" && -n "${3-}" ]] && username="$3"
[[ -z "${password-}" && -n "${4-}" ]] && password="$4"
fi
# Mocking write_log to add support for running the script directly.
command -v write_log >/dev/null || alias write_log=echo
write_log 5 'Starting: ddns-update-porkbun.sh'
[[ -z "${domain-}" ]] && { echo "ERROR: \$domain env missing.""$__usage"; exit 1; }
[[ -z "${hostname-}" ]] && { echo "ERROR: \$hostname env missing.""$__usage"; exit 1; }
[[ -z "${apikey-}" ]] && { echo "ERROR: \$apikey env missing.""$__usage"; exit 1; }
[[ -z "${secretapikey-}" ]] && { echo "ERROR: \$secretapikey env missing.""$__usage"; exit 1; }
if [[ -z "${__IP-}" ]]; then
__IP=$(wget -q https://icanhazip.com -O -)
write_log 5 "Detected IP: $__IP"
fi
if [[ -z "${REGISTERED_IP-}" ]]; then
REGISTERED_IP=$(nslookup -type=A "$hostname" 1.1.1.1 | grep -E 'Address: \d+' | cut -d\ -f2)
write_log 5 "Looked up registered IP: $REGISTERED_IP"
fi
porkbun_record="${hostname%.$domain}"
[[ "$__IP" == "$REGISTERED_IP" && -n "${manual_script_run-}" ]] && { write_log 5 "There are no IP changes, exiting: $__IP == $REGISTERED_IP"; exit 0; }
[[ "$__IP" == "$REGISTERED_IP" ]] && write_log 7 "The IP hasn't changed, DDNS probably triggered a forced update."
# Check if DNS record exists
current_record=$(wget --post-data="{ \"apikey\": \"$username\", \"secretapikey\": \"$password\" }" \
https://porkbun.com/api/json/v3/dns/retrieveByNameType/$domain/A/${porkbun_record} -O -)
echo $current_record | grep -q 'content' || record_missing='true'
if [[ "${record_missing-}" == 'true' ]]; then
# Create new DNS record
wget --post-data="{ \"apikey\": \"$username\", \"secretapikey\": \"$password\", \"name\": \"${porkbun_record}\", \"type\": \"A\", \"content\": \"$__IP\", \"ttl\": \"300\" }" \
"https://porkbun.com/api/json/v3/dns/create/$domain" -O -
write_log 7 "Created DNS record: $hostname -> $__IP"
else
# Update existing record
wget --post-data="{ \"apikey\": \"$username\", \"secretapikey\": \"$password\", \"content\": \"$__IP\", \"ttl\": \"300\" }" \
"https://porkbun.com/api/json/v3/dns/editByNameType/$domain/A/${porkbun_record}" -O -
write_log 7 "Updated DNS record: $hostname $REGISTERED_IP -> $__IP"
fi
write_log 5 'Exiting: ddns-update-porkbun.sh'
ddns.global=ddns
ddns.global.ddns_dateformat='%F %R'
ddns.global.ddns_loglines='250'
ddns.global.ddns_rundir='/var/run/ddns'
ddns.global.ddns_logdir='/var/log/ddns'
ddns.porkbun.lookup_host="$LOOKUP_HOST_FQDN"
ddns.porkbun.domain="$DOMAIN"
ddns.porkbun.username="$APIKEY"
ddns.porkbun.password="$SECRETAPIKEY"
ddns.porkbun=service
ddns.porkbun.use_ipv6='0'
ddns.porkbun.enabled='1'
ddns.porkbun.update_script='/usr/bin/ddns-update-porkbun.sh'
ddns.porkbun.ip_source='network'
ddns.porkbun.ip_network='wan'
ddns.porkbun.interface='wan'
ddns.porkbun.use_syslog='2'
ddns.porkbun.check_unit='minutes'
ddns.porkbun.force_unit='minutes'
ddns.porkbun.retry_unit='seconds'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment