Over the years I've set up a variety of private cloud services. In addition to gaining expertise in these services for clients who would rather rely on their own infrastructure, it's also made me less dependent on web company services which could (and have) disappeared at inconvenient times.
This desire for virtual independence has led me to set up, run and administer my own mail server (goodbye gmail!), git server, read-it-later server (goodbye Pocket!), rss feed sync server (goodbye Feedly), and even a document sync server (goodbye Dropbox). I want some of these services to always be accessible. Those services I host with Linode.
Others—the sync servers—may contain private information I don't necessarily want on someone else's infrastructure (e.g. Linode). Fortunately, these are also services I don't always need access to, "most of the time" is more than good enough. If a sync server is not available when a sync action is requested, the sync client will simply try again later.
I host those sync services from a server under my control, with a residential connection. The problem with this setup is that the IP address may potentially change at any time. As I don't want to go into every device and change settings whenever an IP address changes, I've assigned this IP address a domain name. This way IP address changes only have to be updated in one location1.
I could do better though. I was still waiting for things to break before I act. I could be a bit more proactive. There is such a thing as dynamic DNS which will detect when a change to an IP address for a domain happens, and then update the DNS entry. There is one gotcha to this plan, and it's that Linode has a very functional, but very basic DNS manager. They have no dynamic DNS client that I know of so that seemed to be out of the question. Or it was, until I happened across a very useful article on the interwebs. One Travis Maynard wrote a post on how to get Dynamic DNS working with the Linode API.
Below are my modified instructions on how to get it working with my home-grown opnsense firewall/router. Before you ask: Yes, I could set it up on any machine. However, the router is the only one that is guaranteed to be connected to the internet all the time.
So let's get to it. As explained in the article, if you're using a Linode there is some information you'll need to find out before you can get this all to work.
Create a new user
- Login to your Linode account and navigate to Accounts and then select the Users and Permissions tab.
- Create another user by selecting the Add a User option.
- Make sure that the Yes - this user can only do what I specify option is selected and then Save Changes to create the user.
Give new user specific permissions on desired domains
The next screen will display all of the options that you have to grant or restrict to the user. In this case, all you want to do is check the "All Privs" checkbox on the domain that you've added underneath the DNS Zone Grants section. This will prevent the user (and consequentially the API key) from making changes to anything but the domain we have specified.
— Travis Maynard
Create a Linode API key for the new user
- Login to your Linode as the new user and go to My Profile.
- Select the API Keys tab.
- Create a new API Key.
- Record the API Key for later use.
Find out DOMAINID and RESOURCEID
To find the domain ID:
- In a browser, navigate to the URL2:
MY_API_KEYportion with your actual API Key.
- Locate the
DOMAINIDfor the domain that will have its IP address updated dynamically.
- Using the newly obtained
DOMAINID, modify the URL to the form
https://api.linode.com/?api_key=MY_API_KEY&api_action=domain.resource.list&domainid=DOMAIN_IDand navigate to it in a browser again.
- Search for the subdomain you want to update based on the
NAMEproperty, and copy the
- Modify the URL, and add the new
RESOURCEIDto the end of it:
- Navigate to this URL in a browser to check that it's correct.
To build the full URL neccessary we need some more changes. The near final form is:
What we've done is to change the
domain.resource.list portion of the URL to
domain.resource.update, while appending
&target=[remote_addr] to the end of the URL.
&target=[remote_addr]addition will obtain the external IP address of the machine making the request (your computer/router).
domain.resource.updatewill cause the changes to be recorded in Linode's DNS manager.
After visting the URL in the browser you'll be able to verify that an actual update happened when you check the Last Modified date for the affected Domain Zone in the DNS Manager.
Setup cron to run command periodically
Once you have verified that the update does in fact happen, it's time to automate this. I'll be using the tool
cron to periodically execute a script that visits the URL we crafted above. The script will use the
curl command to access the URL.
For use with curl, we have to escape the
target value's square brackets, and put the entire URL in quotes. Here is the complete URL:
This is the command cron will run:
And finally, here is what we paste into the crontab file, after accessing it with the command
crontab -e in a terminal:
*/30 * * * * /bin/echo `/bin/date`: `curl 'https://api.linode.com/?api_key=MY_API_KEY&api_action=domain.resource.update&domainid=DOMAIN_ID&resourceid=RESOURCE_ID&target=\[remote_addr\]'` >> /var/log/linode_dyndns.log
The cron settings above will run the curl command every 30 minutes and log the result to the
linode_dyndns.log file in the
/var/log directory. Or rather, it will if the user running this cron job has write access to the directory. If not, create the file and make it accessible to the user. Alternatively, change the file path to somewhere the user has permission to write to.