Encrypted DNS

From NixOS Wiki
Revision as of 20:31, 17 April 2020 by imported>Emily (new page documenting dnscrypt-proxy2 module)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

By default, DNS traffic is sent in plain text over the internet; it can be monitored or spoofed by any party along the path, including your ISP. DNSSEC authenticates the DNS records themselves, but can't stop your ISP monitoring domains or dropping queries.

Encrypted DNS protocols aim to address this hole by encrypting queries and responses in transit between DNS resolvers and clients; the most widely deployed ones are DNS over HTTPS (DoH), DNS over TLS (DoT), and DNSCrypt.

NixOS has modules for multiple encrypted DNS proxies, including dnscrypt-proxy 2 and Stubby. services.dnscrypt-proxy2 is generally recommended, as it has the widest protocol and feature support, and is written in a memory-safe language.

Setting nameservers

No matter what proxy you use, you should set your DNS nameservers statically and make sure that your network manager won't override your carefully set nameservers with some random settings it received over DHCP.

{
  networking = {
    nameservers = [ "::1" ];
    resolvconf.enable = false;
    # If using dhcpcd:
    dhcpcd.extraConfig = "nohook resolv.conf";
    # If using NetworkManager:
    networkmanager.dns = "none";
  }

  # Make sure you don't have services.resolved.enable on.
}

If you'd prefer to keep using resolvconf then you can set networking.resolvconf.useLocalResolver instead. Note that it uses the IPv4 loopback address only.

dnscrypt-proxy2

Example configuration

{
  services.dnscrypt-proxy2 = {
    enable = true;
    settings = {
      ipv6_servers = true;
      require_dnssec = true;

      sources.public-resolvers = {
        urls = [
          "https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md"
          "https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md"
        ];
        cache_file = "/var/lib/dnscrypt-proxy2/public-resolvers.md";
        minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3";
      };

      # You can choose a specific set of servers from https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v2/public-resolvers.md
      # server_names = [ ... ];
    };
  };

  systemd.services.dnscrypt-proxy2.serviceConfig = {
    StateDirectory = "dnscrypt-proxy2";
  };
}

See the upstream example configuration file for more configuration options.

Using alongside another DNS server

DNS authoritative nameservers are tied to port 53, and the Linux /etc/resolv.conf doesn't allow specifying a different port for resolvers either. This leads to conflicts if you have another DNS server you need to expose externally on port 53 (e.g. an authoritative DNS server for your domains, or acme-dns), and can't easily run it on a separate IP to dnscrypt-proxy2 (e.g. your authoritative DNS server listens on ::/0.0.0.0). You can resolve this by running the proxy on a different port and forwarding loopback traffic on port 53 to it:

{
  networking.nameservers = [ "::1" ];

  services.dnscrypt-proxy2 = {
    enable = true;
    settings = {
      listen_addresses = [ "[::1]:51" ];
      # ...
    };
  };

  # Forward loopback traffic on port 53 to dnscrypt-proxy2.
  networking.firewall.extraCommands = ''
    ip6tables --table nat --flush OUTPUT
    ${lib.flip (lib.concatMapStringsSep "\n") [ "udp" "tcp" ] (proto: ''
      ip6tables --table nat --append OUTPUT \
        --protocol ${proto} --destination ::1 --destination-port 53 \
        --jump REDIRECT --to-ports 51
    '')}
  '';
}

Note that you can still access the other DNS server locally through the non-loopback interface (e.g. by using your server's external IP).

Stubby

See the options documentation for services.stubby.*.