summaryrefslogtreecommitdiffstats
path: root/net/ddns-scripts/files/usr/lib/ddns/update_hetzner_cloud.sh
blob: 07b0384acedcb1aa4a1bca2716c66be3fb0fd84c (plain)
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
#!/bin/sh
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Script to update Hetzner DNS Records using the Cloud API (api.hetzner.cloud)
#
# 2026 Christopher Obbard <obbardc@gmail.com>
#
# Options passed from /etc/config/ddns:
# Domain   - the zone name in Hetzner Console (e.g. `example.com`)
# Username - the RRset name within the zone (e.g. `www`)
# Password - Hetzner Console API token (Bearer token)
#
# Reference: https://docs.hetzner.cloud/reference/cloud#tag/zone-rrset-actions/set_zone_rrset_records

# Hetzner API base URL
__API="https://api.hetzner.cloud/v1"

# Hetzner API requires a comment. An empty string is fine.
comment=""

. /usr/share/libubox/jshn.sh

# Check CURL exists
[ -z "$CURL" ] || [ -z "$CURL_SSL" ] && {
	write_log 14 "Hetzner Cloud DDNS script requires cURL with SSL support"
	return 1
}

# Check options
[ -z "$username" ] && write_log 14 "Hetzner Cloud DDNS: 'username' (rrset name) not set" && return 1
[ -z "$password" ] && write_log 14 "Hetzner Cloud DDNS: 'password' (API Token) not set" && return 1
[ -z "$domain" ] && write_log 14 "Hetzner Cloud DDNS: 'domain' not set (Zone name)" && return 1
[ "$use_ipv6" -eq 1 ] && type="AAAA" || type="A"

__TYPE="A"
[ "$use_ipv6" -ne 0 ] && __TYPE="AAAA"

# Create JSON payload for set_records API call
# Payload:
# { "records": [ { "value": "<ip>", "comment": "" } ] }
json_init
json_add_array "records"
	json_add_object
		json_add_string "value" "$__IP"
		json_add_string "comment" "$comment"
	json_close_object
json_close_array

__URL="${__API}/zones/${domain}/rrsets/${username}/${__TYPE}/actions/set_records"

__STATUS=$(curl -Ss -X POST "$__URL" \
	-H "Authorization: Bearer ${password}" \
	-H "Content-Type: application/json" \
	-d "$(json_dump)" \
	-w "%{http_code}\n" -o "$DATFILE" 2>"$ERRFILE")

if [ $? -ne 0 ]; then
	write_log 14 "Curl failed (set_records): $(cat "$ERRFILE")"
	return 1
fi

# Treat any 2xx as success; otherwise error.
case "$__STATUS" in
	200|201|202) return 0 ;;
	*)
		write_log 14 "Curl failed (set_records): $__STATUS\nresponse body: $(cat "$DATFILE")"
		return 1
		;;
esac