Merge pull request #33629 from rnhmjoj/dnscrypt-proxy
Restore dnscrypt-proxy
This commit is contained in:
commit
a6912f589e
@ -139,12 +139,6 @@ following incompatible changes:</para>
|
||||
will be accessible at <literal>/run/memcached/memcached.sock</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The DNSCrypt proxy module has been removed, the upstream project
|
||||
is no longer maintained.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
@ -446,6 +446,7 @@
|
||||
./services/networking/dhcpd.nix
|
||||
./services/networking/dnscache.nix
|
||||
./services/networking/dnschain.nix
|
||||
./services/networking/dnscrypt-proxy.nix
|
||||
./services/networking/dnscrypt-wrapper.nix
|
||||
./services/networking/dnsmasq.nix
|
||||
./services/networking/ejabberd.nix
|
||||
|
@ -89,9 +89,6 @@ with lib;
|
||||
# Tarsnap
|
||||
(mkRenamedOptionModule [ "services" "tarsnap" "config" ] [ "services" "tarsnap" "archives" ])
|
||||
|
||||
# dnscrypt-proxy
|
||||
(mkRemovedOptionModule [ "services" "dnscrypt-proxy" "enable" ] "")
|
||||
|
||||
# ibus
|
||||
(mkRenamedOptionModule [ "programs" "ibus" "plugins" ] [ "i18n" "inputMethod" "ibus" "engines" ])
|
||||
|
||||
|
321
nixos/modules/services/networking/dnscrypt-proxy.nix
Normal file
321
nixos/modules/services/networking/dnscrypt-proxy.nix
Normal file
@ -0,0 +1,321 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.dnscrypt-proxy;
|
||||
|
||||
stateDirectory = "/var/lib/dnscrypt-proxy";
|
||||
|
||||
# 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/dyne/dnscrypt-proxy/master/minisign.pub;
|
||||
sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh";
|
||||
};
|
||||
|
||||
# Internal flag indicating whether the upstream resolver list is used.
|
||||
useUpstreamResolverList = cfg.customResolver == null;
|
||||
|
||||
# The final local address.
|
||||
localAddress = "${cfg.localAddress}:${toString cfg.localPort}";
|
||||
|
||||
# The final resolvers list path.
|
||||
resolverList = "${stateDirectory}/dnscrypt-resolvers.csv";
|
||||
|
||||
# Build daemon command line
|
||||
|
||||
resolverArgs =
|
||||
if (cfg.customResolver == null)
|
||||
then
|
||||
[ "-L ${resolverList}"
|
||||
"-R ${cfg.resolverName}"
|
||||
]
|
||||
else with cfg.customResolver;
|
||||
[ "-N ${name}"
|
||||
"-k ${key}"
|
||||
"-r ${address}:${toString port}"
|
||||
];
|
||||
|
||||
daemonArgs =
|
||||
[ "-a ${localAddress}" ]
|
||||
++ resolverArgs
|
||||
++ cfg.extraArgs;
|
||||
in
|
||||
|
||||
{
|
||||
meta = {
|
||||
maintainers = with maintainers; [ joachifm ];
|
||||
doc = ./dnscrypt-proxy.xml;
|
||||
};
|
||||
|
||||
options = {
|
||||
# Before adding another option, consider whether it could
|
||||
# equally well be passed via extraArgs.
|
||||
|
||||
services.dnscrypt-proxy = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = "Whether to enable the DNSCrypt client proxy";
|
||||
};
|
||||
|
||||
localAddress = mkOption {
|
||||
default = "127.0.0.1";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Listen for DNS queries to relay on this address. The only reason to
|
||||
change this from its default value is to proxy queries on behalf
|
||||
of other machines (typically on the local network).
|
||||
'';
|
||||
};
|
||||
|
||||
localPort = mkOption {
|
||||
default = 53;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Listen for DNS queries to relay on this port. The default value
|
||||
assumes that the DNSCrypt proxy should relay DNS queries directly.
|
||||
When running as a forwarder for another DNS client, set this option
|
||||
to a different value; otherwise leave the default.
|
||||
'';
|
||||
};
|
||||
|
||||
resolverName = mkOption {
|
||||
default = "random";
|
||||
example = "dnscrypt.eu-nl";
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
The name of the DNSCrypt resolver to use, taken from
|
||||
<filename>${resolverList}</filename>. The default is to
|
||||
pick a random non-logging resolver that supports DNSSEC.
|
||||
'';
|
||||
};
|
||||
|
||||
customResolver = mkOption {
|
||||
default = null;
|
||||
description = ''
|
||||
Use an unlisted resolver (e.g., a private DNSCrypt provider). For
|
||||
advanced users only. If specified, this option takes precedence.
|
||||
'';
|
||||
type = types.nullOr (types.submodule ({ ... }: { options = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
description = "IP address";
|
||||
example = "208.67.220.220";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
description = "Port";
|
||||
default = 443;
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Fully qualified domain name";
|
||||
example = "2.dnscrypt-cert.example.com";
|
||||
};
|
||||
|
||||
key = mkOption {
|
||||
type = types.str;
|
||||
description = "Public key";
|
||||
example = "B735:1140:206F:225D:3E2B:D822:D7FD:691E:A1C3:3CC8:D666:8D0C:BE04:BFAB:CA43:FB79";
|
||||
};
|
||||
}; }));
|
||||
};
|
||||
|
||||
extraArgs = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
Additional command-line arguments passed verbatim to the daemon.
|
||||
See <citerefentry><refentrytitle>dnscrypt-proxy</refentrytitle>
|
||||
<manvolnum>8</manvolnum></citerefentry> for details.
|
||||
'';
|
||||
example = [ "-X libdcplugin_example_cache.so,--min-ttl=60" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (mkMerge [{
|
||||
assertions = [
|
||||
{ assertion = (cfg.customResolver != null) || (cfg.resolverName != null);
|
||||
message = "please configure upstream DNSCrypt resolver";
|
||||
}
|
||||
];
|
||||
|
||||
users.users.dnscrypt-proxy = {
|
||||
description = "dnscrypt-proxy daemon user";
|
||||
isSystemUser = true;
|
||||
group = "dnscrypt-proxy";
|
||||
};
|
||||
users.groups.dnscrypt-proxy = {};
|
||||
|
||||
systemd.sockets.dnscrypt-proxy = {
|
||||
description = "dnscrypt-proxy listening socket";
|
||||
documentation = [ "man:dnscrypt-proxy(8)" ];
|
||||
|
||||
wantedBy = [ "sockets.target" ];
|
||||
|
||||
socketConfig = {
|
||||
ListenStream = localAddress;
|
||||
ListenDatagram = localAddress;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.dnscrypt-proxy = {
|
||||
description = "dnscrypt-proxy daemon";
|
||||
documentation = [ "man:dnscrypt-proxy(8)" ];
|
||||
|
||||
before = [ "nss-lookup.target" ];
|
||||
after = [ "network.target" ];
|
||||
requires = [ "dnscrypt-proxy.socket "];
|
||||
|
||||
serviceConfig = {
|
||||
NonBlocking = "true";
|
||||
ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
|
||||
User = "dnscrypt-proxy";
|
||||
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
ProtectHome = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
(mkIf config.security.apparmor.enable {
|
||||
systemd.services.dnscrypt-proxy.after = [ "apparmor.service" ];
|
||||
|
||||
security.apparmor.profiles = singleton (pkgs.writeText "apparmor-dnscrypt-proxy" ''
|
||||
${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy {
|
||||
/dev/null rw,
|
||||
/dev/urandom r,
|
||||
|
||||
/etc/passwd r,
|
||||
/etc/group r,
|
||||
${config.environment.etc."nsswitch.conf".source} r,
|
||||
|
||||
${getLib pkgs.glibc}/lib/*.so mr,
|
||||
${pkgs.tzdata}/share/zoneinfo/** r,
|
||||
|
||||
network inet stream,
|
||||
network inet6 stream,
|
||||
network inet dgram,
|
||||
network inet6 dgram,
|
||||
|
||||
${getLib pkgs.dnscrypt-proxy}/lib/dnscrypt-proxy/libdcplugin*.so mr,
|
||||
|
||||
${getLib pkgs.gcc.cc}/lib/libssp.so.* mr,
|
||||
${getLib pkgs.libsodium}/lib/libsodium.so.* mr,
|
||||
${getLib pkgs.systemd}/lib/libsystemd.so.* mr,
|
||||
${getLib pkgs.xz}/lib/liblzma.so.* mr,
|
||||
${getLib pkgs.libgcrypt}/lib/libgcrypt.so.* mr,
|
||||
${getLib pkgs.libgpgerror}/lib/libgpg-error.so.* mr,
|
||||
${getLib pkgs.libcap}/lib/libcap.so.* mr,
|
||||
${getLib pkgs.lz4}/lib/liblz4.so.* mr,
|
||||
${getLib pkgs.attr}/lib/libattr.so.* mr, # */
|
||||
|
||||
${resolverList} r,
|
||||
|
||||
/run/systemd/notify rw,
|
||||
}
|
||||
'');
|
||||
})
|
||||
|
||||
(mkIf useUpstreamResolverList {
|
||||
systemd.services.init-dnscrypt-proxy-statedir = {
|
||||
description = "Initialize dnscrypt-proxy state directory";
|
||||
|
||||
wantedBy = [ "dnscrypt-proxy.service" ];
|
||||
before = [ "dnscrypt-proxy.service" ];
|
||||
|
||||
script = ''
|
||||
mkdir -pv ${stateDirectory}
|
||||
chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory}
|
||||
cp -uv \
|
||||
${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \
|
||||
${stateDirectory}
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.update-dnscrypt-resolvers = {
|
||||
description = "Update list of DNSCrypt resolvers";
|
||||
|
||||
requires = [ "init-dnscrypt-proxy-statedir.service" ];
|
||||
after = [ "init-dnscrypt-proxy-statedir.service" ];
|
||||
|
||||
path = with pkgs; [ curl diffutils dnscrypt-proxy minisign ];
|
||||
script = ''
|
||||
cd ${stateDirectory}
|
||||
domain=raw.githubusercontent.com
|
||||
get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)"
|
||||
$get -o dnscrypt-resolvers.csv.tmp \
|
||||
https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv
|
||||
$get -o dnscrypt-resolvers.csv.minisig.tmp \
|
||||
https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
|
||||
mv dnscrypt-resolvers.csv.minisig{.tmp,}
|
||||
if ! minisign -q -V -p ${upstreamResolverListPubKey} \
|
||||
-m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then
|
||||
echo "failed to verify resolver list!" >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ -f dnscrypt-resolvers.csv ]] && mv dnscrypt-resolvers.csv{,.old}
|
||||
mv dnscrypt-resolvers.csv{.tmp,}
|
||||
if cmp dnscrypt-resolvers.csv{,.old} ; then
|
||||
echo "no change"
|
||||
else
|
||||
echo "resolver list updated"
|
||||
fi
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
ProtectHome = true;
|
||||
ProtectSystem = "strict";
|
||||
ReadWritePaths = "${dirOf stateDirectory} ${stateDirectory}";
|
||||
SystemCallFilter = "~@mount";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.update-dnscrypt-resolvers = {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnBootSec = "5min";
|
||||
OnUnitActiveSec = "6h";
|
||||
};
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ])
|
||||
|
||||
(mkChangedOptionModule
|
||||
[ "services" "dnscrypt-proxy" "tcpOnly" ]
|
||||
[ "services" "dnscrypt-proxy" "extraArgs" ]
|
||||
(config:
|
||||
let val = getAttrFromPath [ "services" "dnscrypt-proxy" "tcpOnly" ] config; in
|
||||
optional val "-T"))
|
||||
|
||||
(mkChangedOptionModule
|
||||
[ "services" "dnscrypt-proxy" "ephemeralKeys" ]
|
||||
[ "services" "dnscrypt-proxy" "extraArgs" ]
|
||||
(config:
|
||||
let val = getAttrFromPath [ "services" "dnscrypt-proxy" "ephemeralKeys" ] config; in
|
||||
optional val "-E"))
|
||||
|
||||
(mkRemovedOptionModule [ "services" "dnscrypt-proxy" "resolverList" ] ''
|
||||
The current resolver listing from upstream is always used
|
||||
unless a custom resolver is specified.
|
||||
'')
|
||||
];
|
||||
}
|
69
nixos/modules/services/networking/dnscrypt-proxy.xml
Normal file
69
nixos/modules/services/networking/dnscrypt-proxy.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
version="5.0"
|
||||
xml:id="sec-dnscrypt-proxy">
|
||||
|
||||
<title>DNSCrypt client proxy</title>
|
||||
|
||||
<para>
|
||||
The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled
|
||||
upstream resolver. The traffic between the client and the upstream
|
||||
resolver is encrypted and authenticated, mitigating the risk of MITM
|
||||
attacks, DNS poisoning attacks, and third-party snooping (assuming the
|
||||
upstream is trustworthy).
|
||||
</para>
|
||||
|
||||
<sect1><title>Basic configuration</title>
|
||||
|
||||
<para>
|
||||
To enable the client proxy, set
|
||||
<programlisting>
|
||||
services.dnscrypt-proxy.enable = true;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Enabling the client proxy does not alter the system nameserver; to
|
||||
relay local queries, prepend <literal>127.0.0.1</literal> to
|
||||
<option>networking.nameservers</option>.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>As a forwarder for another DNS client</title>
|
||||
|
||||
<para>
|
||||
To run the DNSCrypt proxy client as a forwarder for another
|
||||
DNS client, change the default proxy listening port to a
|
||||
non-standard value and point the other client to it:
|
||||
<programlisting>
|
||||
services.dnscrypt-proxy.localPort = 43;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<sect2><title>dnsmasq</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
{
|
||||
services.dnsmasq.enable = true;
|
||||
services.dnsmasq.servers = [ "127.0.0.1#43" ];
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2><title>unbound</title>
|
||||
<para>
|
||||
<programlisting>
|
||||
{
|
||||
services.unbound.enable = true;
|
||||
services.unbound.forwardAddresses = [ "127.0.0.1@43" ];
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
@ -255,6 +255,7 @@ in rec {
|
||||
tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; });
|
||||
tests.docker-edge = hydraJob (import tests/docker-edge.nix { system = "x86_64-linux"; });
|
||||
tests.dovecot = callTest tests/dovecot.nix {};
|
||||
tests.dnscrypt-proxy = callTest tests/dnscrypt-proxy.nix { system = "x86_64-linux"; };
|
||||
tests.ecryptfs = callTest tests/ecryptfs.nix {};
|
||||
tests.etcd = hydraJob (import tests/etcd.nix { system = "x86_64-linux"; });
|
||||
tests.ec2-nixops = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-nixops;
|
||||
|
32
nixos/tests/dnscrypt-proxy.nix
Normal file
32
nixos/tests/dnscrypt-proxy.nix
Normal file
@ -0,0 +1,32 @@
|
||||
import ./make-test.nix ({ pkgs, ... }: {
|
||||
name = "dnscrypt-proxy";
|
||||
meta = with pkgs.stdenv.lib.maintainers; {
|
||||
maintainers = [ joachifm ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
# A client running the recommended setup: DNSCrypt proxy as a forwarder
|
||||
# for a caching DNS client.
|
||||
client =
|
||||
{ config, pkgs, ... }:
|
||||
let localProxyPort = 43; in
|
||||
{
|
||||
security.apparmor.enable = true;
|
||||
|
||||
services.dnscrypt-proxy.enable = true;
|
||||
services.dnscrypt-proxy.localPort = localProxyPort;
|
||||
services.dnscrypt-proxy.extraArgs = [ "-X libdcplugin_example.so" ];
|
||||
|
||||
services.dnsmasq.enable = true;
|
||||
services.dnsmasq.servers = [ "127.0.0.1#${toString localProxyPort}" ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
$client->waitForUnit("dnsmasq");
|
||||
|
||||
# The daemon is socket activated; sending a single ping should activate it.
|
||||
$client->execute("${pkgs.iputils}/bin/ping -c1 example.com");
|
||||
$client->succeed("systemctl is-active dnscrypt-proxy");
|
||||
'';
|
||||
})
|
@ -7,7 +7,7 @@ stdenv.mkDerivation rec {
|
||||
version = "1.9.5";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://download.dnscrypt.org/dnscrypt-proxy/${name}.tar.bz2";
|
||||
url = "https://launchpad.net/ubuntu/+archive/primary/+files/${name}.orig.tar.gz";
|
||||
sha256 = "1dhvklr4dg2vlw108n11xbamacaryyg3dbrg629b76lp7685p7z8";
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user