When CloudFlare took over 1.1.1.1 for their DNS service; this got me thinking about a couple of issues:

  • A. What do you do if you’ve been using 1.1.1.1 on your network, how do you unbreak the Internet?
  • B. How can you enable use of DNS over TLS for clients that don’t have native support?
  • C. How can you enable use of DNS over HTTPS for clients that don’t have native support?

A. Unbreaking the Internet

RFC1918 lays out “private” addresses that you should use for internal use, i.e. my home network is 192.168.1.0/24. But, we all have issues where sometimes we “forget” about the RFC and you are using routable IP space on your internal network. 1.1.1.1 is a good example of this, it’s just so nice looking and easy to type.

In my home network I created a contrived situation to simulate breaking the Internet; creating a network route on my home router that sends all traffic for 1.1.1.1/32 to a Linux host on my internal network. I imagined a situation where I wanted to continue to send HTTP traffic to my own server, but send DNS traffic to the “real” 1.1.1.1.


broken_internet_v2

In the picture above, I have broken the Internet. It is now impossible for me to route traffic to 1.1.1.1/32.  To “fix” the Internet (image below), I used a F5 BIG-IP to send HTTP traffic to my internal server and DNS traffic to 1.1.1.1.

split_traffic_v2


The BIG-IP configuration is straight-forward. 1.1.1.1:80 points to the local server and 1.1.1.1:53 points to 1.0.0.1.  Note that I have used 1.0.0.1 since I broke routing to 1.1.1.1 (1.0.0.1 works the same as 1.1.1.1).

network_map-split-traffic

B. DNS over TLS

After fixing the Internet, I started to think about the challenge of how to use DNS over TLS. RFC7858 is a proposed standard for wrapping DNS traffic in TLS. Many clients do not support DNS over TLS unless you install additional software. I started to wonder, is it possible to have a BIG-IP “upgrade” a standard DNS request to DNS over TLS? There are two issues.

  1. DNS over TLS uses TLS (encryption)
  2. DNS over TLS uses TCP (most clients default to UDP unless handling large DNS requests)

For the first issue, the BIG-IP can already wrap a TCP connection with TLS (often used in providing SSL visibility to security devices that cannot inspect SSL traffic, BIG-IP terminates SSL connection, passes traffic to security device, re-encrypts traffic to final destination).

The second issue can be solved with configuring BIG-IP DNS as a caching DNS resolver that accepts UDP and TCP DNS requests and only forwards TCP DNS requests. This results in an architecture that looks like the following.

dns_over_tls_v3

The virtual server is configured with a TCP profile and serverssl profile.

dns_over_tls_vs

The serverssl profile is configure to verify the authenticity of the server certificate.

dns_over_tls_serverssl

The DNS cache is configured to use the virtual server that performs the UDP to TCP over TLS connection.

dns_over_tls_dns_cache

A bonus of using the DNS cache is that subsequent DNS queries are extremely fast!

C. DNS over HTTPS

I felt pretty good after solving these two issues (A and B), but there was a third nagging issue (C) of how to support DNS over HTTPS. If you have ever looked at DNS traffic it is a binary format over UDP or TCP that is visible on the network. HTTPS traffic is very different in comparison; TCP traffic that is encrypted.

The draft RFC (draft-ietf-doh-dns-over-https-05) has a handy specification for “DNS Wire Format”. Take a DNS packet, shove it into an HTTPS message, get back a DNS packet in a HTTPS response. Hmm, I bet an iRule could solve this problem of converting DNS to HTTPS.

Using iRulesLX I created a process where a TCL iRule takes a UDP payload off the network from a DNS client, sends it to a Node.JS iRuleLX extension that invokes the HTTPS API, returns the payload to the TCL iRule that sends the result back to the DNS client on the network.

The solution looks like the following.

dns_over_https_v2

This works on my home network, but I did notice occasional 405 errors from the HTTPS services while testing.

The TCL iRule is simple, grab a UDP payload.

when CLIENT_DATA {

  set payload [UDP::payload]
  set encoded_payload [b64encode $payload]

  set RPC_HANDLE [ILX::init dns_over_https_plugin dns_over_https]
  set rpc_response [ILX::call $RPC_HANDLE query_dns $encoded_payload]
  UDP::respond [b64decode $rpc_response]

}

The Node.JS iRuleLX Extension makes the API call to the HTTPS service.

...
ilx.addMethod('query_dns', function(req, res) {
   var dns_query = atob(req.params()[0]);
var options = {
  host: '192.168.1.254',
  port: 80,
  path: '/dns-query?ct=application/dns-udpwireformat&dns=' + req.params()[0],
  method: 'GET',
  headers: {
   'Host':'cloudflare-dns.com'  
  }
};
var cfreq = http.request(options, function(cfres) {
  var output = "";
  cfres.setEncoding('binary');
  cfres.on('data', function (chunk) {
    output += chunk;
  });
  cfres.on('end', () => {  
   res.reply(btoa(output));
  });
});
cfreq.on('error', function(e) {
  console.log('problem with request: ' + e.message);
});
cfreq.write(dns_query);
cfreq.end(); 
});
...

After deploying the iRule; you can update your DNS cache to point to the Virtual Server hosting the iRule.

Note that the Node.JS code connects back to another Virtual Server on the BIG-IP that does a similar upgrade from HTTP to HTTPS and uses a F5 OneConnect profile.  You could also have it connect directly to the HTTPS server and recreate a new connection on each call.

All done?

Before I started I wasn’t sure whether I could unbreak the Internet or upgrade old DNS protocols into more secure methods, but F5 BIG-IP made it possible. Until the next time that the Internet breaks.