Generating DNS-zones from the Digital Ocean API
June 1, 2016
DNS
Automation
When you’re managing an ever-changing machine infrastructure in a public cloud, it is easy to forget to add new and remove old machines from your DNS.
At PowerDNS, we had a demonstration setup that was rebuilt several times in the span of two weeks, and I got tired of editing the zonefile* by hand, so let’s automate the hell out of it.
Fortunately, Digital Ocean has an API that allows you to retrieve Droplet (VPS) information. This API has bindings in several languages, so it’s easy to generate these DNS-entries.
Here’s the quick ‘n dirty python script I whipped up:
#!/usr/bin/env python2
import sys
import time
import os
try:
import json
except ImportError:
import simplejson as json
try:
from dopy.manager import DoError, DoManager
except ImportError as e:
print("dopy library required for this script")
sys.exit(1)
conf = {}
################################################################################
# Change these settings
################################################################################
conf['APIkey'] = os.environ.get('API_KEY', 'changeme')
conf['zone'] = os.environ.get('ZONE', 'thisiswrong.example')
conf['ns'] = ['ns1.%s' % conf.get('zone'), 'ns2.%s' % conf.get('zone')]
conf['SOA'] = "%s. hostmaster.%s. %s 360 60 8640 360" % (conf.get('ns')[0], conf.get('zone'), int(time.time()))
################################################################################
# Begin script
################################################################################
manager = DoManager(None, conf.get('APIkey'), api_version=2)
droplets = manager.all_active_droplets()
sys.stdout.write("$ORIGIN %s.\n" % conf.get('zone'))
sys.stdout.write("$TTL 120\n")
sys.stdout.write("\n")
sys.stdout.write("@\tIN\tSOA\t%s\n" % conf.get('SOA'))
for ns in conf.get('ns'):
sys.stdout.write("@\tIN\tNS\t%s.\n" % ns)
sys.stdout.write("\n")
for droplet in droplets:
if not droplet['name'].endswith('.%s' % conf.get('zone')):
continue
name = droplet['name'].split('.')[0]
for net in droplet['networks']['v4']:
if net['type'] == 'public':
sys.stdout.write("%s\tIN\tA\t%s\n" % (name, net['ip_address']))
for net in droplet['networks']['v6']:
if net['type'] == 'public':
sys.stdout.write("%s\tIN\tAAAA\t%s\n" % (name, net['ip_address']))
You’ll need the dopy and six(required by dopy) python modules installed.
The easiest way to generate the zone is through cron:
*/5 * * * * ZONE=myzone.example; API_KEY=s3cr1t; /usr/local/bin/do-zone-generator.py > /tmp/$ZONE.zone && cp /tmp/$ZONE.zone /var/lib/powerdns/zones && pdns_control bind-reload-now $ZONE >/dev/null
This will generate the zone, copy it to the right place and tell PowerDNS to reload the zone (sending out NOTIFYs).
Of course, the script could be extended to insert the data into a database-backed PowerDNS using the PowerDNS API. But this is left as an exercise to the reader.
*: As a sidenote, even though the PowerDNS Authoritative Server is deployed most
of the time because of its excellent database backends,
for my own small-scale deployments, I prefer zone-files over a database. But this
might change, as pdnsutil
supports an edit-zone
command that will create a faux zonefile from the zone
in the database, open it in $EDITOR
and will insert it into the database after
editing. See Bert Hubert’s blogpost on this.