Home | History | Annotate | Download | only in mdnsresponder
      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
     79 of client machines, the server has the option of dropping the TLS
     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 	};
    276