blob: 5e6de747e2601408352bddcd1714dccc9aaa46b7 (
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
|
#!/usr/bin/env bash
#
# Copyright 2022 David Vazgenovich Shakaryan
#
# HETZNER_TOKEN=<token> hetzner-ddns.sh <domain>
# HETZNER_TOKEN_FILE=/path/to/token hetzner-ddns.sh <domain>
# systemctl enable --now "hetzner-ddns@$(systemd-escape <domain>).timer"
IP_RESOLVER='https://ifconfig.co'
TARGET="${1}"
shopt -s extglob
die() {
[[ -n "${@}" ]] && echo "${@}" >&2
exit 1
}
hetzcurl() {
curl -sfH "Auth-API-Token: ${HETZNER_TOKEN}" \
"https://dns.hetzner.com/api/v1/${1}" \
"${@:2}"
}
if [[ -z "${HETZNER_TOKEN}" ]] && [[ -n "${HETZNER_TOKEN_FILE}" ]]; then
[[ -f "${HETZNER_TOKEN_FILE}" ]] || die 'Specified token file' \
"(${HETZNER_TOKEN_FILE}) does not exist"
HETZNER_TOKEN="$(<"${HETZNER_TOKEN_FILE}")"
fi
[[ -n "${HETZNER_TOKEN}" ]] || die 'Missing token'
ip="$(curl -sf4 "${IP_RESOLVER}")" || die 'IP lookup failed'
zone_re="${TARGET}"
while [[ "${zone_re}" =~ ^([^\\]*)\.(.*)$ ]]; do
zone_re="(${BASH_REMATCH[1]}\\.)?${BASH_REMATCH[2]}"
done
res="$(hetzcurl "zones")" || die 'Zones lookup failed'
IFS='|' read zone_id zone_name < <(jq -er --arg re "${zone_re}" \
'[.zones[] | select(.name | test("\\A" + $re + "\\z"))] |
if . == [] then (null | halt_error) else . end |
max_by(.name | length) | [.id, .name] | join("|")' \
<<< "${res}") || die 'Zone not found'
rec_name="${TARGET%%?(.)${zone_name}}"
rec_name="${rec_name:-@}"
res="$(hetzcurl "records?zone_id=${zone_id}")" || die 'Records lookup failed'
rec_id="$(jq -er --arg name "${rec_name}" \
'first(.records[] | select(.type == "A" and .name == $name)) | .id' \
<<< "${res}")" || die 'Record not found'
res="$(hetzcurl "records/${rec_id}")" || die 'Record lookup failed'
old_ip="$(jq -r '.record.value' <<< "${res}")"
if [[ "${old_ip}" == "${ip}" ]]; then
echo "IP unchanged from ${ip}"
exit
fi
data="$(jq -c --arg ip "${ip}" \
'.record | {zone_id, type, name, value: $ip }' <<< "${res}")"
res="$(hetzcurl "records/${rec_id}" \
-H 'Content-Type: application/json' \
-X PUT -d "${data}")" || die 'Record update failed'
new_ip="$(jq -r '.record.value' <<< "${res}")"
echo "IP changed from ${old_ip} to ${new_ip}"
|