Note: this is a public draft. It contains notes worth sharing, but it still needs a bit of rewriting.


Background


I’ve decided to ditch NoIP service today. As good it is for casual use, forgetting to renew the hostname results in… a lot of problems.

I’ve switched to CloudFlare DNS due to faster DNS lookup times and content caching services. CDN is a nice thing to have.

My current cloud server is on an A record, and all the home vhosts and other things are using CNAME redirection via the hopto.org domain.

Goal: to make my own “dynamic DNS”, the dumb way.


How am I going to do it?


Let us create an A record on the CloudFlare DNS service, and name it myIp.domain.tld. For now, let us point it to current real IP (which will get regularly updated). Other domains, currently on hopto.org CNAME, get reconfigured to that A record. The script runs on Cron to check the IP (1x per 4 hours), and, if necessary update the record.

Luckily, CloudFlare has my back with their http API:


Where am I going to get my public IP?


Let us discover our IP via my main cloud server: consider a file

www.domain.tld/my/path/to/ip.php

<?php 
echo $_SERVER['REMOTE_ADDR'];
?>

This PHP script works flawlessly, unless if being accessed over a proxy

So now, using curl, we can access the file from any IP in the world and use its output as our feedback. This is our IP address.


But you mentioned Cron, so there must be a script, right?


Right.

Let’s pop-open an empty file.

ip_update.sh

IP=$(curl www.domain.tld/my/path/to/ip.php)

Getting the data


Surf to CloudFlare’s dashboard, select your domain, and wait for Overview webpage. Store our zone key and email into variables. We’re going to need both of that.

ip_update.sh

DNS_ZONE=............
EMAIL=[email protected]

let’s add the domain we want to update

ip_update.sh

NS_NAME=myhomeip.domain.tld

Back in our DNS Overview page, we have a hyperlink just beneath our zone ID. It’s called “Get your API key”. Click on it, and find the “Global API Key”. Store it to the variable.

ip_update.sh

API_KEY=................

So, according to the Cloudflare API, we need to have their DNS record’s ID to update it.

Their useful API docu even has an example, so let’s try it.

ip_update.sh

# Get record data
curl -X GET "https://api.cloudflare.com/client/v4/zones/$DNS_ZONE/dns_records?type=A&name=$NS_NAME" \
	     -H "X-Auth-Email: $EMAIL" \
	          -H "X-Auth-Key: $API_KEY" \
		       -H "Content-Type: application/json"

Save the script and run it. you should get something like JSON response with elements. One of them starts with the name id.

Comment out the curl part we’ve just smashed into our script or discard it. We won’t be needing that anymore.

Copy the ID and save that into the variable.

NS_RECORD=...........

Time to be updated.


This is how your script looks now.

ip_update.sh

IP=$(curl https://cloud.domain.tld/path/to/ip.php)
DNS_ZONE=..............
EMAIL=[email protected]
API_KEY=........
NS_NAME=homeip.domain.tld
NS_RECORD=..........
#Update record:
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$DNS_ZONE/dns_records/$NS_RECORD" \
	     -H "X-Auth-Email: $EMAIL" \
	          -H "X-Auth-Key: $API_KEY" \
		       -H "Content-Type: application/json" \
		            --data "{\"type\":\"A\",\"name\":\"$NS_NAME\",\"content\":\"$IP\",\"ttl\":1800,\"proxied\":false}"

##TODO

Check if IP needs updating first. Always check things first.

Crontab the script every 4 hours. Or once a day.

Just a few links

Full file:

ip_update.sh

IP=$(curl https://cloud.domain.tld/path/to/ip.php)
DNS_ZONE=..............
EMAIL=[email protected]
API_KEY=........
NS_NAME=homeip.domain.tld
NS_RECORD=..........
dnsIpResult=$(curl -X GET "https://api.cloudflare.com/client/v4/zones/$DNS_ZONE/dns_records?type=A&name=$NS_NAME" \
	     -H "X-Auth-Email: $EMAIL" \
	          -H "X-Auth-Key: $API_KEY" \
		  -H "Content-Type: application/json")
dnsIp=$(echo $dnsIpResult | jq -r '.result[0].content')
echo "My DNS ip: $dnsIp";
echo "My IP: $IP";
if [ "$dnsIp" = "$IP" ]
then
	echo "Record matches current IP, no need to update";
else
	echo "Update the record";
	#Update record:
	curl -X PUT "https://api.cloudflare.com/client/v4/zones/$DNS_ZONE/dns_records/$NS_RECORD" \
		     -H "X-Auth-Email: $EMAIL" \
		          -H "X-Auth-Key: $API_KEY" \
			       -H "Content-Type: application/json" \
			            --data "{\"type\":\"A\",\"name\":\"$NS_NAME\",\"content\":\"$IP\",\"ttl\":1800,\"proxied\":false}"
fi

Peace out :)

hint: curl and jq live in /usr/bin/, echo lives in /bin/…