Categories
Linux

Dynamic DNS(DDNS) with OpenWRT and Cloudflare

Do you want to update your DNS when your IP changes? Are you using a router running OpenWRT and Cloudflare? Then this short guide is perfect for you!

Let’s dive right into it. The OpenWRT router needs to modify your DNS settings on Cloudflare, so we need to create an API token. We could use the global API token, but we’ll use an API token with more limited access instead.

Creating a token on Cloudflare

Head to Cloudflare and go to My profile -> API Tokens -> Create Token -> Create custom token.

Give your token a name, e.g., OpenWRT DDNS and add the following permissions:

  • Zone, Zone: Read
  • Zone, DNS: Edit

Under Zone Resources, select Include, then Specific Zone and choose your domain, e.g., example.com. Hit Continue to summary and then Create Token.

Remember to take note of this token as you’ll need it later.

Configuring OpenWRT

Next up is configuring OpenWRT to work with Cloudflare. We’ll mostly use the Web GUI, but we’ll also SSH into the router to make a small change to how authentication is done with Cloudflare.

In the Web GUI, do the following:

  1. Go to System -> Software and press Update lists.
  2. Enter “ddns” into the filter field, and press Install on the ddns-scripts-cloudflare and the luci-app-ddns packages.
  3. Log out of the GUI and back in. You should now have a Services -> Dynamic DNS option. Go to it.
  4. Press Edit on myddns_ipv4.
  5. Set DDNS Service provider to cloudflare.com-v4 and click Switch service.
  6. Then set Lookup Hostname and Domain to the domain you want to update, e.g., example.com. Check the Enable and the Use HTTP Secure checkbox, set Path to CA-Certificate to /etc/ssl/certs.
  7. Finally, set the Username to Bearer and paste the Cloudflare token you created into the Password field and hit Save and then Save & Apply.

If you want to update a subdomain instead, use the following format in the Domain field: [email protected] and sub.example.com in the Lookup field.

The settings should now look something like this:

Example of how the page might look when you have entered your settings.
Example of how the page might look when you have entered your settings.

Great work. We’re almost done.

Change how your current IP is determined

If you head to Network -> Interfaces and you can see an IPV4-address under WAN, then OpenWRT knows what your current IP is, and you can skip this step.

However, if you don’t see an IP address there, you’ll have to go to Services -> Dynamic DNS -> press Edit on myddns_ipv4 -> Go to Advanced Settings tab and change IP address source to something else.

Start using it

Now, head to Services -> Dynamic DNS and press Restart DDns.

Congratulations! 🎉 It should now be up and running. You can press Edit -> Log file viewer to view the log of what it’s doing.

If you have any questions, feel free to ask! 🙂

Questions and answers

Not all situations are the same, so here are some hopefully helpful answers where the problem is slightly different.

What if I’m using Cloudflare to proxy traffic for the domain I’m using with DDNS?

The setup above works by using DNS to check what IP the domain example.com points to. If the domain doesn’t point to your current IP, then it’s updated.

But what if you’re also using Cloudflare to proxy requests? When Cloudflare is proxying the requests, example.com no longer points at your IP address directly. Instead, example.com points to Cloudflare, and Cloudflare becomes a middleman between your IP and the user.

Proxying can be helpful for multiple things: It hides your IP address from the user, and Cloudflare can cache things like images for you.

The issue arises when you now compare the IP of example.com with your IP. Since you’ll no longer get your own IP when checking the DNS record of example.com, OpenWRT will think that the IP for the domain is wrong and start interacting with Cloudflare to update it.

The thing is that the IP is most likely correct, but it can’t know for sure without using Cloudflare’s API to check.

It goes something like this:

  1. OpenWRT gets the IP of example.com, and Cloudflare returns its own IP’s as the real IP is hidden behind Cloudflare.
  2. OpenWRT thinks that DNS needs to be updated as you got Cloudflare’s IP and not your own.
  3. It connects to Cloudflare using the API and gains access to see the real IP the domain points to through Cloudflare.
  4. If the real IP is wrong, it’s updated to your current IP.

So, as you can see, it will still work, but you’ll have unnecessary interactions with the Cloudflare API.

So you might wonder: How do we avoid unnecessary interactions with Cloudflare?

There is a clever solution to this problem that is possible thanks to Cloudflare supporting CNAME flattening. Let me show you how.

First, create a new subdomain that is mostly random by going to DNS settings for your domain on Cloudflare. If you want to hide your IP, the domain should not be guessable. I’m going to use the subdomain top-secret.example.com in this example. Replace it with your own.

Press the Add record -> choose Type A -> enter top-secret.example.com in the Name field -> enter a incorrect IP like 123.123.123.123 in the IPv4 field. Make sure it’s not proxied and press Save.

How to add the subdomain on Cloudflare.

Next, remove the record for the proxied domain. In my case, that’s example.com. Now, create a new DNS record, for example.com, but this time with the Type CNAME. Point it to top-secret.example.com and enable proxying, then Save.

How to add the root domain back to Cloudflare.

Adding this CNAME will make Cloudflare redirect example.com to top-secret.example.com internally, without exposing the actual IP of your server.

You’re now done with the Cloudflare part. The only thing you need to do now is to make two small changes to your DDNS settings in OpenWRT.

Go to the Basic settings for DDNS, change the Lookup Hostname to top-secret.example.com, and set the Domain to [email protected] (note the @). Click Save and Apply.

Changing the DDNS lookup hostname and Domain in OpenWRT.

Now, head to Services -> Dynamic DNS and press the Start/Stop button twice to restart the service.

This way, DDNS will interact with the secret subdomain(top-secret.example.com) to keep your DNS updated, while the actual domain(example.com) points to the subdomain without exposing the domain or real IP.

You should now have a setup that only talks to Cloudflare when the IP has changed, even if you’re using proxying. Congratulations!⭐

Note that it, by default, might take up to 10 minutes to update the IP. You can view the logs to see what’s going on. (Dynamic DNS -> Log File Viewer tab)😊

16 replies on “Dynamic DNS(DDNS) with OpenWRT and Cloudflare”

In the new RC (21.02) of OpenWRT, the script has been updated. Now, if you set the username field to “Bearer” (captial B) it will automatically authenticate using the token — no script mods needed!

Really? That’s great! I’ll update the post when 21.02 is released.

Thanks for the heads up 👍

UPDATE: The post is now updated to work with 21.02. 😊

I mean, without this blgo post, I wouldnt have got it right. so can you explain how you determined this way? There is no documentation about it in openwrt?

You can check what the Cloudflare provider script does to figure out how the entered values are used.

If the webpage I want to serve is proxied by cloudflare, then it somehow doesnt work correctly, since nslookup will report the cloudflare proxy servers IP. Is there a way to make it work so that it updates the cloudflare internal DNS to my host instead of checking the nslookup of my webpage?

192732 : ************ ************** ************** **************
192732 note : PID ‘5138’ started at 2021-07-16 19:27
192732 : ddns version : 2.7.8-13
192732 : uci configuration:
ddns.cloudflare_ipv4_mydomain.domain=’mydomain.com’
ddns.cloudflare_ipv4_mydomain.enabled=’1′
ddns.cloudflare_ipv4_mydomain.ip_source=’web’
ddns.cloudflare_ipv4_mydomain.lookup_host=’mydomain.com’
ddns.cloudflare_ipv4_mydomain.password=’***PW***’
ddns.cloudflare_ipv4_mydomain.service_name=’cloudflare.com-v4′
ddns.cloudflare_ipv4_mydomain.username=’placeholderNotused’
ddns.cloudflare_ipv4_mydomain=service
192733 : verbose mode : 0 – run normal, NO console output
192733 : check interval: 600 seconds
192733 : force interval: 259200 seconds
192733 : retry interval: 60 seconds
192733 : retry counter : 0 times
192733 : No old process
192733 : last update: never
192733 : Detect registered/public IP
192733 : #> /usr/bin/nslookup mydomain.com >/var/run/ddns/cloudflare_ipv4_mydomain.dat 2>/var/run/ddns/cloudflare_ipv4_mydomain.err
192733 : Registered IP ‘104.21.38.92
172.67.221.122’ detected
192733 info : Starting main loop at 2021-07-16 19:27
192733 : Detect local IP on ‘web’
192733 : #> /usr/bin/curl -RsS -o /var/run/ddns/cloudflare_ipv4_mydomain.dat –stderr /var/run/ddns/cloudflare_ipv4_mydomain.err –noproxy ‘*’ ‘http://checkip.dyndns.com’
192733 : Local IP ‘X:X:X:X (MyHomeIP)’ detected on web at ‘http://checkip.dyndns.com’
192734 : Update needed – L: ‘X:X:X:X (MyHomeIP)’ R: ‘104.21.38.92 (CLOUDFLARES IP)
172.67.221.122’
192734 : parsing script ‘/usr/lib/ddns/update_cloudflare_com_v4.sh’
192734 : #> /usr/bin/curl -RsS -o /var/run/ddns/cloudflare_ipv4_mydomain.dat –stderr /var/run/ddns/cloudflare_ipv4_mydomain.err –noproxy ‘*’ –header ‘Authorization:Bearer ***PW***’ –header ‘Content-Type: application/json’ –request GET ‘https://api.cloudflare.com/client/v4/zones?name=mydomain.com’
192735 : #> /usr/bin/curl -RsS -o /var/run/ddns/cloudflare_ipv4_mydomain.dat –stderr /var/run/ddns/cloudflare_ipv4_mydomain.err –noproxy ‘*’ –header ‘Authorization:Bearer ***PW***’ –header ‘Content-Type: application/json’ –request GET ‘https://api.cloudflare.com/client/v4/zones/37a6f1a0762fec47713a9c55e49abebf/dns_records?name=mydomain.com&type=A’
192737 : IPv4 at CloudFlare.com already up to date
192737 info : Update successful – IP ‘X:X:X:X (MyHomeIP)’ send
192737 info : Forced update successful – IP: ‘X:X:X:X (MyHomeIP)’ send
192737 : Waiting 600 seconds (Check Interval)

It should also work when proxying the domain(as seen by “Update successful” in your logs).

However, OpenWRT will have to check with Cloudflare by interacting with their API every single time to check what IP Cloudflare is proxying it to.

I’ve added a workaround(post updated) that eliminates these unnecessary requests if you don’t want them while still hiding your IP. I hope it helps. 😉

at least with the package “ddns-scripts_cloudflare.com-v4″ version 2.7.8-14, the authentication header sent to cloudflare does not conform to the expected header lines.

The script in /usr/bin/ddns/update_cloudflare_com_v4.sh is installed through that package, and forms it’s header lines as:

__PRGBASE=”$__PRGBASE –header ‘X-Auth-Email: $username’ ”
__PRGBASE=”$__PRGBASE –header ‘X-Auth-Key: $password’ ”
__PRGBASE=”$__PRGBASE –header ‘Content-Type: application/json’ ”

The cloudflare api does not want X-Auth-Email and X-Auth-Key as separate lines, and should not have a space character between “Content-Type:” and “application/json”. The proper API form of the header is:

__PRGBASE=”$__PRGBASE –header ‘Content-Type: application/json’ ”
__PRGBASE=”$__PRGBASE –header ‘Authorization: $username $password’ ”

where $username is expecting to be “Bearer” and $password the [api token] string, both set in the ddns configuration.

This is rewritten with OpenWrt 21.02 in mind. Editing the Cloudflare script is no longer needed. 🙂

Thanks ! You helped me setup Cloudflare Dynamic DNS on my OpenWRT router without a glitch. Thanks for the step by step guide.

I never even knew Cloudflare could do dynamic DNS and I have been a long time user of Cloudflare. Having been relying on the Synology DDNS for years until recently the service started breaking down. I actually asked ChatGPT what other options I have and it pointed out Cloudflare being one of the options. Actually didn’t trust that answer at first as I recall searching numerous times on Cloudflare website itself and it had (and still has) no mentioning of any DDNS service. Closest thing they put up on their site is still this https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/ which actually led me to think they don’t offer this service themselves.

With this setup, there is no longer a need for CNAME redirections to third-party URL. Thanks again for the guide.

Really appreciate tip with the “fake” subdomain with CNAME record to the root domain to avoid exposing the public IP! Well done!

Thank you for this. One issue I’m seeing is that if I do not specify a subdomain, I cannot get updates to work. The log shows:
222156 WARN : Could not detect ‘record id’ for host.domain.tld: ‘mydomain.com’

How did you get yours to work with your root domain and not a subdomain like ‘www.mydomain.com’, please?

Hi TraceKat! By following the guide it should work as is. I just checked it for you with OpenWRT 23.05 too, just to be sure. Check that you have entered the values correctly and that you have done the same for the API token on cloudflare. 🙂

This was a life saver after Google Domains sold out to SquareSpace and I lost my current DDNS service proviuder. Thank you!

Something to note if your A record in Cloudflare is not your base domain name, like my setup:

My domain is (for example) abc.com
And my A record is actually at def.abc.com
abc.com is a CNAME record pointing to def.abc.com

So, for anyone else what doesn’t have a A record at the base domain, you will need to set in the Domain field of your DDNS configuration settings (using my example above): [email protected]

This discovery happened only after a long painful troubleshooting process of why my DDNS setup wasn’t working, and examining the source code at the end before I finally cracked it. (Still confused though as to why it was programmed this way…. could have used the “lookup host” in addition to the “domain” field…)

Leave a Reply

Your email address will not be published. Required fields are marked *