-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remember current public IP and hit Hetzner API endpoint only if it changes #37
Comments
I think it makes more sense, if the script would resolve the DynDNS record on it's own and compare it with the current public IP address, which the script gets from ip.hetzner.com. If the results mismatch, an update is needed. This way, the Hetzner API would only be used when an DNS record update is necessary and informations are not stored in some file. |
@Giga-Pudding I feel that caching could make that difficult. The API, on the other hand, will always give the most up-to-date results. Someone's service being down for up to the length of their TTL every so often would be a horrible problem to try and diagnose. I think anything like that is worth avoiding. |
hmm, I don't see how caching could impact the update procedure itself, but i agree that caching could be an issue after the record has been successfully updated. When the script is executed the next time, it would maybe update the record again, because the local dns resolver still replies with an old cached record. This would falsely cause the script to think, an update is necessary again. It wouldn't be a problem (the DynDNS record would always be correct), but it would lead to unnecessary updates, as long as the TTL of the old record is not expired. However, this could easily be avoided by querying Hetzners authoritative Nameservers directly. In that case, there's no caching involved. We can use dig to find them: all authoritative nameservers: only the primary authoritative nameserver (probably better): Advantage:
Disadvantage:
Also: |
@Giga-Pudding You're right, my comment on caching wasn't fully thought-out. Forgive me, I wrote it at midnight ;) I agree with your proposed approach on a theoretical level, however, I strongly feel that a dependency on the entire BIND suite for You also raise a good point that direct contact to Hetzner nameservers through the DNS protocol may not always be possible. In fact, I'd argue that on many networks, DNS should be firewalled and only available through a local server that caches and queries a known-good resolver, and that this is best practice. I agree with @shuuryou that hitting the API multiple times per call, even in cases where an update isn't needed, is unnecessary and not ideal. The best approach, in my view, is indeed to store the latest state of the record and only fetch the client's external IP address until it's clear that an update is needed. Something in I'm considering rewriting this script in a compiled language, likely either C or Rust (both come with their pain points). Should I actually commit to that, I may end up making it a daemon intended to be launched by an init system and run in the background. In that case, the last-set IP address could be stored in memory only. Either way, the cached IP address should be stored along with the time it was last checked, to account for the possibility that it's changed by something other than the script. It could then be re-checked at a given interval, maybe once every 6 hours or so. |
For anyone stopping by here who needs a solution today, consider this patch. Barring any bugs (I did this in a few minutes, please excuse any mistakes -- it works for me), it should resolve the issue for most people who care about it without introducing additional dependencies on tools or external REST APIs. It's working only with what's already there. --- theirs 2023-12-11 16:56:51.219668495 +0100
+++ mine 2023-12-11 17:09:19.196359942 +0100
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
# DynDNS Script for Hetzner DNS API by FarrowStrange
# v1.3
@@ -117,6 +117,8 @@
else
logger Info "Current public IP address: ${cur_pub_addr}"
fi
+
+ last_ip_file="/tmp/ddns_lastip_${zone_id}_aaaa"
elif [[ "${record_type}" = "A" ]]; then
logger Info "Using IPv4, because A was set as record type."
cur_pub_addr=$(curl -s4 https://ip.hetzner.com | grep -E '^([0-9]+(\.|$)){4}')
@@ -126,11 +128,27 @@
else
logger Info "Current public IP address: ${cur_pub_addr}"
fi
+
+ last_ip_file="/tmp/ddns_lastip_${zone_id}_a"
else
logger Error "Only record type \"A\" or \"AAAA\" are support for DynDNS."
exit 1
fi
+if [ ! -f "$last_ip_file" ]
+then
+ old_pub_addr="unknown"
+else
+ old_pub_addr=$(<"$last_ip_file")
+fi
+
+if [ "$old_pub_addr" = "$cur_pub_addr" ]; then
+ logger Info "Public ip address has not changed. Nothing to do."
+ exit 0
+fi
+
+echo $cur_pub_addr > $last_ip_file
+
# get record id if not given as parameter
if [[ "${record_id}" = "" ]]; then
record_zone=$(curl -s -w "\n%{http_code}" --location \ |
@shuuryou Note that this patch won't work on pure or nearly-pure POSIX shells like None of this is criticism, by the way! I really appreciate you putting this up here. :) |
It might be a good idea not to hit the Hetzner DNS API every five or so minutes when your script is used as part of a cron job. Since you're getting the IP addresses anyway, maybe consider refactoring the script a little bit so it remembers the last public IP address, compares it with the current one, and only invokes any part of Hetzner's DNS API if there is a change.
I had to roll my own DynDNS updater for Namecheap years ago and here's how I achieved the above:
This needs to be updated slightly for IPv6 but I'm sure you get the general idea. Also "api.ipify.org" doesn't do any user agent sniffing (Hetzner's IP address service does to decide whether to return a website or just the IP as plain text). If you switch to it, you can get rid of the pipe to grep.
The text was updated successfully, but these errors were encountered: