favonia / cloudflare-ddns

🌟 A small, feature-rich, and robust Cloudflare DDNS updater
Apache License 2.0
825 stars 36 forks source link

Configurable Cloudflare IPs #478

Closed ashway83 closed 1 year ago

ashway83 commented 1 year ago

The Cloudflare IP address 1.1.1.1 is currently hardcoded and cannot be changed. However, it would be helpful to make it configurable because some internet service providers, such as Vodafone in Spain, intercept connections to 1.1.1.1, which makes it inaccessible. I know it's weird, and I don't know why they do so, but we have what we have. By making the IP address configurable, users could specify an alternative IP address such as 1.0.0.1 that could be accessed instead. Anyways, having hardcoded values is usually a bad practice.

ashway83 commented 1 year ago

My workaround was to change the destination address using DNAT:

iptables -t nat -A OUTPUT -d 1.1.1.1 -j DNAT --to-destination 1.0.0.1
favonia commented 1 year ago

Thanks for your sharing. The 1.1.1.1 was unassigned before 2010, so people thought it's a private IP address that they can use for whatever purposes. Cloudflare has been working on unblocking it in various routers and ISPs. Unfortunately, the engineers at Vodafone probably have not picked this up yet. I believe Cloudflare's official recommendation is that you should report this to our own ISP because they might listen to their customers more than Cloudflare.

Personally, I don't think hardcoding 1.1.1.1 is that bad in this case, because Cloudflare really pushes 1.1.1.1 as the name of their DNS products and encourages people to put the string "1.1.1.1" into their configuration files. If anything, I think hardcoding the expected format of the debugging page and/or the DNS query to get the IP is a greater sin. However, I understand that some routers and/or ISPs are still blocking 1.1.1.1, and making things work is more important than being principled. So, I agree something should be done here. (The principled, long-term solution is to get your ISP(s) to unblock 1.1.1.1.)

So far, the IP 1.1.1.1 is used in three ways: (1) to establish a "fake" IPv4 UDP connection to detect the local IPv4 address, (2) to detect the public IPv4 address via Cloudflare's debugging page, and (3) to detect the public IPv4 address via DoH. Only the last two really matter. While I see the value of letting users configure the IP, two major UI concerns came into my mind:

  1. Every new configuration is adding new logic that could interact with other configurations in an unexpected way, so this should be done by extending existing provider configurations. It could be cloudflare.doh:1.0.0.1. I have to think about it. The design question is, to what degree we want to make DoH configurable? There are many hardcoded parameters. The same question can be asked about the debugging page parser. I prefer not to be stuck with a format where further similar extensions are impossible.
  2. Ideally I want the configuration to indicate the intention of the user more than some technical details. Specifying the IP seems to belong the later than the former.

As an immediate hack to make things work without changing the UI, my current idea is to ping those IPs and determine whether the network is working at all. In the cases where 1.0.0.1 is reachable but 1.1.1.1 fails, 1.0.0.1 will be used; otherwise, 1.1.1.1. The same logic applies to 2606:4700:4700::1001 and 2606:4700:4700::1111. It is still hardcoding these IPs but it will automatically choose the IPs that work. Does that sound good and elegant enough? EDIT: This would not work. See below.

favonia commented 1 year ago

Actually, pinging is not good enough, because your ISP will probably still respond to 1.1.1.1 (with their own servers). Another idea is to force IPv4 or IPv6 when fetching the debugging page so that we can use www.cloudflare.com directly (not sure how to force IPv4/6 yet). The DoH can be done in the same way.

favonia commented 1 year ago

@ashway83 Updates: I'm writing some new code which uses cloudflare-dns.com with specified IP network families instead of 1.1.1.1 and 2606:4700:4700::1111.

ashway83 commented 1 year ago

@favonia Thank you for your meticulous approach. Actually, I envisioned something far simpler - keep 1.1.1.1 as a default Cloudflare's IP, but allow to "override" it via an environment variable, e.g. CLOUDFLARE_IP. If the environment variable is set, it takes precedence over the default 1.1.1.1. And to clarify, by "hardcoded" I meant explicitly specified in the source code, not configuration, meaning changing the value requires re-compilation.

ashway83 commented 1 year ago

Actually, pinging is not good enough, because your ISP will probably still respond to 1.1.1.1 (with their own servers). Another idea is to force IPv4 or IPv6 when fetching the debugging page so that we can use www.cloudflare.com directly (not sure how to force IPv4/6 yet). The DoH can be done in the same way.

I agree it doesn't address many scenarios. In particular, in my case with Vodafone ICMP packets can reach the destination, while HTTPS (TCP 1.1.1.1:443) traffic is being intercepted and redirected to the router's web interface.

favonia commented 1 year ago

Actually, I envisioned something far simpler - keep 1.1.1.1 as a default Cloudflare's IP, but allow to "override" it via an environment variable, e.g. CLOUDFLARE_IP. If the environment variable is set, it takes precedence over the default 1.1.1.1.

Yes, I understand this. :grin: My main hesitation is that the scope of it would not be super clear---is it about the Cloudflare API server, the IP address to create fake UDP connections, the DNS-over-HTTPS entry point, the debugging page, or maybe all of them?

As for my latest attempt to fix it elegantly, I am still fighting with the Go standard library to disable its IPv4/6 dual-stacking. The goal is to make it work out of box BUT without Go's ugly type switches or stupid hacks AND without re-implementing a significant part of it. My current approach is to replace its DNS resolution with a less powerful but working version---it would try only the first IP address instead of going through all the IPs as in the standard library. I will make a PR soon---currently writing unit tests for the new DNS hack.

favonia commented 1 year ago

@ashway83 The PR that implemented a more sophisticated method was merged. Could you possibly try out the edge tag and see whether it works out of box for you? That is, it should not need any manual tweaking. If that's the case, I can really close this issue. Again, thanks for the reporting! This doesn't work. Will try something new later.

favonia commented 1 year ago

@ashway83 I implemented a (different) mechanism to automatically switch to 1.0.0.1 when 1.1.1.1 cannot be used. Could you possibly try the edge tag and see whether it works without the need to use iptables? Thank you.

favonia commented 1 year ago

@ashway83 Hi, I might release a new stable version soon, which will have the new functionality to detect the IPs. I did some basic testing and believed the updater will think 1.1.1.1 works with the iptables rule. However, ideally you should be able to remove the iptables rule and let the updater switch to 1.0.0.1 by itself. I tried to be careful and conservative but please let me know if something doesn't work. Thank you!

favonia commented 1 year ago

@ashway83 I'm closing this issue because the version with automatic detection has been released. Please re-open it if you encounter any problem (with or without the iptables rule). Thank you!