gpdm / DoH

A DNS-over-HTTP implementation
BSD 3-Clause "New" or "Revised" License
1 stars 1 forks source link
dns dns-over-https

DoH Server

This is a "DNS over HTTP" (DoH) server implementation written in Go.

The implementation follows RFC8484, and provides several key features:

What this DoH implementation is not:

Known Limitations:

Motivation

First, I wanted to understand, how exactly DoH works, and what pitfalls it brings. From this, the idea spawned to actually polish this daemon, so it could be easily "plugged" into any existing network infrastructure to run a local DoH service yourself.

Running the server

Running with Docker

It is the primarily intended mode of operations to run the DoH daemon trough Docker.

docker pull gpdm/doh[:<tag>]

tags:

then run it as follows:

docker run -d \
   [-p 8080:8080] \
   -p 8443:8443 \
   -v /path/to/doh/conf:/conf \
   -e [ENV-VARS]
   gpdm/doh[:<tag>]

Configuration can be done from both entvironment vars, or a config file, see also config section below. To use the config file, or to pass over TLS certificates, use a volume mount to /conf.

CRIY (Compile and Run It Yourself)

To compile the binary yourself, do this in the source directory:

go build

To run it, here's a short excerpt on the CLI args:

Usage of ./DoH:
  -configfile string
        config file (optional)
  -debug
        debug mode
  -verbose
        verbose mode

As you see, there's not too many options. A sample config file is provided beneath ./conf/DoH.toml.sample, I'll cover that further below.

Configuration Directives

This section covers available configuration directives. They can either be set from environment variables (useful for Docker), or from the configuration file.

global

[global]

# default listen address.
# set to "" to list to all addresses (default)
#
listen = ""

# default log level
#
# these are Syslog-compatible log levels
# Emergency = 0
# Alert = 1
# Crit = 2
# Error = 3
# Warn = 4
# Notice = 5        # default for DoH daemon -- not chatty at all
# Information = 6   # also controlled from cli using -verbose switch: add's some diagnostics information
# Debug = 7         # also controlled from cli using -debug switch: very chatty and fully verbose
#
loglevel = 5

To use from environment, specify like so:

docker run [..] -e GLOBAL.LISTEN="" GLOBAL.LOGLEVEL=7 [..]

dns

# DNS resolver
#
# at least one host must be specified in
# URI format, as described in https://tools.ietf.org/html/rfc3986
#
# multiple hosts can be specified as shown below,
# both in FQDN format or using IP(v4|6) addresses.
# 
# use udp:// for standard DNS resolvers
#   - port number can be specified using ':<port>' syntax, defaults to ':53'
# use https:// for DoH servers
#   - port number can be specified using ':<port>' syntax, defaults to ':443'
#   - DoH servers can support both POST or GET request methods,
#       append '#<request-method>' to indicate preferred method (defaults to '#POST')
#   - use the FQDN only, do not append '/dns-query' URI to hostname (read: it will be ignored)
#
#   [ "udp://192.0.2.1:53", "udp://fully-qualified-host.local", "https://cloudflare-dns.com#POST", "https://cloudflare-dns.com#GET" ]
#
[dns]
    resolvers = [ "udp://192.0.2.1:53", "udp://localhost" ]

To use from environment, specify like so:

docker run [..] -e DNS.RESOLVERS=$(udp://192.0.2.1,udp://localhost:53) [..]

tls

# settings for TLS HTTP/2 service (mandatory)
#
[tls]
  enable = true
  port = 443
  pkey = "./conf/private.key"
  cert = "./conf/public.crt"

To use from environment, specify like so:

docker run [..] -e TLS.ENABLE=1 -e TLS.PORT=443 -e TLS.PKEY=./conf/private.key -e TLS.CERT=./conf/public.crt [..]

http

# http-only server
# according to RFC8484, DoH must only be supported via TLS on HTTP/2
# However, for development purposes, the http-plain mode can be helpful,
# i.e. to capture wire format traffic.
# When running in Docker, it may be also indiciated to expose the service
# through plain-text HTTP, and run it behind a frontend load-balancer,
# which does the TLS offloading.
#
[http]
  enable = false
  port = 80

To use from environment, specify like so:

docker run [..] -e HTTP.ENABLE=true -e HTTP.PORT=80 [..]

influx

The DoH daemon has some support to send limited telemetry information to InfluxDB. The idea is not to be a data collector, but provide meaningful statistical information, such as request and query counters, and in the future maybe also query runtime. If you want to receive telemetry, you'll have to enable it accordingly. By default, no telemetry information is sent.

Here's an example of how this looks like:

Sample Influx Statistics

# Optional influxDB to report telemetry information
#
# Telemetry logging only includes counters for HTTP GET / POST requests,
# and the number of DNS RR Type requests (e.g. TYPE A, TYPE NS) processed.
# No additional information, e.g. queried hostnames, returned IP addresses,
# source IPs, etc, is included in the telemetry.
#
[influx]
  enable = false
  url = ""
  database = ""
  username = ""
  password = ""

To use from environment, specify like so:

docker run [..] -e INFLUX.ENABLE=true -e INFLUX.URL=... -e INFLUX.USERNAME=... INFLUX.PASSWORD=... [..]

redis

The DoH daemon has support to use Redis as an application-level cache.

The cache serves the same purposes as any regular DNS query cache: To store response data, and return from cache, until the DNS TTL has expired, saving time on extra recursion round-trips, and thus potentially also reduce load.

# Optional Redis cache support to perform application-level caching of DNS responses
# This works side-by-side with any ordinary DNS query cache, but on the DoH frontend service,
# saving extra round-trips and recursion through the DNS backends.
#
[redis]
    enable = false
    addr = "localhost"
    port = "6379"
    password = ""

To use from environment, specify like so:

docker run [..] -e REDIS.ENABLE=true -e REDIS.ADDR=... -e REDIS.PORT=... REDIS.PASSWORD=... [..]

Client Configuration

To use your own DoH instance, the client must be configured accordingly.

Configuring TRR in Firefox

Trusted Recursive Resolvers are configured in Firefox from about:config.

Search for any trr related config properties. The ones to change are these:

curl

Using DoH with curl, starting from v7.62, as simple as this:

curl --doh-url https://<fully-qualified-domain-name>/dns-query [any-url-you-want-to-access]

A Personal Opinion on DoH

To make it absolutely clear: I endorse the argument of added privacy enforced by using DoH over traditional, unencrypted DNS transport. However, I also do have my strongs concerns about certain things.

If you don't care, simply skip this section on my personal opinion ;-)

Browsers support DoH using Centralized Providers

Both Firefox and Chrome gained DoH support and are ready to send DNS queries over to either CloudFlare or Google. Throwing the queries over to centralized facilities goes against the principles and building foundations of the Internet, which had a decentral setup in mind.

And what serves the purpose of encrypted transmission, if data is collected at large scale with two huge global players?

It's easy to do data analysis by just looking at the logs, profiles could be built by just looking at the source IPs and correlating with the DNS questions.

Yes, practically everybody running a DNS server can do this. And yes, even my implementation provides a debugging mode, which could be abused for doing such nasty things.

The point is: DoH should run locally, and be connected to your own local DNS recursive resolver (not to mistake this with a forwarding-only resolver).

DoH in Browsers bypass the local DNS resolver

DoH in the current form is an overlay transport mechanism, which is implemented in the Browsers, and bypasses your locally configured DNS resolvers of the Operating System.

This not only that the queries pass-by your local resolvers, it also takes away some control from the local network administrator.

Depending on the settings (.e.g. Firefox, see TRR, the browser can be taught to either ignore the local resolver entirely, or only take it into account after the DoH recursion.

Going for privacy, this is great, as it bypasses any locally enforced DNS policy (.e.g to blocking of unwanted websites) at once. At the same time, this is bad, because it bypasses any locally enforced DNS Policy at once.

I consider this sort of a double-edged sword.

Freedom of Information Availability vs. the Law

In some countries, any institution may be obliged by law to enforce certain access and content to be blocked. Sometimes, an institution (let's say, a school) may even willingly decide to enforce certain blocking policies, say for ethical reasons.

Some people consider this "censorship". I am against censorship, and everybody is free to ask my about my opinion on Switzerlands law to block out foreign online casions. Worst. Thing. Ever.

However: There is unarguably some content on the web, which is definitely not suitable to pupils of a certain age. So a school IMO must have the authority to enforce certain blocking rules, DoH takes this away entirely.

In addition: As long as the gorvernors of any organization can be potentially held liable for not blocking certain content, DoH is simply not the way to go.

Compatibility and Other Issues

What I Like about DoH

License

This implementation is licensed under the terms of the BSD 3-Clause License.

More Documentation

TO DO

See also TODO.md

Acknowledgements

Thanks to @hoempf for some helpful hints, code reviews, testing assistance, and the Dockerfile template.