qdm12 / ddns-updater

Container to update DNS records periodically with WebUI for many DNS providers
https://hub.docker.com/r/qmcgaw/ddns-updater/
MIT License
1.75k stars 166 forks source link

Bug: IPv6 update for Noip #330

Closed incaseoftrouble closed 9 months ago

incaseoftrouble commented 2 years ago
  1. Is this urgent: No
  2. DNS provider(s) you use: noip
  3. Program version: Running version latest built on 2022-03-15T14:38:50Z (commit 584597d)
  4. What are you using to run the container: docker-compose
  5. Extra information

Configuration file:

{
  "settings": [
    {
      "provider": "noip",
      "domain": "...",
      "host": "...",
      "username": "...",
      "password": "...",
      "ip_version": "ipv6",
      "provider_ip": false
    }
  ]
}

I have several issues which might be connected: 1) If I specify IPV6_PREFIX=/64, the address actually is updated to literally <prefix>:: instead of <prefix>:<old suffix> (the former is also written in the updates.json). This may be an issue on the side of noip but I'd expect this to actually do (i) query current ipv6 address (ii) query old ipv6 address from ddns provider (iii) update with <current ipv6 prefix>:<suffix from ddns provider>. 2) In any case, the ipv4 address is always updated even though ip_version: "ipv6" is specified. I use a reverse proxy for ipv4 connections (since I don't have a public ipv4 - DS-lite). This should leave ipv4 alone (although this might also be an issue on the side of noip?) 3) When I set provider_ip: true, I get ERROR no IP address in response (but the ipv4 address still is updated!) 4) ddns-updater picks up the wrong ipv6 address: INFO IPv6 address of <...> is fdbd:... (the ULA of the host) and your IPv6 address is 2a02:... (the correct global IP), triggering an update every time even though the ddns provider has the correct ip set. (this also adds an entry in the updates.json every time)

incaseoftrouble commented 2 years ago

Update: 4 is wrong, this is due to my local dns resolver returning the ULA first, adding dns: 9.9.9.9 as option fixes this. The other issues remain though.

qdm12 commented 1 year ago
  1. Forgive my IPv6 illiteracy, but this looks interesting, so there would be no way to know from other sources the IPv6 address you want to update your record to? What happens if this is the first you set the IPv6 address for a record? πŸ€” Asking since this problem might be relevant for other DNS providers.
  2. That seems odd, try using the latest image to see if it fixes it? The code for noip is at: https://github.com/qdm12/ddns-updater/blob/936bf4386e26f9c22105e62f5d357bdaf66af5c3/internal/settings/providers/noip/provider.go#L113-L119 and the myip field is definitely not set when ipv6 is selected. Maybe a problem on noip's side? πŸ€”
  3. I wasn't aware noip would not return your ip when using their systems to find your ip (they do return one, when you send your IP... weird). https://github.com/qdm12/ddns-updater/blob/936bf4386e26f9c22105e62f5d357bdaf66af5c3/internal/settings/providers/noip/provider.go#L172-L174 I changed that if condition from len(ips) == 0 to !p.useProviderIP && len(ips) == 0 which should solve it.
  4. Note you can now use RESOLVER_ADDRESS=9.9.9.9 if you prefer.
incaseoftrouble commented 1 year ago
  1. That's a good point. If the record doesn't exist, one probably has to throw an error I guess?
  2. I implemented my own hack for this and it works, so it is not (only) on noip's side. I vaguely recall that I had some idea of why this might happen but forgot it. I will (hopefully) try with the new version soon-ish (but no promises, I am a bit swamped currently)

3+4: Great!

qdm12 commented 1 year ago

Might be relevant: ipv6 4 last bytes were often removed by a bad regex in the code, this is now fixed in 954dffd3a78751061c01e0f9135086a306f57dc2 for the latest image and future release v2.6.0.

incaseoftrouble commented 1 year ago

Just as a small addition, forgot to write that: Why is this a "relevant" thing? (just in case this isn't already clear)

My use case (and I believe this may be not so uncommon with IPv6 getting more prominent) is that I have several devices I want to reach (e.g. my router and my NAS). With IPv4 this is "easy" in the sense that the only relevant address is the router + NAT tables in the router. With IPv6 each device needs its own address (since there is supposed to be no NAT). So I have one no-ip entry for my NAS and one for my router. With prefix delegation set up, the addresses of both devices have the shape <prefix given by ISP><eui-64 suffix derived from MAC>. Now, I want a single process to update both the NAS and router address (e.g. running on the NAS since I cannot / do not want to run a ddns service on the router). Then, it needs to get the public IPv6 address of the NAS, extract the prefix given by the ISP, and update the no-ip entry pointing to the router with <new prefix><old suffix>.

So, in this scenario, having a static file with suffixes would also be sufficient (and would solve the "what if the entry doesn't exist" case). E.g. something like

my-ddns-address.ddns.net,::aaaa:bbbb:cccc:dddd/64
other-address.ddns.net,::1/64

I'll hopefully get to try the latest docker image this weekend

EDIT: What would be even more interesting would be to update the address with public prefix + something derived from a local resolver: With ipv6 privacy extensions, the suffix is not uniquely derived from the MAC but (effectively) randomly generated and changed every few hours / days / whatever. So it would be interesting to define ways how to get the global and local part of the address. But this might be tricky / require a well configured local DNS server, and is definitely a more niche application.

qdm12 commented 9 months ago

Sorry to get back to this so late, this issue is still an urgent issue I would like to solve. Let's say the AAAA record is either:

Would it fit the use case to have an extra field "ipv6_suffix" for each update setting, and then update the AAAA record to masked(public_ip):ipv6_suffix? So that you could update not only the machine it's running on but also other machines. I would also ideally like to avoid finding the existing IPv6 suffix of the record, which could be unreliable in the following 3 cases above.

So it would be interesting to define ways how to get the global and local part of the address

Any clue how to do this? That would be a cool second part to this, to automate the detection of the ipv6_suffix for the current machine? It's certainly possible with mac address derived local addresses, but not sure with the privacy ones you mention. Thanks!

incaseoftrouble commented 9 months ago

Sorry to get back to this so late

me too! I wanted to try this for a long time but alas other things always popped up ...

Would it fit the use case to have an extra field "ipv6_suffix" for each update setting, and then update the AAAA record to masked(public_ip):ipv6_suffix? So that you could update not only the machine it's running on but also other machines.

Yes absolutely. Basically being able to say "update this ipv6 record with mask "::1234:1234/16" for example. Meaning that one would specify a suffix mask <address>/<length> and in an update one would get the appropriate ipv6, replace the last 128-<length> part with <address> and update the record to that. (It would also be fine to just allow for a /64 suffix I guess.)

Thinking about it, finding the existing ipv6 would not be needed I guess. The whole point of a ddns updater would be to update the first 64 (or so) bits and leave the last intact. But this last bit shouldn't change anyway so it is ok for a user to manually set it once (by providing ipv6 suffix). It might be a nice optional diagnosis tool though -- if the record exists and has a value, complain / log when the suffix has a different value than the provided suffix (then almost surely something isn't right)

Deriving ipv6

Thinking about it, this is probably impossible to get get working reliably from the "outside" :( I think that what could be possible would be to define a local dns + dns name to resolve, which would, if configured properly, give you the "static" and privacy one, then one could check which of the provided names is (i) globally routed and (ii) not matching the MAC address of that device. But even then it could be just a statically configured entry or so ...

I think that the best way to do this would be to push some logic onto the concrete devices. In the sense that you would have a small script which notifies the ddns-updater of the new address or so. But then you could honestly just run a second ddns updater instance on that machine. A compromise solution would be instead of just writing a static ipv6_suffix to allow for a command to be called which provides the ipv6 suffix via stdout. Then, tech-savvy users could write their own logic to fetch the current suffix. But this arguably is very very niche.

Overall, I think that this is not something to worry about too much. Privacy extensions are more relevant / intended for client devices (I think my NAS doesn't even choose one). If someone would be worried about, e.g., their server-device's identity "leaking" (i.e. having the MAC address in the public ipv6) one could just assign a static suffix I guess.

qdm12 commented 9 months ago

Awesome! I opened PR #611 which adds an ipv6_suffix json parameter for each update configuration. Can you try it using image qmcgaw/ddns-updater:pr-611? Also ideally try as well with an empty string "ipv6_suffix": "" to check it doesn't fail (and in this case, it would get ignored simply). Thanks!! πŸ‘

incaseoftrouble commented 9 months ago

Tried it, seems to work :+1: thanks for this!

On a small note: I think the README should say that provider_ip and ipv6_suffix are incompatible (so I think at least)

Sort of unrelated: With RESOLVER_ADDRESS=9.9.9.9 I get ERROR settings validation: resolver settings: splitting host and port from address: address 9.9.9.9: missing port in address. Adding :53 solves it but might be convenient to make this the default :)

qdm12 commented 9 months ago

Awesome πŸ’―

611 merged πŸ‘ Closing this πŸ˜‰ Feel free to comment if anything is wrong.

incaseoftrouble commented 9 months ago

Incompatible

Just to add: Yes, agree, I meant noting that they are logically incompatible, i.e. if you provide ipv6_suffix then setting provider_ip is unneeded / does not make sense. And, agree, provider_ip could well be removed I think.

Thanks for adding this feature!