Commit 52f05532 authored by Joachim Fasting's avatar Joachim Fasting
Browse files

Add dnscrypt-proxy service

The dnscrypt-proxy service relays regular DNS queries to
a DNSCrypt enabled upstream resolver.
The traffic between the client and the upstream resolver is
encrypted and authenticated, which may mitigate the risk of
MITM attacks and third-party snooping (assuming a trustworthy
upstream).

Though dnscrypt-proxy can run as a standalone DNS client,
the recommended setup is to use it as a forwarder for a
caching DNS client.
To use dnscrypt-proxy as a forwarder for dnsmasq, do

```nix
{
  # ...

  networking.nameservers = [ "127.0.0.1" ];
  networking.dhcpcd.extraConfig = "nohook resolv.conf";

  services.dnscrypt-proxy.enable = true;
  services.dnscrypt-proxy.localAddress = "127.0.0.1";
  services.dnscrypt-proxy.port = 40;

  services.dnsmasq.enable = true;
  services.dnsmasq.extraConfig = ''
    no-resolv
    server=127.0.0.1#40
    listen-address=127.0.0.1
  '';

  # ...
}
```
parent 216ed768
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -158,6 +158,7 @@
      seeks = 148;
      prosody = 149;
      i2pd = 150;
      dnscrypt-proxy = 151;

      # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!

+1 −0
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@
  ./services/networking/ddclient.nix
  ./services/networking/dhcpcd.nix
  ./services/networking/dhcpd.nix
  ./services/networking/dnscrypt-proxy.nix
  ./services/networking/dnsmasq.nix
  ./services/networking/ejabberd.nix
  ./services/networking/firewall.nix
+133 −0
Original line number Diff line number Diff line
{ config, lib, pkgs, ... }:
with lib;

let
  apparmorEnabled = config.security.apparmor.enable;
  dnscrypt-proxy = pkgs.dnscrypt-proxy;
  cfg = config.services.dnscrypt-proxy;
  uid = config.ids.uids.dnscrypt-proxy;
  daemonArgs = [ "--daemonize"
                 "--user=dnscrypt-proxy"
                 "--local-address=${cfg.localAddress}:${toString cfg.port}"
                 (optionalString cfg.tcpOnly "--tcp-only")
                 "--resolvers-list=${dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv"
                 "--resolver-name=${cfg.resolverName}"
               ];
in

{
  ##### interface

  options = {

    services.dnscrypt-proxy = {

      enable = mkOption {
        default = false;
        type = types.bool;
        description = ''
          Enable dnscrypt-proxy.
          The proxy relays regular DNS queries to a DNSCrypt enabled
          upstream resolver.
          The traffic between the client and the upstream resolver is
          encrypted and authenticated, which may mitigate the risk of MITM
          attacks and third-party snooping (assuming the upstream is
          trustworthy).
        '';
      };

      localAddress = mkOption {
        default = "127.0.0.1";
        type = types.string;
        description = ''
          Listen for DNS queries on this address.
        '';
      };

      port = mkOption {
        default = 53;
        type = types.int;
        description = ''
          Listen on this port.
        '';
      };

      resolverName = mkOption {
        default = "opendns";
        type = types.string;
        description = ''
          The name of the upstream DNSCrypt resolver to use.
          See <literal>${dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv</literal>
          for alternative resolvers (e.g., if you are concerned about logging
          and/or server location).
        '';
      };

      tcpOnly = mkOption {
        default = false;
        type = types.bool;
        description = ''
          Force sending encrypted DNS queries to the upstream resolver
          over TCP instead of UDP (on port 443).
          Enabling this option may help circumvent filtering, but should
          not be used otherwise.
        '';
      };

    };

  };

  ##### implementation

  config = mkIf cfg.enable {

    ### AppArmor profile

    security.apparmor.profiles = mkIf apparmorEnabled [
      (pkgs.writeText "apparmor-dnscrypt-proxy" ''

        ${dnscrypt-proxy}/sbin/dnscrypt-proxy {
          capability ipc_lock,
          capability net_bind_service,
          capability net_admin,
          capability sys_chroot,
          capability setgid,
          capability setuid,

          /dev/null rw,
          /dev/urandom r,

          ${pkgs.glibc}/lib/*.so mr,
          ${pkgs.tzdata}/share/zoneinfo/** r,

          ${dnscrypt-proxy}/share/dnscrypt-proxy/** r,
          ${pkgs.gcc.gcc}/lib/libssp.so.* mr,
          ${pkgs.libsodium}/lib/libsodium.so.* mr,
        }
      '')
    ];

    ### User

    users.extraUsers = singleton {
      inherit uid;
      name = "dnscrypt-proxy";
      description = "dnscrypt-proxy daemon user";
    };

    ### Service definition

    systemd.services.dnscrypt-proxy = {
      description = "dnscrypt-proxy daemon";
      after = [ "network.target" ] ++ optional apparmorEnabled "apparmor.service";
      requires = mkIf apparmorEnabled [ "apparmor.service" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        Type = "forking";
        ExecStart = "${dnscrypt-proxy}/sbin/dnscrypt-proxy ${toString daemonArgs}";
      };
    };

  };
}