On this blog and in my tweets, I have mentioned my love for using StackExchange's DNSControl a few times. One of my co-workers mentioned I should share more about why I enjoy using this tool and how you can too (Thanks Javier!). So in this blog, I am going to share my user story of automating and logging DNS changes with DNSimple & DNSControl.

I started with DNS back when I worked for a large print media company who hosted their own DNS with BIND on Solaris boxes. We managed several hundred domains in text files rsync'ed across four servers. When I started, I quickly found this to be completely unwieldy and dangerous. I wanted a better solution. I eventually came up with a system where we set up a subversion server for operations (gasp – what?) and checked in all the bind configurations. Then it was just a matter of adding a hook to email the ops team whenever someone committed a change, and then log into all the servers and make them pull. Eventually I added Jenkins to the mix to replace the hooks with tests and handle the deploy. I felt like a genius. Listen, I was an early 20-something, so give me a break. We eventually rolled out Nagios with the same ordeal, allowing safe and sane changes across multiple teams.

At a later job, we did the same thing with TinyDNS, git, and some Makefiles to control consistency with the commit & deploy steps.

I was a customer of DNSimple before I became an employee. However, I only stored a small subset of my "important" domains in DNSimple, and purchased and managed several in a horrible mix of providers and self-hosted TinyDNS. When I discovered DNSControl from Stack Exchange, the way I handle DNS changed forever, and maybe it will change for you and your team too.

Getting Started with DNSControl

Now, I do not want to replace the well-written documentation, but let us quickly go over how to use DNSControl. You start by installing the command line with go, then creating two files: a creds.json and a dnconfig.js.

The creds.json file is simple – a json data structure of your DNSimple account info:

{
  "dnsimple_example":{
    "token": "myApiToken"
  }
}

The power here is that you can have multiple accounts set up – either different DNSimple accounts or a mix of providers like NS1, DigitalOcean, and Hexonet – all managed in the same file.

The dnscontrol.js is where the real work is done. The file is written in javascript, but do not worry if you are not a JS developer, I know I am not. All of DNSControl's data is declared with a simple DSL. Here is a quick example of one of my favorite domains.

var REG_DNSIMPLE = NewRegistrar('dnsimple_example', 'DNSIMPLE');
var DNSIMPLE = NewDnsProvider('dnsimple_example', 'DNSIMPLE');

DEFAULTS(DefaultTTL(3600));

D('wearing.pink', REG_DNSIMPLE, DnsProvider(DNSIMPLE),
  CNAME('autodiscover', 'autodiscover.outlook.com.'),
  MX('@', 0, 'wearing-pink.mail.protection.outlook.com.'),
  TXT('@', 'MS=ms77747446'),
  TXT('@', 'v=spf1 include:spf.protection.outlook.com -all'),
)

To simplify things for later, I have made a variable of the DNSimple registrar and provider settings and set my default TTL.

Below that, I declare the domain wearing.pink, tell DNSControl that DNSimple is both the registrar and DNS provider, then define the standard Office 365 records for my email.

Now that I have this all set up, I can run the following commands to test my configuration and see what changes would be made:

# Check my configuration and do a dry run to see what would change
~/go/bin/dnscontrol preview
# update all dns
~/go/bin/dnscontrol push

Since this is all javascript, I could even do clever things like this:

// Frequently used settings
var FrequentlyUsedIP = IP('1.2.3.4')

// Domains to park
var unused_domains = [
  'furry.expert',
  'onlyhavecans.fyi',
  'squirrel.moe',
]

for (i = 0; i < unused_domains.length; i++) {
  D(unused_domains[i], REG_DNSIMPLE, DnsProvider(DNSIMPLE),
    A('@', FrequentlyUsedIP)
  )
}

If you want more ideas for clever javascript tricks to do with DNSControl, check out the example documentation.

All of that is cool, but I do not think we have addressed the full power of DNSControl yet.

Revision Control & Automation

Being a command line utility with plain text configurations, we can use git to keep a revision history of all of our config changes! Now, I do not want to allow for accidents to happen and changes to be missed, so let us use make to script this:

DNSCONTROL=~/go/bin/dnscontrol

all: check build

check: update creds.json
  # verify the json structure
  python -m json.tool creds.json
  $(DNSCONTROL) version

build: creds.json dnsconfig.js
  # Verify config and dry run
  $(DNSCONTROL) preview

commit: check build
  # interactively ask about every change to commit
  git add --patch
  git commit --signoff --verbose
  git push origin master
PHONY: commit

deploy: check commit
  # push to production
  $(DNSCONTROL) push
PHONY: deploy

update:
  go get -u -v github.com/StackExchange/dnscontrol
PHONY: update

Now we are really cooking. When we run make, we will do a dry run and verify all our configs. If we are happy with everything, we can run make commit to verify everything and commit the changes, or go all in with a make deploy to run all of the steps.

Next Level: CI/CD & Team Management

What I have shown above is great for a single user managing their domains in a controlled way, but let us take it even father now for the business customers.

By hosting this repo in our private Gitea, Bitbucket, or GitHub instance, and converting the Makefile to a Jenkinsfile or Travis-CI configuration, you can have your team check out the repo, make changes in pull requests so that your CI can run the preview to test changes and do team reviews, then have a merge to master deliver the change right on the spot.

You probably do not want every user to have the production credentials on their machine. So in this example, we would want to use the sandbox creds.json below in repo for devs to use. This links DNSControl to the DNSimple sandbox, so they can test in sandbox but not modify production. Then you can encrypt the real credentials and have your CI use those.

{
  "dnsimple_example":{
    "baseurl": "https://api.sandbox.dnsimple.com",
    "token": "SandboxApiKey"
  }
}

Wrapping Up

I hope this deep dive in to how I manage my domains sparks you to think about what interfaces you and your team use to control your DNS records.

DNScontrol is admittedly a very opinionated tool that does one thing really well. It will not cover all your domain and certificate automating needs, but by using some scripting and version control you can really take control of DNS.

Happy Hacking!