diff options
author | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2023-03-24 00:33:09 -0700 |
---|---|---|
committer | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2023-03-24 00:33:09 -0700 |
commit | 9ef2d8b2721cc691af1f9aef09e86ac8d40ada09 (patch) | |
tree | bd8116bc90fd2ee2e217bf632b02f1cac31360c6 /hetzner-ddns | |
parent | f2a41436c916b7763057e848b66a0158c6846ff5 (diff) | |
download | hetzner-ddns-9ef2d8b2721cc691af1f9aef09e86ac8d40ada09.tar.gz hetzner-ddns-9ef2d8b2721cc691af1f9aef09e86ac8d40ada09.tar.xz |
Diffstat (limited to 'hetzner-ddns')
-rwxr-xr-x | hetzner-ddns | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/hetzner-ddns b/hetzner-ddns new file mode 100755 index 0000000..6d70e69 --- /dev/null +++ b/hetzner-ddns @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# Copyright 2022 David Vazgenovich Shakaryan +# +# HETZNER_TOKEN=<token> hetzner-ddns <domain> +# HETZNER_TOKEN_FILE=/path/to/token hetzner-ddns <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}" |