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