1Private DNS
2
3Summary
4
5Private DNS is an extension to standard Wide Area Bonjour that allows
6for secure, encrypted, and authorized communications. Private data sent
7from a client to a DNS server is encrypted using Transport Layer
8Security (TLS), ensuring that the data is hidden from prying eyes, and
9contains Transaction Signatures (TSIG), so the server can authorize the
10request. TSIGs are typically associated with Dynamic Updates; we are
11using them for standard and long-lived queries as well. Private DNS also
12protects Dynamic Updates from eavesdropping, by wrapping the update in a
13TLS communication channel if the server has been configured appropriately.
14
15Architectural Overview
16
17mDNSResponder has been modified to automatically issue a private query
18when necessary. After receiving an NXDOMAIN error, mDNSResponder checks
19in the system keychain to see if the user has a DNS query key (TSIG key)
20for the name in question, or for a parent of that name. If a suitable
21key is found, mDNSResponder looks up the zone data associated with the
22name of the question. After determining the correct name server,
23mDNSResponder looks up an additional SRV record "_dns-private._tcp". If
24it finds this record, mDNSResponder will re-issue the query privately.
25If either there is no _dns-private._tcp record, or there is no secret
26key, the call fails as it initially did, with an NXDOMAIN error.
27
28Once the secret key is found and the SRV record is looked up, mDNSResponder
29opens a TLS connection to the server on the port specified in the SRV
30record just looked up. After the connection succeeds, mDNSResponder
31can proceed to use that communication channel to make requests of
32the server. Every private packet must also have a TSIG record;
33the DNS server uses this TSIG record to allow access to its data.
34
35When setting up a long-lived query over TCP (with or without TLS)
36TCP's standard three-way handshake makes the full four-packet LLQ setup
37exchange described in <http://files.dns-sd.org/draft-sekar-dns-llq.txt>
38unnecessary. Instead, when connecting over TCP, the client simply sends
39a setup message and expects to receive ACK + Answers. The setup message
40sent is formatted as described in the LLQ document, however there is
41an additional TSIG' resource record added to the end of it. The TSIG
42resource records looks and acts exactly as it does in a secure update.
43So when the server receives an LLQ (or a standard query), it looks to
44see if the zone that is being referenced is public or private. If it's
45private, then it makes sure that the client is authorized to query that
46zone (by using the TSIG signature) and returns the appropriate data.
47When a zone is configured as private, the server will do this type of
48authorization checking for every query except those queries that are
49looking for SOA and NS records.
50
51Implementation Issues
52
53dnsextd
54
55dnsextd has been modified to behave much like a DNS firewall. The "real"
56DNS server is configured to listen on non-standard ports on the loopback
57interface. dnsextd then listens on the standard DNS ports (TCP/UDP port
5853) and intercepts all DNS traffic. It is responsible for determining
59what zone a DNS request is associated with, determining whether the
60client is allowed access to that zone, and returning the appropriate
61information back to the caller. If the packet is allowed access, dnsextd
62forwards the request to the "real" nameserver, and returns the result to
63the caller.
64
65It was tempting to use BIND9's facility for configuring TSIG enabled
66queries while doing this work. However after proceeding down that path,
67enough subtle interaction problems were found that it was not practical
68to pursue this direction, so instead dnsextd does all TSIG processing
69for queries itself. It does continue to use BIND9 for processing TSIG
70enabled dynamic updates, though one minor downside with this is that
71there are two configuration files (named.conf or dnsextd.conf) that have
72the same secret key information. That seems redundant and error-prone,
73and moving all TSIG processing for both queries and updates into dnsextd
74would fix this.
75
76All private LLQ operations are TSIG-enabled and sent over a secure
77encrypted TLS channel. To accommodate service providers who don't want
78to have to keep open a large number of TLS connections to a large number
79of client machines, the server has the option of dropping the TLS
80connection after initial LLQ setup and sending subsequent events and
81refreshes using unencrypted UDP packets. This results in less load on
82the server, at the cost of slightly lower security (LLQs can only be set
83up by an authorized client, but once set up, subsequent change event
84packets sent over unencrypted UDP could be observed by an eavesdropper).
85A potential solution to this deficiency might be in using DTLS, which is
86a protocol based on TLS that is capable of securing datagram traffic.
87More investigation needs to be done to see if DTLS is suitable for
88private DNS.
89
90It was necessary to relax one of the checks that dnsextd performs during
91processing of an LLQ refresh. Prior to these changes, dnsextd would
92verify that the refresh request came from the same entity that setup the
93LLQ by comparing both the IP Address and port number of the request
94packet with the IP Address and port number of the setup packet. Because
95of the preceding issue, a refresh request might be sent over two
96different sockets. While their IP addresses would be the same, their
97port numbers could potentially differ. This check has been modified to
98only check that the IP addresses match.
99
100When setting up a semi-private LLQ (where the request and initial answer
101set is sent over TLS/TCP, but subsequent change events are sent over
102unencrypted UDP), dnsextd uses the port number of the client's TCP
103socket to determine the UDP event port number. While this eliminates the
104need to pass the UDP event port number in the LLQ setup request
105(obviating a potential data mismatch error), I think it does more harm
106than good, for three reasons:
107
1081) We are relying that all the routers out there implement the Port
109   Mapping Protocol spec correctly.
110
1112) Upon setup every LLQ must NAT map two ports. Upon tear down every LLQ
112   must tear down two NAT mappings.
113
1143) Every LLQ opens up two sockets (TCP and UDP), rather than just the
115   one TCP socket.
116
117All of this just to avoid sending two bytes in the LLQ setup packet
118doesn't seem logical. The approach also necessitates creating an
119additional UDP socket for every private LLQ, port mapping both the TCP
120socket as well as the UDP socket, and moderately increasing the
121complexity and efficiency of the code. Because of this we plan to allow
122the LLQ setup packet to specify a different UDP port for change event
123packets. This will allow mDNSResponder to receive all UDP change event
124packets on a single UDP port, instead of a different one for each LLQ.
125
126Currently, dnsextd is buggy on multi-homed hosts. If it receives a
127packet on interface 2, it will reply on interface 1 causing an error in
128the client program.
129
130dnsextd doesn't fully process all of its option parameters.
131Specifically, it doesn't process the keywords: "listen-on",
132"nameserver", "private", and "llq". It defaults to expecting the "real"
133nameserver to be listening on 127.0.0.1:5030.
134
135
136mDNSResponder
137
138Currently, mDNSResponder attempts to issue private queries for all
139queries that initially result in an NXDOMAIN error. This behavior might
140be modified in future versions, however it seems patently incorrect to
141do this for reverse name lookups. The code that attempts to get the zone
142data associated with the name will never find the zone for a reverse
143name lookup, and so will issue a number of wasteful DNS queries.
144
145mDNSResponder doesn't handle SERV_FULL or STATIC return codes after
146setting up an LLQ over TCP. This isn't a terrible problem right now,
147because dnsextd doesn't ever return them, but this should be fixed so
148that mDNSResponder will work when talking to other servers that do
149return these error codes.
150
151
152Configuration:
153
154Sample named.conf:
155
156//
157// Include keys file
158//
159include "/etc/rndc.key";
160// Declares control channels to be used by the rndc utility.
161//
162// It is recommended that 127.0.0.1 be the only address used.
163// This also allows non-privileged users on the local host to manage
164// your name server.
165
166//
167// Default controls
168//
169controls
170	{
171	inet 127.0.0.1 port 54 allow { any; } keys { "rndc-key"; };
172	};
173
174options
175	{
176	directory "/var/named";
177	/*
178	 * If there is a firewall between you and nameservers you want
179	 * to talk to, you might need to uncomment the query-source
180	 * directive below. Previous versions of BIND always asked
181	 * questions using port 53, but BIND 8.1 uses an unprivileged
182	 * port by default.
183	 */
184
185	forwarders
186			{
187			65.23.128.2;
188			65.23.128.3;
189			};
190
191	listen-on port 5030 { 127.0.0.1; };
192	recursion true;
193	};
194
195//
196// a caching only nameserver config
197//
198zone "." IN
199	{
200	type hint;
201	file "named.ca";
202	};
203
204zone "localhost" IN
205	{
206	type master;
207	file "localhost.zone";
208	allow-update { none; };
209	};
210
211zone "0.0.127.in-addr.arpa" IN
212	{
213	type master;
214	file "named.local";
215	allow-update { none; };
216	};
217
218zone "hungrywolf.org." in
219	{
220	type master;
221	file "db.hungrywolf.org";
222	allow-update { key hungrywolf.org.; };
223	};
224
225zone "157.23.65.in-addr.arpa" IN
226	{
227	file "db.65.23.157";
228	type master;
229	};
230
231zone "100.255.17.in-addr.arpa" IN
232	{
233	file "db.17.255.100";
234	type master;
235	};
236
237zone "66.6.24.in-addr.arpa" IN
238	{
239	file "db.24.6.66";
240	type master;
241	};
242
243key hungrywolf.org.
244	{
245	algorithm hmac-md5;
246	secret "c8LWr16K6ju6KMO5zT6Tyg==";
247	};
248
249logging
250	{
251	category default { _default_log; };
252
253	channel _default_log
254		{
255		file "/Library/Logs/named.log";
256		severity info;
257		print-time yes;
258		};
259	};
260
261
262Sample dnsextd.conf:
263
264options { };
265
266key "hungrywolf.org."
267	{
268	secret "c8LWr16K6ju6KMO5zT6Tyg==";
269	};
270
271zone "hungrywolf.org."
272	{
273	type private;
274	allow-query { key hungrywolf.org.; };
275	};
276