January 25, 2018

Google DNS-over-HTTPS using LEDE / OpenWRT and https_dns_proxy. DNSBench Benchmark and impressions

Just to give you a quick introduction. Currently all your DNS queries are pure UDP and go unsecured through the internet if you're not using a VPN. Your ISP or anybody else who is able to act as a man-in-the-middle like public hotspots could modify these DNS queries and redirect you. There are different solutions available which are not heavily used yet but this will probably change in the future. Google just purposed a standard which they call DNS-over-HTTPS and it simply returns JSON over a HTTPS connection. All you need on your end is a certificate package to make sure that it's actually Google and not somebody else listening. This is preinstalled on every PC but not on LEDE / OpenWRT. https_dns_proxy, which is the software I am using to connect to the Google service, hardcoded 8.8.8.8 as HTTPS source so you don't need an initial DNS resolve which could be modified. As it doesn't offer something like caching and is probably not meant to be a "full" DNS server, it should be put as a backend of something like DNSMasq which is the default DNS server on LEDE / OpenWRT. Now that you know the basics, let's go ahead.

I fiddled around with https_dns_proxy already a couple of weeks ago but it didn't work because of a bug in libcurl. Yesterday finally they patched it in the current LEDE build so it won't simply fail with an empty payload. I was a bit worried about the actual performance as it will connect through TCP and setup a HTTPS connection instead of a simple UDP request. A few words about my router: It is a Pentium G4560 running VMWare ESXi and LEDE 17.01.4 in a VM using the VMXnet3 virtual adapter and a PCI passthrough of the second gigabit Intel NIC for the PPPoE connection. Downloading with about 100 Mbit barely reaches 2-3% CPU utilization so this shouldn't be a bottleneck at all.

So I used Steve Gibson's DNSBench to check performance with just using 8.8.8.8 as DNS Server on my LEDE instance.

These are the benchmark results of the first run without DNS-over-HTTPS. Option noresolv is set to 1 to make sure it won't use any other DNS and I also added the option cachesize '2500' as usually you want to cache as many requests as possible.


  192.168.  1.  5 |  Min  |  Avg  |  Max  |Std.Dev|Reliab%|
  ----------------+-------+-------+-------+-------+-------+
  + Cached Name   | 0,000 | 0,000 | 0,000 | 0,000 | 100,0 |
  + Uncached Name | 0,021 | 0,073 | 0,314 | 0,078 | 100,0 |
  + DotCom Lookup | 0,026 | 0,029 | 0,033 | 0,001 | 100,0 |
  ------+-------+-------+-------+-------+-------+
                       gateway.lan
                Local Network Nameserver

For comparison, this is the result for the Google DNS server I am using as source DNS:


  8.  8.  8.  8   |  Min  |  Avg  |  Max  |Std.Dev|Reliab%|
  ----------------+-------+-------+-------+-------+-------+
  - Cached Name   | 0,012 | 0,013 | 0,013 | 0,000 | 100,0 |
  - Uncached Name | 0,022 | 0,067 | 0,332 | 0,075 | 100,0 |
  - DotCom Lookup | 0,027 | 0,029 | 0,031 | 0,001 | 100,0 |
  ---<-------->---+-------+-------+-------+-------+-------+
             google-public-dns-a.google.com
                 GOOGLE - Google LLC, US

As expected, there is not much of a difference. Now comes the fun part: We edit the DNSmasq configuration to use 127.0.0.1#5053 (where https_dns_proxy is listening) and run the benchmark again. And I expected something completely different:


  192.168.  1.  5 |  Min  |  Avg  |  Max  |Std.Dev|Reliab%|
  ----------------+-------+-------+-------+-------+-------+
  + Cached Name   | 0,000 | 0,000 | 0,000 | 0,000 | 100,0 |
  + Uncached Name | 0,022 | 0,068 | 0,291 | 0,064 | 100,0 |
  + DotCom Lookup | 0,027 | 0,030 | 0,059 | 0,005 | 100,0 |
  ------+-------+-------+-------+-------+-------+
                       gateway.lan
                Local Network Nameserver

Nothing changed.
How could that be? I mean, I am using TCP and a HTTPS connection, it should be slower? To make sure I don't accidently still use the 8.8.8.8 DNS I shutdown https_dns_proxy and tried to resolve something. Didn't work. I started https_dns_proxy manually using -vvv to enable verbose mode and checked that it will query the hostnames I try to resolve on my PC. And it did. All seems to be okay. So either DNSBench is not reliable or the difference is simply not measurable because of HTTPS keep-alive or something else, I don't know. I even disabled the cachesize option of DNSMasq and restarted it to make sure that things don't go wrong on that end. But results are still identical. Well, I like what I see. Next step will be to try if this also works on real embedded routers without issues so I can make sure when not at home that nobody messes around with my DNS without having to use a VPN every time.