summaryrefslogtreecommitdiff
path: root/content/html/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html
blob: e177de6e0bca5a5f3adb615543b49f647e799a02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spinning up my own authoritative DNS servers</title>
<link rel="shortcut icon" type="image/gif" href="/favicon.ico" />
<style type="text/css">
body {
    margin: auto;
    max-width: 900px;
    background-color: #FFFFEF;
    border: 1px dashed #880000;
    border-radius: 8px;
    padding: 5px;
}
img {
    display:block;
    max-width: 80%;
}
a.textlink:before {
    content: " ⇒ ";
    padding-left: 2px;
}
a.textlink {
    text-decoration: none;
    color: #FF0000;
}
a.textlink:hover {
    text-decoration: underline;
}
i {
    color: #FFA500;
}
pre {
    background-color: #F1F8E9;
    border: 1px dashed #BB0000;
    border-radius: 8px;
    padding: 5px;
    font-family: "Lucida Console", "Courier New", monospace;
}
h1 {
    text-align: center;
    color: #880000;
}
h2, h3 {
    color: #BB0000;
}
</style>
</head>
<body>
<h1>Spinning up my own authoritative DNS servers</h1>
<p class="quote"><i>Written by Paul Buetow 2016-05-22</i></p>
<h2>Background</h2>
<p>Finally, I had time to deploy my own authoritative DNS servers (master and slave) for my domains "buetow.org" and "buetow.zone". My domain name provider is Schlund Technologies. They allow their customers to manually edit the DNS records (BIND files). And they also give you the opportunity to set your own authoritative DNS servers for your domains. From now, I am making use of that option.</p>
<a class="textlink" href="http://www.schlundtech.de">Schlund Technologies</a><br />
<h2>All FreeBSD Jails</h2>
<p>In order to set up my authoritative DNS servers I installed a FreeBSD Jail dedicated for DNS with Puppet on my root machine as follows:</p>
<pre>
include freebsd

freebsd::ipalias { '2a01:4f8:120:30e8::14':
  ensure    =&gt; up,
  proto     =&gt; 'inet6',
  preflen   =&gt; '64',
  interface =&gt; 're0',
  aliasnum  =&gt; '5',
}

include jail::freebsd

class { 'jail':
  ensure              =&gt; present,
  jails_config        =&gt; {
    dns                     =&gt; {
      '_ensure'             =&gt; present,
      '_type'               =&gt; 'freebsd',
      '_mirror'             =&gt; 'ftp://ftp.de.freebsd.org',
      '_remote_path'        =&gt; 'FreeBSD/releases/amd64/10.1-RELEASE',
      '_dists'              =&gt; [ 'base.txz', 'doc.txz', ],
      '_ensure_directories' =&gt; [ '/opt', '/opt/enc' ],
      'host.hostname'       =&gt; "'dns.ian.buetow.org'",
      'ip4.addr'            =&gt; '192.168.0.15',
      'ip6.addr'            =&gt; '2a01:4f8:120:30e8::15',
    },
    .
    .
  }
}
</pre>
<h2>PF firewall</h2>
<p>Please note that "dns.ian.buetow.org" is just the Jail name of the master DNS server (and "caprica.ian.buetow.org" the name of the Jail for the slave DNS server) and that I am using the DNS names "dns1.buetow.org" (master) and "dns2.buetow.org" (slave) for the actual service names (these are the DNS servers visible to the public). Please also note that the IPv4 address is an internal one. I have a PF to use NAT and PAT. The DNS ports are being forwarded (TCP and UDP) to that Jail. By default, all ports are blocked, so I am adding an exception rule for the IPv6 address as well. These are the PF rules in use:</p>
<pre>
% cat /etc/pf.conf
.
.
# dns.ian.buetow.org 
rdr pass on re0 proto tcp from any to $pub_ip port {53} -&gt; 192.168.0.15
rdr pass on re0 proto udp from any to $pub_ip port {53} -&gt; 192.168.0.15
pass in on re0 inet6 proto tcp from any to 2a01:4f8:120:30e8::15 port {53} flags S/SA keep state
pass in on re0 inet6 proto udp from any to 2a01:4f8:120:30e8::15 port {53} flags S/SA keep state
.
.
</pre>
<h2>Puppet managed BIND zone files</h2>
<p>In "manifests/dns.pp" (the Puppet manifest for the Master DNS Jail itself) I configured the BIND DNS server this way:</p>
<pre>
class { 'bind_freebsd':
  config         =&gt; "puppet:///files/bind/named.${::hostname}.conf",
  dynamic_config =&gt; "puppet:///files/bind/dynamic.${::hostname}",
}
</pre>
<p>The Puppet module is actually a pretty simple one. It installs the file "/usr/local/etc/named/named.conf" and it populates the "/usr/local/etc/named/dynamicdb" directory with all my zone files.</p>
<p>Once (Puppet-) applied inside of the Jail I get this:</p>
<pre>
paul uranus:~/git/blog/source [4268]% ssh admin@dns1.buetow.org.buetow.org pgrep -lf named
60748 /usr/local/sbin/named -u bind -c /usr/local/etc/namedb/named.conf
paul uranus:~/git/blog/source [4269]% ssh admin@dns1.buetow.org.buetow.org tail -n 13 /usr/local/etc/namedb/named.conf
zone "buetow.org" {
    type master;
    notify yes;
    allow-update { key "buetoworgkey"; };
    file "/usr/local/etc/namedb/dynamic/buetow.org";
};

zone "buetow.zone" {
    type master;
    notify yes;
    allow-update { key "buetoworgkey"; };
    file "/usr/local/etc/namedb/dynamic/buetow.zone";
};
paul uranus:~/git/blog/source [4277]% ssh admin@dns1.buetow.org.buetow.org cat /usr/local/etc/namedb/dynamic/buetow.org
$TTL 3600
@    IN   SOA   dns1.buetow.org. domains.buetow.org. (
     25       ; Serial
     604800   ; Refresh
     86400    ; Retry
     2419200  ; Expire
     604800 ) ; Negative Cache TTL
; Infrastructure domains
@ IN NS dns1
@ IN NS dns2
* 300 IN CNAME web.ian
buetow.org. 86400 IN A 78.46.80.70
buetow.org. 86400 IN AAAA 2a01:4f8:120:30e8:0:0:0:11
buetow.org. 86400 IN MX 10 mail.ian
dns1 86400 IN A 78.46.80.70
dns1 86400 IN AAAA 2a01:4f8:120:30e8:0:0:0:15
dns2 86400 IN A 164.177.171.32
dns2 86400 IN AAAA 2a03:2500:1:6:20::
.
.
.
.
</pre>
<p>That is my master DNS server. My slave DNS server runs in another Jail on another bare metal machine. Everything is set up similar to the master DNS server. However, that server is located in a different DC and in different IP subnets. The only difference is the "named.conf". It's configured to be a slave and that means that the "dynamicdb" gets populated by BIND itself while doing zone transfers from the master.</p>
<pre>
paul uranus:~/git/blog/source [4279]% ssh admin@dns2.buetow.org tail -n 11 /usr/local/etc/namedb/named.conf
zone "buetow.org" {
    type slave;
    masters { 78.46.80.70; };
    file "/usr/local/etc/namedb/dynamic/buetow.org";
};

zone "buetow.zone" {
    type slave;
    masters { 78.46.80.70; };
    file "/usr/local/etc/namedb/dynamic/buetow.zone";
};
</pre>
<h2>The end result</h2>
<p>The end result looks like this now:</p>
<pre>
% dig -t ns buetow.org
; &lt;&lt;&gt;&gt; DiG 9.10.3-P4-RedHat-9.10.3-12.P4.fc23 &lt;&lt;&gt;&gt; -t ns buetow.org
;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 37883
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;buetow.org.			IN	NS

;; ANSWER SECTION:
buetow.org.		600	IN	NS	dns2.buetow.org.
buetow.org.		600	IN	NS	dns1.buetow.org.

;; Query time: 41 msec
;; SERVER: 192.168.1.254#53(192.168.1.254)
;; WHEN: Sun May 22 11:34:11 BST 2016
;; MSG SIZE  rcvd: 77

% dig -t any buetow.org @dns1.buetow.org
; &lt;&lt;&gt;&gt; DiG 9.10.3-P4-RedHat-9.10.3-12.P4.fc23 &lt;&lt;&gt;&gt; -t any buetow.org @dns1.buetow.org
;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 49876
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 7

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;buetow.org.			IN	ANY

;; ANSWER SECTION:
buetow.org.		86400	IN	A	78.46.80.70
buetow.org.		86400	IN	AAAA	2a01:4f8:120:30e8::11
buetow.org.		86400	IN	MX	10 mail.ian.buetow.org.
buetow.org.		3600	IN	SOA	dns1.buetow.org. domains.buetow.org. 25 604800 86400 2419200 604800
buetow.org.		3600	IN	NS	dns2.buetow.org.
buetow.org.		3600	IN	NS	dns1.buetow.org.

;; ADDITIONAL SECTION:
mail.ian.buetow.org.	86400	IN	A	78.46.80.70
dns1.buetow.org.	86400	IN	A	78.46.80.70
dns2.buetow.org.	86400	IN	A	164.177.171.32
mail.ian.buetow.org.	86400	IN	AAAA	2a01:4f8:120:30e8::12
dns1.buetow.org.	86400	IN	AAAA	2a01:4f8:120:30e8::15
dns2.buetow.org.	86400	IN	AAAA	2a03:2500:1:6:20::

;; Query time: 42 msec
;; SERVER: 78.46.80.70#53(78.46.80.70)
;; WHEN: Sun May 22 11:34:41 BST 2016
;; MSG SIZE  rcvd: 322
</pre>
<h2>Monitoring</h2>
<p>For monitoring I am using Icinga2 (I am operating two Icinga2 instances in two different DCs). I may have to post another blog article about Icinga2 but to get the idea these were the snippets added to my Icinga2 configuration:</p>
<pre>
apply Service "dig" {
    import "generic-service"

    check_command = "dig"
    vars.dig_lookup = "buetow.org"
    vars.timeout = 30

    assign where host.name == "dns.ian.buetow.org" || host.name == "caprica.ian.buetow.org"
}

apply Service "dig6" {
    import "generic-service"

    check_command = "dig"
    vars.dig_lookup = "buetow.org"
    vars.timeout = 30
    vars.check_ipv6 = true

    assign where host.name == "dns.ian.buetow.org" || host.name == "caprica.ian.buetow.org"
}
</pre>
<h2>DNS update workflow</h2>
<p>Whenever I have to change a DNS entry all have to do is:</p>
<ul>
<li>Git clone or update the Puppet repository</li>
<li>Update/commit and push the zone file (e.g. "buetow.org")</li>
<li>Wait for Puppet. Puppet will deploy that updated zone file. And it will reload the BIND server.</li>
<li>The BIND server will notify all slave DNS servers (at the moment only one). And it will transfer the new version of the zone.</li>
</ul>
<p>That's much more comfortable now than manually clicking at some web UIs at Schlund Technologies.</p>
<p>E-Mail me your thoughts at comments@mx.buetow.org!</p>
<a class="textlink" href="../">Go back to the main site</a><br />
</body>
</html>