Hosting your home server with your own Dynamic DNS (GoDaddy)

...

I'm a geek. I have a home (on premise!) server running Linux used for a lot of experimentation and runs a lot of software I had to otherwise pay for when using a public cloud. And I'm building another two thanks to one of my friends who gave away two powerful servers he doesn't use. And I'd like to access my servers from the outside when I'm not at home. You can always punch a hole in your home router to forward traffic from a specific port to a server, however, home broadband usually changes IP addresses frequently, therefore you can't rely on it. Of course you can pay your ISP to reserve a static IP, but I'm cheap and adventurous. You can always use one of the free dynamic DNS services, however most of them are also paid or not working, therefore I went on a journey to solve this problem.

GoDaddy

My domain is hosted with GoDaddy, it's a popular service used by many. However, what not many of us know, is that GoDaddy has a REST API for some of their services, and as lucky as I am, Domains API are part of them. Domains API allow you to list domains you own, get public information, and most importantly get or set DNS records from the API. This essentially solves my problem!

Testing API

First of all, I wanted to test how the API works, and for that I've used this awesome VS Code extension, which is free and just works. I am (or was) big fan of Postman, it's like an IDE for REST API development and it's great, however it has one caveat - you can't share your projects effectively in a team, unless you're on a paid subscription plan (actually, all the team members have to be on a paid plan) which doesn't justify the costs and makes it extremely unattractive for personal and occasional use. REST Client for VS code works with a local text file, which you can just commit to your git repo and make it available to everyone using normal methods.

To generate an API key, go to API Key Management page and add one to get the key and secret. You would want to check that "production" is enabled, otherwise you won't be able to update real life records:

Then copy key and secret value somewhere safe

The API also requires something called X-Shopper-ID header in all of the calls, but this is simply your customer id you can grab from the top-right corner on godaddy website:

Adding a new DNS Record

Before I could test the API, I wanted to create a new subdomain for my home server, let's call it lxmoon, so I went ahead and did it on domain management page:

To avoid long waits between router IP address changes, you might want to also set TTL to the minimum value. To validate that your record has updated, you can use an online tool like DNS Checker, to avoid confusion with local machine DNS caching, because it will take some time before your machine sees the new IP.

Updating the Record

Using the API, the first this is to test what's returned for the new subdomain, therefore I've created a file called godaddy.rest in VS Code and filled it in with the following:

@key = your_key
@secret = your_secret
@auth = sso-key {{key}}:{{secret}}
@domain = your_domain
@shopperId = customer_id
## get domain record details
GET https://api.godaddy.com/v1/domains/{{domain}}/records/A/lxmoon
Authorization: {{auth}}
X-Shopper-Id: {{shopperId}}

Then press "send in VS code to see the results":

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 66
Strict-Transport-Security: max-age=15724800; includeSubDomains
X-Request-Id: 98af72bdd9f01fb322d0876021ac326e
X-DataCenter: PHX3
Expires: Thu, 24 Jan 2019 10:46:32 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Thu, 24 Jan 2019 10:46:32 GMT
Connection: keep-alive
[
  {
    "data": "1.1.1.1",
    "name": "lxmoon",
    "ttl": 1800,
    "type": "A"
  }
]

As you can see, it has returned the information about my domain, and it's all correct, I've used initial IP 1.1.1.1 to test the thing, of course it's invalid. Now, the second task is to be able to update the IP address to something new, and there is a call for that too:

## update my A record
PUT https://api.godaddy.com/v1/domains/{{domain}}/records/A/lxmoon
Authorization: {{auth}}
X-Shopper-Id: {{shopperId}}
Content-Type: application/json
[
  {
    "data": "1.1.1.2",
    "name": "lxmoon",
    "ttl": 1800,
    "type": "A"
  }
]

Here I'm trying to update my record to 1.1.1.2, again just for test. And it worked! Problem solved.

Automating the solution

I've got the REST part working, however it's not possible to run it on a server, as those are just local tests. Therefore my next step would be to ensure I can run an automatic DNS update script somewhere on my Linux server. And because .NET Core runs on Linux, I've decided to wrap this up in a nice .NET Core Global tool project. You can grab this project from this GitHub repo to play with, and it's also published on NuGet. It essentially replicates what i've done in a more mature way. This tool accepts the following parameters:

Usage: a <domain> <record> [options]
  updates A record
Arguments
  domain  name of the domain to update
  record  name of the record to update
Options
  -c|--customer-id  customer id
  -k|--key          your key (go to https://developer.godaddy.com/keys to obtain one)
  -s|--secret       your secret
  -v|--value        value of the new IP address, optional, when not specified will set to your current external IP

Now it's time to ssh to my Linux server and use the tool (it already has .net core installed):

ivan@moon:~$ dotnet tool install -g update-godaddy-a-record
You can invoke the tool using the following command: update-godaddy-a-record
Tool 'update-godaddy-a-record' (version '1.0.0') was successfully installed.

Because .net core is magic, I can just run the tool and see what happens:

It actually works!

Running it on schedule

The easiest way to run on schedule is to use cron in Linux. I've already installed .NET Global tool into my user's account, therefore we need to tell cron to run it under my account. If you use something like crontab it won't work, because it runs all the tasks under cron user, which has to access to my account's global tools.

The tools in Linux are actually installed under ~/.dotnet/tools as you can see here:

ivan@moon:~$ ls ./.dotnet/tools/
dotnet-housework  update-godaddy-a-record

And they're executables. Therefore to run it on schedule you need to modify /etc/crontab as administrator (sudo nano /etc/crontab) and add the following line to the file:


*/15 * * * * your_username ~/.dotnet/tools/update-godaddy-a-record a gavryli.uk lxmoon -c customer_id -k key -s secret 1> ~/gd.out.log 2> ~/gd.err.log

In this example i'm telling cron to run the task every 15 minutes, under your_username user, and directly referencing the tool's path, passing all the required parameters. I'm also redirecting console and error output to local log files just to see if anything goes wrong, but you can remove it once you're happy with the results

To check that it's running grep linux syslog for cron entries grep CRON /var/log/syslog:

...
Jan 24 11:57:01 moon CRON[19924]: (ivan) CMD (~/.dotnet/tools/update-godaddy-a-record a gavryli.uk lxmoon -c ... -k ..._... -s ... 1> ~/gd.out.log 2> ~/gd.err.log)

All good here!

GoDaddy also displays my updated IP address (don't try to hack it, it's already changed!)


Thanks for reading. If you would like to follow up with future posts please subscribe to our rss feed.