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