Dynamic DNS (DDNS) in AWS for internal zones

6 minute read , Dec 27, 2014

The following configuration will resolve the internal (meaning inside VPC) domain queries for encompasshost.com and forward all other queries to the default VPC dns server. This will make possible to have more friendly and meaningful host names like app11.encompasshost.com instead of default one ip-10-122-0-211.ap-southeast-2.compute.internal inside the VPC.

Server Setup

We will use BIND9 as DNS server since it provides the needed functionality.


On Ubuntu/Debian this is simple as:

root@ip-10-122-0-211:~# aptitude install bind9 dnsutils

BIND9 Configuration

We need to configure the RNDC key file for DDNS authorization, the main named config file and the zone files of course. If the key file config is not generated by default (depends on the bind version installed) we can create one:

root@ip-10-122-0-211:~# rndc-confgen -a -b 128

This will generate just the key clause and write it to keyfile /etc/bind/rndc.key. The key length will be 128 which is default one and may be up to 512 bits.

key "rndc-key" {
    algorithm hmac-md5;
    secret "1KJjYVuDxsJpcmGIJs/0Uw==";

Next we put some options we need in the bind config file /etc/bind/named.conf.options:

options {
    directory "/var/cache/bind";
    dnssec-validation no;
    auth-nxdomain no;    # conform to RFC1035
    listen-on-v6 { none; };
    version "get lost";
    allow-transfer { none; };
    allow-query { any; };
    minimal-responses yes;
    forwarders {
        // AWS DNS server for the VPC;

and populate the local config file /etc/bind/named.conf.local with our settings and zones where we specify who and how can update our zones and access the DNS server in general:

// Do any local configuration here
// Consider adding the 1918 zones here, if they are not used in your
// organization
include "/etc/bind/zones.rfc1918";
// logging
    channel encompasshost_log{
       file "/var/log/named/encompasshost.log" versions 3 size 2m;
       severity info;
       print-severity yes;
       print-time yes;
       print-category yes;
    category default{
// ACLs
acl "internals" {;;
controls {
    inet allow { "internals"; } keys { "rndc-key"; };
// Add local zone definitions here.
zone "encompasshost.com" {
    type master;
    file "encompasshost.com.db";
    allow-update { key "rndc-key"; };
    notify yes;
zone "122.10.in-addr.arpa" {
    type master;
    file "10.122.db";
    allow-update { key "rndc-key"; };
    notify yes;
include "/etc/bind/rndc.key";

Next we create the master zone file /var/cache/bind/encompasshost.com.db we have referenced in the above setup:

$TTL 259200    ; 3 days
encompasshost.com    IN SOA    ns1.encompasshost.com. root.ns1.encompasshost.com. (
                2012122209 ; serial
                28800      ; refresh (8 hours)
                7200       ; retry (2 hours)
                2419200    ; expire (4 weeks)
                86400      ; minimum (1 day)
            NS    ns1.encompasshost.com.
            MX    10 mail.encompasshost.com.
            SPF   "v=spf1 ip4: ~all"
$ORIGIN encompasshost.com.
_domainkey       TXT    "t=y\; o=~\;"
amq-db11         A
amq11            A
amq21            A
app11            A
app21            A
$TTL 30    ; 30 seconds
camel            A
$TTL 259200    ; 3 days
db11             A
db12             A
db21             A
gfs11            A
gfs21            A
gw               A
                 TXT    "Network gateway"
mail             A
                 TXT    "v=spf1 a -all"
ns1              A
$TTL 86400    ; 1 day
somehost         A
$TTL 259200    ; 3 days
www              CNAME    encompasshost.com.

and the reverse zone file /var/cache/bind/10.122.db too:

; Filename: 10.122.db
; Reverse Zone file for 10.122.x.x
@       IN      SOA     ns1.encompasshost.com. root.ns1.encompasshost.com. (
                        2014122201 ; Serial, todays date + todays serial
                        8H      ; Refresh
                        2H      ; Retry
                        4W      ; Expire
                        1D)     ; Minimum TTL
                IN      NS      ns1.encompasshost.com.
$ORIGIN 0.122.10.in-addr.arpa.
211     IN     PTR     ns1.encompasshost.com.
43      IN     PTR     app11.encompasshost.com.
$ORIGIN 1.122.10.in-addr.arpa.
245     IN     PTR     gfs11.encompasshost.com.
215     IN     PTR     amq11.encompasshost.com.
189     IN     PTR     amq-db11.encompasshost.com.
$ORIGIN 2.122.10.in-addr.arpa.
146     IN     PTR     db11.encompasshost.com.
132     IN     PTR     db12.encompasshost.com.
$ORIGIN 10.122.10.in-addr.arpa.
35      IN     PTR     app21.encompasshost.com.
$ORIGIN 11.122.10.in-addr.arpa.
254     IN     PTR     gfs21.encompasshost.com.
29      IN     PTR     amq21.encompasshost.com.
$ORIGIN 22.122.10.in-addr.arpa.
198     IN     PTR     db21.encompasshost.com.

We also add host name resolution for the DNS server it self in the hosts file /etc/hosts:    ns1.encompasshost.com ns1

Create the log dir:

root@ip-10-122-0-211:~# mkdir -p /var/log/named
root@ip-10-122-0-211:~# chown bind\: /var/log/named

Now all is ready to restart the server:

root@ip-10-122-0-211:~# service bind9 restart

DDNS setup

Setting the DNS is not enough. Since everything in AWS is dynamic we also want to dynamically update our zone too. We will use nsupdate tool for that purpose. First we create nsupdate file my-nsupdate.txt:

zone encompasshost.com
prereq nxdomain somehost2.encompasshost.com
update add somehost2.encompasshost.com. 86400 A

and then run the nsupdate to add a new host record for example:

root@ip-10-122-0-211:~# nsupdate -k /etc/bind/rndc.key -v my-nsupdate.txt
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;encompasshost.com.        IN    SOA
somehost2.encompasshost.com. 0    NONE    ANY
somehost2.encompasshost.com. 86400 IN    A

The server log confirms the record being inserted:

23-Dec-2014 10:36:54.086 update-security: info: client rndc-key: signer "rndc-key" approved
23-Dec-2014 10:36:54.086 update: info: client rndc-key: updating zone 'encompasshost.com/IN': adding an RR at 'somehost2.encompasshost.com' A

Now if we check for that host resolution:

root@ip-10-122-0-211:~# dig +short somehost2.encompasshost.com

but only after issuing:

root@ip-10-122-0-211:~# rndc sync

OR in case that didn’t work:

root@ip-10-122-0-211:~# rndc freeze && rndc thaw

we can see the zone file updated as well. This is due to the DDNS caching so the above is needed only in cases when we want immediate update of the zone file.

Now we can update our reverse zone as well. We create a new file my-reverse-nsupdate.txt:

zone 122.10.in-addr.arpa
update add 86400 IN PTR somehost2.encompasshost.com.

and run nsupdate:

root@ip-10-122-0-211:~# nsupdate -k /etc/bind/rndc.key -v my-reverse-nsupdate.txt
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;122.10.in-addr.arpa.        IN    SOA
0.122.10.in-addr.arpa.    86400    IN    PTR    somehost2.encompasshost.com.

The server log should confirm the record being added:

23-Dec-2014 10:43:16.692 update-security: info: client rndc-key: signer "rndc-key" approved
23-Dec-2014 10:43:16.692 update: info: client rndc-key: updating zone '122.10.in-addr.arpa/IN': adding an RR at '0.122.10.in-addr.arpa' PTR

To remove records we replace add with delete in the ddns files. In case we want to add/delete multiple records in the same go, the update lines have to be separated by an empty line in the nsupdate file. For example:

zone 122.10.in-addr.arpa
update delete 86400 IN PTR somehost2.encompasshost.com.
update delete 86400 IN PTR somehost3.encompasshost.com.

As we mentioned before DDNS uses caching. It creates journaling files for the zones:

root@ip-10-122-0-211:~# ls -l /var/cache/bind/*.jnl
total 20
-rw-r--r-- 1 bind bind  802 Dec 23 10:43 10.122.db.jnl
-rw-r--r-- 1 bind bind 1313 Dec 23 10:36 encompasshost.com.db.jnl

and this journal gets flushed to the zone files on regular intervals, 15 minutes by default, except when forced via rndc as shown above.

Worth mentioning here is that once the zone has been put under control of DDNS the zone files should not be updated manually any more.

Client Setup

On the clients we need to introduce our new DNS server and extend the search stanza in the DNS resolver so we can find the hosts by their short name. Since the DNS resolution in VPC hosts is set via DHCP on the primary interface we need to add the following options to the DHCP client setup in /etc/dhcp/dhclient.conf file:

prepend domain-name-servers;
append domain-name " encompasshost.com";

After restarting the networking the resolution file /etc/resolv.conf will look like this:

search ap-southeast-2.compute.internal encompasshost.com

which will enable the hosts to use our DNS server.


We can test the dns resolution from one of the servers in the VPC:

root@ip-10-122-0-43:~# dig +short @ app21.encompasshost.com

and the reverse resolution as well:

root@ip-10-122-0-43:~# dig +short @ -x

We can also confirm that the resolution of the default ap-southeast-2.compute.internal domain is still working:

root@ip-10-122-0-43:~# dig +short @ ip-10-122-11-29.ap-southeast-2.compute.internal
root@ip-10-122-0-43:~# dig +short @ -x
root@ip-10-122-0-43:~# host ip-10-122-11-29.ap-southeast-2.compute.internal
ip-10-122-11-29.ap-southeast-2.compute.internal has address
root@ip-10-122-0-43:~# host ip-10-122-11-29
ip-10-122-11-29.ap-southeast-2.compute.internal has address

All was left now is package all this into Ansible role for our automated deployments.

Leave a Comment