Created
August 16, 2025 18:52
-
-
Save troubleshooter/f47e999ea469820b8d12dec0ea280c1c to your computer and use it in GitHub Desktop.
Dynamic DNS updater script for use with Linode.com's DNS API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # A shell script to update Linode's DNS with WAN IPs | |
| # | |
| # For use with version 4 of Linode's DNS API | |
| # Requires: | |
| # * dig | |
| # * nc | |
| # * notify-send | |
| # * rsyslog/syslog | |
| # * touch | |
| # * tr | |
| # * wc | |
| # * logger | |
| # * Linode (https://linode.com) API Key, domain ID and resource IDs | |
| # Will create needed files .wan_ip4.txt and .wan_ip6.txt in | |
| # home directory on first run | |
| # Sends notifications via notify-send for failed/problem | |
| # events/changes. | |
| # Run as often as you'd like via cron. Cron job must have environment variables | |
| # exported for notify-send to work properly. Example: | |
| # */15 * * * * export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus; export XDG_RUNTIME_DIR=/run/user/1000; /home/user/scripts/dns-updater-v4.sh | |
| # Logs to syslog. | |
| # The only modifications you need to make are the Linode-specific | |
| # ones directly below, the proper environment variables for your | |
| # system and script path for the cron job and the paths for the various | |
| # commands to suit your system. | |
| # Script defaults to check for both IPv4 and IPv6. Change if | |
| # necessary. See CHECK_IP4 and CHECK_IP6 below. | |
| # Fixed to work with Linode API V4 - 2023/05/08 | |
| # Updated to work with Linux Mint 22.1 Xia 2025/08/01 | |
| # Copyright (C) 2017-2025 Terry Roy | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. http://www.gnu.org/licenses/. | |
| ## LINODE-SPECIFIC SETTINGS | |
| # Linode DNS API variables | |
| # You need to add your information here. | |
| TOKEN= | |
| DOMAIN_ID= | |
| RECORD_ID4= | |
| RECORD_ID6= | |
| # If an error occurs updating the DNS records, the error(s) are returned | |
| # in an array that starts with "errors". Note this is Linode-specific. | |
| ERROR_STR="errors" | |
| # Turn checks on/off 1 = on, 0 = off | |
| # This is handy for travelling. Some WANs won't have an IPv6 | |
| # address assigned. In that case, turn off IPv6 checks here instead of | |
| # getting error notifications every time the cron job is run. | |
| CHECK_IP4=1 | |
| CHECK_IP6=1 | |
| ## LOGGING | |
| # Log error messages to syslog as "error". The actual error message is appended | |
| # to this so keep the space in at the end before the quotation mark. | |
| ERR_MSG="/usr/bin/logger -i -t DNS-Updater -p user.err " | |
| # Log other messages to syslog as "notice". The actual message is | |
| # appended to this so keep the space in at the end before the quotation mark. | |
| NOTICE_MSG="/usr/bin/logger -i -t DNS-Updater -p user.notice " | |
| # Current date and time for notification message | |
| LOGTIME=$(date "+%H:%M:%S") | |
| # Shared options for notify-send | |
| NS="/usr/bin/notify-send " | |
| NSHDR="DNS-Update $LOGTIME" | |
| # Paths to commands used | |
| TCH="/usr/bin/touch" | |
| # Use a function to generate the json for curl | |
| generate_json() | |
| { | |
| cat <<EOF | |
| { | |
| "target": "$1" | |
| } | |
| EOF | |
| } | |
| ## DATA FILES | |
| # Check for existence and if not present, create. | |
| if [ ! -f "$HOME"/.wan_ip4.txt ] && [ "$CHECK_IP4" -eq 1 ]; then | |
| $TCH "$HOME"/.wan_ip4.txt | |
| $NOTICE_MSG "No IPv4 file, created $HOME/.wan_ip4.txt" | |
| $NS "$NSHDR" "No IPv4 file found. Created $HOME/.wan_ip4.txt." | |
| fi | |
| if [ ! -f "$HOME"/.wan_ip6.txt ] && [ "$CHECK_IP6" -eq 1 ]; then | |
| $TCH "$HOME"/.wan_ip6.txt | |
| $NOTICE_MSG "No IPv6 file found, created $HOME/.wan_ip6.txt" | |
| $NS "$NSHDR" "No IPv6 file found - Created $HOME/.wan_ip6.txt" | |
| fi | |
| # Put in checks for commands | |
| #echo "Debug One" | |
| ## RETRIEVE IPS | |
| # Retrieve WAN IPv4 | |
| if [ "$CHECK_IP4" -eq 1 ] ; then | |
| # Test for connecticvity to nameserver | |
| # Returns '0' if successful and '1' if unsuccessful. | |
| /usr/bin/nc -z resolver1.opendns.com 53 >/dev/null 2>&1 | |
| ONLINE4=$? | |
| if [ "$ONLINE4" -ne 0 ] ; then | |
| $ERR_MSG "No IPv4 connection" | |
| $NS "$NSHDR" "ERROR IPv4 - No connection" | |
| else | |
| WAN_IP4=$(/usr/bin/dig +short -4 myip.opendns.com a @resolver1.opendns.com) | |
| # Quick pseudo-validation | |
| VALID4=$(echo "$WAN_IP4" | /usr/bin/tr -dc \'.\' | /usr/bin/wc -c) | |
| if [ "$VALID4" -eq 3 ]; then | |
| OLD_WAN_IP4=$(cat "$HOME"/.wan_ip4.txt) | |
| ### Comparison to previous IP | |
| if [ "$WAN_IP4" != "$OLD_WAN_IP4" ]; then | |
| UPDATE4=$(curl -s -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer $TOKEN" \ | |
| -X PUT --data \ | |
| "$(generate_json $WAN_IP4)" \ | |
| https://api.linode.com/v4/domains/$DOMAIN_ID/records/$RECORD_ID4) | |
| # Check there's no error message in the array | |
| case "$UPDATE4" in | |
| *$ERROR_STR* ) | |
| $ERR_MSG "ERROR: Problem updating IPv4: Error returned. Old IPv4: $OLD_WAN_IP4 New IPv4: $WAN_IP4 Error: $UPDATE4" | |
| $NS "$NSHDR" "ERROR - Error returned in IPv4 update Old IPv4: $OLD_WAN_IP4 New IPv4: $WAN_IP4 Error: $UPDATE4" | |
| ;; | |
| "") | |
| $ERR_MSG "ERROR: Problem updating IPv4: Empty String. Old IPv4: $OLD_WAN_IP4 New IPv4: $WAN_IP4" | |
| $NS "$NSHDR" "ERROR - Empty string returned during IPv4 update" | |
| ;; | |
| * ) | |
| echo "$WAN_IP4" > "$HOME"/.wan_ip4.txt | |
| $NOTICE_MSG "\"Updated IPv4 from $OLD_WAN_IP4 to $WAN_IP4. Result:$UPDATE4\"" | |
| $NS "$NSHDR" "IPv4 changed - Updated DNS record. Result:$UPDATE4" | |
| ;; | |
| esac | |
| else | |
| $NOTICE_MSG "No IPv4 change" | |
| $NS "$NSHDR" "No IPv4 change $WAN_IP4" | |
| fi | |
| else | |
| # Notify if problem | |
| $ERR_MSG "\"ERROR: IPv4 resolver invalid response. Response: $WAN_IP4\"" | |
| $NS "$NSHDR" "ERROR - IPv4 resolver invalid response. Response: $WAN_IP4" | |
| fi | |
| fi | |
| fi | |
| # Retrieve WAN IPv6 | |
| if [ "$CHECK_IP6" -eq 1 ] ; then | |
| # Test for connecticvity to nameserver | |
| # If successful, commands returns '0' and '1' if unsuccessful. | |
| /usr/bin/nc -z resolver1.ipv6-sandbox.opendns.com 53 >/dev/null 2>&1 | |
| ONLINE6=$? | |
| if [ "$ONLINE6" -ne 0 ] ; then | |
| $ERR_MSG "No IPv6 connection" | |
| $NS "$NSHDR" "ERROR IPv6 - No connection" | |
| else | |
| WAN_IP6=$(/usr/bin/dig +short -6 myip.opendns.com aaaa @resolver1.ipv6-sandbox.opendns.com) | |
| # Quick pseudo-validation | |
| VALID6=$(echo "$WAN_IP6" | /usr/bin/tr -dc \':\' | /usr/bin/wc -c) | |
| if [ "$VALID6" -gt 3 ]; then | |
| OLD_WAN_IP6=$(cat "$HOME"/.wan_ip6.txt) | |
| # Update DNS record | |
| if [ "$WAN_IP6" != "$OLD_WAN_IP6" ]; then | |
| UPDATE6=$(curl -s -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer $TOKEN" \ | |
| -X PUT --data \ | |
| "$(generate_json $WAN_IP6)" \ | |
| https://api.linode.com/v4/domains/$DOMAIN_ID/records/$RECORD_ID6) | |
| # Check there's no error message in array | |
| case "$UPDATE6" in | |
| *$ERROR_STR* ) | |
| $ERR_MSG "Problem updating IPv6 Old IPv6: $OLD_WAN_IP6 New IPv6: $WAN_IP6 $UPDATE6" | |
| $NS "$NSHDR" "ERROR - Error in IPv6 record update. Old IPv6: $OLD_WAN_IP6 New IPv6: $WAN_IP6 $UPDATE6" | |
| ;; | |
| "") | |
| $ERR_MSG "ERROR: Problem updating IPv6: Empty String. Old IPv6: $OLD_WAN_IP6 New IPv6: $WAN_IP6" | |
| $NS "$NSHDR" "ERROR - Empty string returned during IPv6 update" | |
| ;; | |
| * ) | |
| echo "$WAN_IP6" > "$HOME"/.wan_ip6.txt | |
| $NOTICE_MSG "Updated IPv6 from $OLD_WAN_IP6 to $WAN_IP6. Result:$UPDATE6" | |
| $NS "$NSHDR" "IPv6 changed - Updated DNS record. Result:$UPDATE6" | |
| ;; | |
| esac | |
| else | |
| $NOTICE_MSG "No IPv6 change" | |
| $NS "$NSHDR" "No IPv6 change $WAN_IP6" | |
| fi | |
| else | |
| # Notify if problem | |
| $ERR_MSG "IPv6 invalid response. Response: $WAN_IP6" | |
| $NS "$NSHDR" "ERROR - IPv6: invalid response. Response: $WAN_IP6" | |
| fi | |
| fi | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment