Última atividade 2 days ago

Lookup current IPv4 and IPv6 addresses and updates them on Hetzner DNS if they changed. Saves current IP addresses in `$HOME/.my-ipv4` and `$HOME/.my-ipv6`

Revisão e1677d0b7a3b6d0707ccfe3c3a71ba7153e52f3d

update-ip.sh Bruto
1#!/bin/sh
2# Created on 2025-12-07 12:22 UTC
3# Author: Dominic Reich (OE7DRT) <quick.hat4396@qtztsjosmprqmgtunjyf.com>
4
5# Get my outgoing ipv4 address via ifconfig.me and compare it with the recent ip saved in ~/.my-ipv4
6# Update dns with the actual ipv4 if it changed
7
8# set default value for CHECK
9# override this when starting the script like `CHECK=true update-ip.sh`
10: "${CHECK:=false}"
11
12if [ "${CHECK}x" = "truex" ]
13then
14 # currently abort if jq is not installed
15 # might enter another routine to still display the record at the end
16 command -v jq > /dev/null 2>&1 || { echo >&2 "jq not found"; exit 1; }
17
18 # jq may be placed somewhere in /usr/local so first get the real path
19 JQ=$(command -v jq)
20fi
21
22# specify file names
23IPFILE4="${HOME}/.my-ipv4"
24IPFILE6="${HOME}/.my-ipv6"
25
26# Auth token (Hetzner new dns console as of Dec 2025)
27API_URL="https://api.hetzner.cloud/v1/zones/"
28AUTH_TOKEN=""
29
30# Domain settings (zone id, rrset name, rrset type)
31ZONE_ID="domain.local"
32RR_NAME="@"
33RR_TYPE4="A"
34RR_TYPE6="AAAA"
35RR_COMMENT4=""
36RR_COMMENT6=""
37
38# remote ipv4
39IPREMOTE4=$(curl -4 -sgkL https://ifconfig.me/ip 2>/dev/null)
40IPREMOTE6=$(curl -6 -sgkL https://ifconfig.me/ip 2>/dev/null)
41# echo "DBG: got ip ${IPREMOTE4}"
42
43# check if remote ips contain valid data
44# TODO:
45# get info about useful regex on ipv4 and ipv6 addresses
46
47# check if old IP4 is saved in file
48if [ -w "${IPFILE4}" -a -f "${IPFILE4}" ]
49then
50 SAVEDIP4=$(cat ${IPFILE4})
51 # echo "DBG: saved ip is ${SAVEDIP4}"
52else
53 # no existing ipv4 file, create one and add new ipv4 into it
54 /bin/echo -n "${IPREMOTE4}" > ${IPFILE4}
55 # echo "DBG: saved ip ${IPREMOTE4} to file ${IPFILE4}"
56fi
57
58#check for IP6
59if [ -w "${IPFILE6}" -a -f "${IPFILE6}" ]
60then
61 SAVEDIP6=$(cat ${IPFILE6})
62 # echo "DBG: saved ip is ${SAVEDIP4}"
63else
64 # no existing ipv4 file, create one and add new ipv4 into it
65 /bin/echo -n "${IPREMOTE6}" > ${IPFILE6}
66 # echo "DBG: saved ip ${IPREMOTE4} to file ${IPFILE4}"
67fi
68
69# check if remote ip is identical as locally saved ip
70if [ "${SAVEDIP4}x" = "${IPREMOTE4}x" ]
71then
72 # if remote ip is equal as locally saved (old) ip
73 # echo "DBG: do nothing, ips are equal"
74 echo >&2 "IPv4 identical. Nothing to do"
75else
76 # ips differ, update dns ip with remote ip and save into local ip file
77 curl -s -g -X POST -H "Authorization: Bearer ${AUTH_TOKEN}" \
78 -H "Content-Type: application/json" \
79 -d '{"records":[{"value":"'${IPREMOTE4}'","comment":"'${RR_COMMENT4}'"}]}' \
80 "${API_URL}${ZONE_ID}/rrsets/${RR_NAME}/${RR_TYPE4}/actions/set_records" > /dev/null
81 /bin/echo -n "${IPREMOTE4}" > ${IPFILE4}
82fi
83
84if [ "${CHECK}x" = "truex" ]
85then
86 # finally check (output actual json record saved on dns server) remote ip
87 curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
88 "${API_URL}${ZONE_ID}/rrsets/${RR_NAME}/${RR_TYPE4}" | ${JQ} "[.[] | .records]"
89fi
90
91# and repeat for ipv6
92if [ "${SAVEDIP6}x" = "${IPREMOTE6}x" ]
93then
94 # if remote ip is equal as locally saved (old) ip
95 # echo "DBG: do nothing, ips are equal"
96 echo >&2 "IPv6 identical. Nothing to do"
97else
98 # ips differ, update dns ip with remote ip and save into local ip file
99 curl -s -g -X POST -H "Authorization: Bearer ${AUTH_TOKEN}" \
100 -H "Content-Type: application/json" \
101 -d '{"records":[{"value":"'${IPREMOTE6}'","comment":"'${RR_COMMENT6}'"}]}' \
102 "${API_URL}${ZONE_ID}/rrsets/${RR_NAME}/${RR_TYPE6}/actions/set_records" > /dev/null
103 /bin/echo -n "${IPREMOTE6}" > ${IPFILE6}
104fi
105
106if [ "${CHECK}x" = "truex" ]
107then
108 # also check for ipv6
109 curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
110 "${API_URL}${ZONE_ID}/rrsets/${RR_NAME}/${RR_TYPE6}" | ${JQ} "[.[] | .records]"
111fi
112