Yannis blog

Setup Cloudflare dynamic DNS in USG

16 Nov 2022

Recently I got a USG-3P from Ubiquiti and setup a network on that. I hosted the Unifi Controller in a server using docker compose. Unfortunately although the USG works very well with the controller, I was surpised to find out that Cloudflare DDNS does not work. I finally managed to find a hack to make it work, but it is far from ideal.

Note: EdgeRouter Lite 3 (ERL3) worked with the same configuration

Cloudflare

First you need to create the A Record for the hostname you wish to use for the USG. Make sure to have a low TTL value for the entry (I suggest 1 minute). Once the record is created, API calls can successfully modify the record. For example we create under Zone example.com an A record for hostname myserver.

Update USG ddclient binary

Unfortunately the USG has a really old version of ddclient that uses Cloudflare API v1, which is deprecated, unsupported and no longer working

Become root

sudo su -

then run

[[ -f /usr/sbin/ddclient-original ]] || cp /usr/sbin/ddclient /usr/sbin/ddclient-original
curl -L -o /tmp/ddclient https://gitlab.com/-/snippets/2458808/raw/main/ddclient
cp /tmp/ddclient /usr/sbin/ddclient
chmod +x /usr/sbin/ddclient

Now you should have a ddclient that supports Cloudflare API v4.

USG Advanced Configuration

Configuring Cloudflare Dynamic DNS is not possible through the Unifi controller, but only through UniFi - USG Advanced Configuration using config.gateway.json. This means that we have to create a file named config.gateway.json under .

Where is

  • UniFi Cloud Key, : /usr/lib/unifi
  • UniFi Dream Machine: /usr/lib/unifi (you must first enter unifi-os shell)
  • Debian/Ubuntu Linux: /usr/lib/unifi
  • Windows: %userprofile%/Ubiquiti UniFi.
  • macOS: ~/Library/Application Support/UniFi
  • Docker compose : /opt/containers/unifi/data

Path for config.gateway.json

The file config.gateway.json should be created under <unifi_base>/data/sites/site_ID, where site_ID value is default most of the times. You can always find the site_ID from the url of the Unifi Controller https://192.168.11.10/manage/<site_ID>/dashboard.

Missing sites path

In my case the site_ID path was not there under <unifi_base>/data. In order to have these folders you have to upload a floorplan at least one time so the folder structures will be created. Unfortunately there is no way to create a Floorplan using the modern UI of the controller (at least up to version 7.2.94), so we need to enable the “Legacy Interface”.

Go to Settings -> System -> Legacy Interface and enable it.

Then go to Map and from the top left dropdown select Topology and change it to Floor plan. Then add a floor plan and save it. Any map imagec in the UniFi GUI will work. Now the directory structure should be generated and you could delete the floor plan and disable the “Legacy Interface”.

Add Cloudflare configuration

I assume that the interface used for Dynamic DNS is pppoe0, if you are using a different interface eg: eth0 change the value at line 6.

Now you should create the file <unifi_base>/data/sites/site_IDconfig.gateway.json and add the cloudflare configuration

{
  "service": {
    "dns": {
      "dynamic": {
        "interface": {
          "pppoe0": {
            "service": {
              "custom-cloudflare": {
                "host-name": [
                  "myserver.example.com"
                ],
                "login": "<USERNAME>",
                "options": [
                  "zone=example.com"
                ],
                "password": "<GlobalCloudFlareAPIToken>",
                "protocol": "cloudflare",
                "server": "api.cloudflare.com/client/v4"
              }
            }
          }
        }
      }
    }
  }
}

Note: You may need to add a line just before service, in order to find the public IP by using DynDns web service instead of the IP assigned to the interface:

            "web": "dyndns",

Note: This version of ddclient needs the Global Cloudflare API token. When we change this instructions to use ddclient version >= 3.10 we can start an API token that’s been granted All zones, Zone:Read and DNS:Edit permissions.

The config.gateway.json file must have unifi:unifi as the owner and group permissions. You can check to verify with

ls -l <unifi_base>/data/sites/site_ID

To change it, once you’re in the site directory, use the command:

chown unifi:unifi config.gateway.json

Applying the configuration

After adding the config.gateway.json to the UniFi Network site of your choosing, you can test it by running a “force provision” to the USG in UniFi Devices -> select the USG -> Config -> Manage Device -> Force provision. This will take a while to provision (30 seconds to 3 minutes), and if it stays in provisioning longer than that, there may be a formatting error in the config.gateway.json, and you are experiencing the provisioning loop that was mentioned earlier. You shoould check the Unifi controller notifications about the provisioning of USG or you can check server.log in the application and search for commit error. You can usually find what went wrong with the provisioning of the newly customized configuration in the log files.

Verification

Check DNS entry

You can visit Cloudflare dashboard and check if the appropriate DNS entry is updated or you could simply lookup the appropriate DNS entry:

nslookup myserver.example.com 1.1.1.1

Check USG Dynamic DNS status

Connect to USG using SSH and become root to check dyndns status

sudo su -
show dns dynamic status
interface    : pppoe0
ip address   : 1.2.3.4
host-name    : myserver.example.com
last update  : Thu Jan  1 02:00:00 1970
update-status: good

Get Zone info

You can information for a zone by using Cloudflare authentication key:

curl -X GET "https://api.cloudflare.com/client/v4/zones?name=example.com" \
     -H "Content-Type:application/json" 
     -H "X-Auth-Email: <USERNAME>" \
     -H "X-Auth-Key: <GlobalCloudFlareAPIToken>" 

or using an API token:

curl -X GET "https://api.cloudflare.com/client/v4/zones?name=example.com" \
     -H "Authorization: Bearer <API_TOKEN>" \
     -H "Content-Type:application/json"

if the API token is not working you can validate it:

curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
     -H "Authorization: Bearer <API_TOKEN>" \
     -H "Content-Type:application/json"

ddclient configuration

After the Forced provision of the USG a file with the configuration for the specified interface is created /etc/ddclient/ddclient_<INTERFACE>.conf. In my case file /etc/ddclient/ddclient_pppoe0.conf with the following contents:

#
# autogenerated by vyatta-dynamic-dns.pl on Mon ...
#
daemon=1m
syslog=yes
ssl=yes
pid=/var/run/ddclient/ddclient_pppoe0.pid
cache=/var/cache/ddclient/ddclient_pppoe0.cache
use=if, if=pppoe0


# Service : custom-cloudflare
server=api.cloudflare.com/client/v4, protocol=cloudflare max-interval=28d zone=example.com login=<USERNAME> password='<GlobalCloudFlareAPIToken>' myserver.example.com

Check ddclient logs

To see ddclient messages connect to USG using SSH and as root run:

grep ddclient /var/log/messages 

Debug ddclient

Connect to USG using SSH and as root:

  • kill ddclient
    pkill ddclient
    
  • run ddclient with extra logging
    pkill ddclient 
    ddclient -foreground -force -debug -verbose -file /etc/ddclient/ddclient_pppoe0.conf
    

Now check again the ddclient logs

Note: If you see http://www.cloudflare.com/api_json.html in your requests, this means that you are using an old ddclient with API v1 support.