Background
I have, for a time, been using CloudFlare for CDN, Optimizations and DNS Management for this and a few other domains. At the same time, I’ve been using DynDNS to provide name resolution to my home network/lab. Browsing around the CloudFlare JSON API I noticed that I can update DNS records through some fairly simple HTTP GET requests, and since the RouterOS (the operating system used by Mikrotik’s routerboards) has support for some pretty decent scripting I decided to let my router update CloudFlare for some custom and free dynamic DNS resolution.
Attribution
My current script is a modified version of a script developed by Konstantin Antselovich.
Original script: http://konstant1n.livejournal.com/9759.html
Original Author website: http://konstantin.antselovich.com/
Thanks!
How you do it
What you need
- RouterOS v6+ for HTTPS support
- Cloudflare API key, aka “Token”, found at the account page
- Cloudflare DNS Zone name
- Cloudflare Record Id (more on that later)
- Cloudflare subdomain name
- Name of the external router interface
Getting the Record Id
To get the record id of the DNS record you want to update, I used the rec_load_all function of the API. And since I’m a windows admin, I use powershell for that.
$cfDomain = "homelab" |
That should, given that you’ve replaced all the example variables produce something like this:
rec_id : 16606003 |
And with that done, you should have everything necessary for the RouterOS script.
Building the Script
I’m not going to go into the syntax of ros scripts, but I’ll try to explain the different building block to some degree.
Defining our variables
First, we’ll define out variables used for the script. The global hostname_ variable is actually redundant as I could derive the FQDN from _CFDomain and $CFZone, but it’s used in other scripts in my router so it’s included for that purpose alone.
######## Set and collect general variables ######### |
Take care to verify and compare the CFxx variables with the ones used in and received from the previous powershell script, but also the $hostname variable.
- hostname: Used in the script to lookup the currently set IP-address.
- WANInterface: The list name of the external interface, used to find the external IP-address.
- CFemail: Your CloudFlare Login email.
- CFtkn: Your API Key/Token.
- CFzone: Your DNS Zone.
- CFid: The rec_id from the powershell script.
- CFtype: A for IPv4 host records, AAAA for IPv6.
- CFttl: Record time-to-live, use “1” for automatic.
- CFservicemode: Use “0” for direct traffic or “1” to go through their CDN, Optimizations and Security features. I recommend “0” for anything but websites.
- CFDomain: Subdomain or rather hostname for your router.
- CFDebug: Use “True” to print some informational stuff to the router logs.
Resolve IP-addresses
Next, we resolve two IP-addresses. First one, externalIP_, is the routers current external address. And the second, _resolvedIP, is the address currently set for our $hostname.
######## Resolve and set IP-variables ########## |
Build CF API Request and log debug info
Now, build the URI for the API call from our variables.
######## Build CF API Url ######################### |
And if the $CFdebug variable is set to “true”, write a little info to the router log.
######## Write debug info to log ################# |
Compare and Update
Last step is to compare externalIP_ with _resolvedIP. If they don’t match, our external IP-address has been changed and we need to update the DNS record. To execute the HTTP GET request we use a RouterOS tool called fetch to which we pass the $CFurl variable. The reason this will not work before RouterOS v6+ is that before that there was no support for HTTPS, and CloudFlare does not accept unencrypted traffic for their APIs.
The script block is pretty straight-forward. Compare, update, flush local DNS-cache and write to the log.
######## Compare and update CF if necessary ##### |
Permissions/Policies
This part, I’m not entirely sure on. The script needs to do name resolution, read settings and set variables and it works for me with these policies configured:
- Read
- Write
- Test
- Sniff
- Sensitive
It might be over-doing it a bit, but I have not had occasion to fiddle with them yet.
Scheduling
I have this script scheduled to run once every 20 minutes. As it will only try to update the DNS records if there is a difference between them and the current external IP-address I feel fairly sure that I won’t be tagged as a spammer or something by CloudFlare.
The schedule is configured with:
- Start Time: startup
- Interval: 00:20:00
- On Event:
/system script run <Name of your script>
- Policy: Read,Write, Test, Sniff, Sensitive
This schedule is also a good place for a hairpin update if it’s needed.
The Copy-Paste part
Now, obviously, at least make sure you proof-read the script before putting it into production. I am not a network dude, and I rarely dabble with ROS scripts. I am certain that anyone with a bit more experience could create a smarter, prettier version of this script, so use at your own risk. Anyway, here’s the script in it’s entirety.
######## Set and collect general variables ######### |