From 2f912bf0a3b0142b6e514faea00d703a3f7aeaeb Mon Sep 17 00:00:00 2001 From: Joachim Fasting Date: Mon, 24 Oct 2016 00:34:03 +0200 Subject: [PATCH 1/2] dnscrypt-proxy service: auto-update upstream resolver list By default, we use the list of public DNSCrypt resolvers provided by dnscrypt-proxy upstream. The list is updated at regular intervals. --- .../services/networking/dnscrypt-proxy.nix | 122 ++++++++++++++---- 1 file changed, 95 insertions(+), 27 deletions(-) diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix index 5a24db8ccba0..82bf178f4cb7 100644 --- a/nixos/modules/services/networking/dnscrypt-proxy.nix +++ b/nixos/modules/services/networking/dnscrypt-proxy.nix @@ -5,15 +5,25 @@ let apparmorEnabled = config.security.apparmor.enable; dnscrypt-proxy = pkgs.dnscrypt-proxy; cfg = config.services.dnscrypt-proxy; + stateDirectory = "/var/lib/dnscrypt-proxy"; localAddress = "${cfg.localAddress}:${toString cfg.localPort}"; - daemonArgs = - [ "--local-address=${localAddress}" - (optionalString cfg.tcpOnly "--tcp-only") - (optionalString cfg.ephemeralKeys "-E") - ] - ++ resolverArgs; + # The minisign public key used to sign the upstream resolver list. + # This is somewhat more flexible than preloading the key as an + # embedded string. + upstreamResolverListPubKey = pkgs.fetchurl { + url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/minisign.pub; + sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh"; + }; + + # Internal flag indicating whether the upstream resolver list is used + useUpstreamResolverList = cfg.resolverList == null && cfg.customResolver == null; + + resolverList = + if (cfg.resolverList != null) + then cfg.resolverList + else "${stateDirectory}/dnscrypt-resolvers.csv"; resolverArgs = if (cfg.customResolver != null) then @@ -22,9 +32,16 @@ let "--provider-key=${cfg.customResolver.key}" ] else - [ "--resolvers-list=${cfg.resolverList}" - "--resolver-name=${toString cfg.resolverName}" + [ "--resolvers-list=${resolverList}" + "--resolver-name=${cfg.resolverName}" ]; + + # The final command line arguments passed to the daemon + daemonArgs = + [ "--local-address=${localAddress}" ] + ++ optional cfg.tcpOnly "--tcp-only" + ++ optional cfg.ephemeralKeys "-E" + ++ resolverArgs; in { @@ -66,24 +83,20 @@ in default = "dnscrypt.eu-nl"; type = types.nullOr types.str; description = '' - The name of the upstream DNSCrypt resolver to use, taken from the - list named in the resolverList option. - The default resolver is located in Holland, supports DNS security - extensions, and claims to not keep logs. + The name of the upstream DNSCrypt resolver to use, taken from + ${resolverList}. The default resolver is + located in Holland, supports DNS security extensions, and + claims to not keep logs. ''; }; resolverList = mkOption { + default = null; + type = types.nullOr types.path; description = '' - The list of upstream DNSCrypt resolvers. By default, we use the most - recent list published by upstream. + List of DNSCrypt resolvers. The default is to use the list of + public resolvers provided by upstream. ''; - example = literalExample "${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv"; - default = pkgs.fetchurl { - url = https://raw.githubusercontent.com/jedisct1/dnscrypt-proxy/master/dnscrypt-resolvers.csv; - sha256 = "1i9wzw4zl052h5nyp28bwl8d66cgj0awvjhw5wgwz0warkjl1g8g"; - }; - defaultText = "pkgs.fetchurl { url = ...; sha256 = ...; }"; }; customResolver = mkOption { @@ -150,7 +163,7 @@ in } ]; - security.apparmor.profiles = mkIf apparmorEnabled (singleton (pkgs.writeText "apparmor-dnscrypt-proxy" '' + security.apparmor.profiles = optional apparmorEnabled (pkgs.writeText "apparmor-dnscrypt-proxy" '' ${dnscrypt-proxy}/bin/dnscrypt-proxy { /dev/null rw, /dev/urandom r, @@ -177,9 +190,9 @@ in ${getLib pkgs.lz4}/lib/liblz4.so.* mr, ${getLib pkgs.attr}/lib/libattr.so.* mr, - ${cfg.resolverList} r, + ${resolverList} r, } - '')); + ''); users.users.dnscrypt-proxy = { description = "dnscrypt-proxy daemon user"; @@ -188,11 +201,61 @@ in }; users.groups.dnscrypt-proxy = {}; + systemd.services.init-dnscrypt-proxy-statedir = optionalAttrs useUpstreamResolverList { + description = "Initialize dnscrypt-proxy state directory"; + script = '' + mkdir -pv ${stateDirectory} + chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory} + cp --preserve=timestamps -uv \ + ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \ + ${stateDirectory} + ''; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + systemd.services.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList { + description = "Update list of DNSCrypt resolvers"; + + requires = [ "init-dnscrypt-proxy-statedir.service" ]; + after = [ "init-dnscrypt-proxy-statedir.service" ]; + + path = with pkgs; [ curl minisign ]; + script = '' + cd ${stateDirectory} + curl -fSsL -o dnscrypt-resolvers.csv.tmp \ + https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv + curl -fSsL -o dnscrypt-resolvers.csv.minisig.tmp \ + https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv.minisig + mv dnscrypt-resolvers.csv.minisig{.tmp,} + minisign -q -V -p ${upstreamResolverListPubKey} \ + -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig + mv dnscrypt-resolvers.csv{.tmp,} + ''; + + serviceConfig = { + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + ProtectSystem = true; + }; + }; + + systemd.timers.update-dnscrypt-resolvers = optionalAttrs useUpstreamResolverList { + timerConfig = { + OnBootSec = "5min"; + OnUnitActiveSec = "6h"; + }; + wantedBy = [ "timers.target" ]; + }; + systemd.sockets.dnscrypt-proxy = { description = "dnscrypt-proxy listening socket"; socketConfig = { - ListenStream = "${localAddress}"; - ListenDatagram = "${localAddress}"; + ListenStream = localAddress; + ListenDatagram = localAddress; }; wantedBy = [ "sockets.target" ]; }; @@ -200,8 +263,13 @@ in systemd.services.dnscrypt-proxy = { description = "dnscrypt-proxy daemon"; - after = [ "network.target" ] ++ optional apparmorEnabled "apparmor.service"; - requires = [ "dnscrypt-proxy.socket "] ++ optional apparmorEnabled "apparmor.service"; + after = [ "network.target" ] + ++ optional apparmorEnabled "apparmor.service" + ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service"; + + requires = [ "dnscrypt-proxy.socket "] + ++ optional apparmorEnabled "apparmor.service" + ++ optional useUpstreamResolverList "init-dnscrypt-proxy-statedir.service"; serviceConfig = { Type = "simple"; From 806e652e5194df934fadb167f8ba44a1a3065c1d Mon Sep 17 00:00:00 2001 From: Joachim Fasting Date: Sat, 5 Nov 2016 16:10:10 +0100 Subject: [PATCH 2/2] dnscrypt-proxy test: simplification --- nixos/tests/dnscrypt-proxy.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/nixos/tests/dnscrypt-proxy.nix b/nixos/tests/dnscrypt-proxy.nix index b686e9582a7d..26409949ec62 100644 --- a/nixos/tests/dnscrypt-proxy.nix +++ b/nixos/tests/dnscrypt-proxy.nix @@ -22,8 +22,6 @@ import ./make-test.nix ({ pkgs, ... }: { }; testScript = '' - $client->start; - $client->waitForUnit("sockets.target"); $client->waitForUnit("dnsmasq"); # The daemon is socket activated; sending a single ping should activate it.