Setup Dynamic DNS using Linode

Michael Shepanski
January 16, 2011
0 comments
Jan
16

Being a fan and veteran of both DNS services dyndns.org and no-ip.org I was always very slightly annoyed that I could not do this with my personal domain name. The annoyance went up a little more when DynDNS started forcing me to log in every 3 months just to let them know I am still alive.

Since moving to Linode this weekend, I setup my own short DNS updating script. Update the variables APIKEY, DOMAIN, and RECORD to your desired settings and let her rip. Here are my settings for reference. This way when I hit home.pushingkarma.com, it routes to my home network.

Install the Linode Python Bindings

Unfortunatly the Linode team didn't include a setup.py with the their release of the Python API Bindings, so we'll need to manually install them. You can go download the Linode Python Bindings from GitHub. The easiest way to to just clone their repo using the command git clone https://github.com/tjfontaine/linode-python.git.

Once you have a copy of them on your system, you can symlink or copy them to some location on your Python path. I put mine in the directory [PYTHON]/site-packages/linode/.... If you are unsure where your Python path is, you can spin up Terminal and run the following commands. You'll most commonly want to find the one ending with the string site-packages.

$ python
> import sys
> sys.path

The Update Script

#!/usr/bin/python
"""
linode-dns.py
Author: Michael Shepanski
Date: Jan 17, 2010

A script to update a DNS record on Linode to your external IP.

References:
  http://atxconsulting.com/content/linode-api-bindings
  https://github.com/tjfontaine/linode-python/
"""
import pycurl
import re
from linode import api
from StringIO import StringIO

APIKEY = '[SECRET_API_KEY]'
DOMAIN = 'pushingkarma.com'
RECORD = 'home'
CHECKIP = "http://checkip.dyndns.org:8245/"

def get_extenral_ip():
    """ Return the current external IP. """
    print "Fetching external IP from: %s" % CHECKIP
    html = StringIO()
    curl = pycurl.Curl()
    curl.setopt(curl.URL, CHECKIP)
    curl.setopt(curl.WRITEFUNCTION, html.write)
    curl.perform()
    curl.close()
    external_ip = re.findall('[0-9.]+', html.getvalue())[0]
    return external_ip

def set_dns_target(utarget, udomain=DOMAIN, urecord=RECORD):
    """ Update the domain's DNS record with the specified target. """
    linode = api.Api(APIKEY)
    for domain in linode.domain_list():
        if domain['DOMAIN'] == udomain:
            # Check the DNS Entry already exists
            for record in linode.domain_resource_list(domainid=domain['DOMAINID']):
                if record['NAME'] == urecord:
                    if record['TARGET'] == utarget:
                        # DNS Entry Already at the correct value
                        print "Entry '%s:%s' already set to '%s'." % (udomain, urecord, utarget)
                        return record['RESOURCEID']
                    else:
                        # DNS Entry found; Update it
                        print "Updating entry '%s:%s' target to '%s'." % (udomain, urecord, utarget)
                        return linode.domain_resource_update(domainid=domain['DOMAINID'],
                            resourceid=record['RESOURCEID'], target=utarget)
            # DNS Entry not found; Create it
            print "Creating entry '%s:%s' target as '%s'." % (udomain, urecord, utarget)
            return linode.domain_resource_create(domainid=domain['DOMAINID'],
                name=urecord, type='A', target=utarget, ttl_sec=3600)
            print "Error: Domain %s not found." % udomain

if __name__ == '__main__':
    set_dns_target(get_extenral_ip(), DOMAIN, RECORD)
    print "Done."

Setup a Cronjob

Add this line to your crontab using crontab -e to run the script once an hour.

0 * * * *    python /path/to/script/linode-dns.py

Thats It! -- You should be good to go.

Comments

There are currently no comments for this article.