Merge pull request #85900 from rnhmjoj/dnscrypt
nixos/dnscrypt-wrapper: use dnscrypt-proxy1
This commit is contained in:
commit
a4f9e8bf68
@ -5,12 +5,20 @@ let
|
|||||||
cfg = config.services.dnscrypt-wrapper;
|
cfg = config.services.dnscrypt-wrapper;
|
||||||
dataDir = "/var/lib/dnscrypt-wrapper";
|
dataDir = "/var/lib/dnscrypt-wrapper";
|
||||||
|
|
||||||
|
mkPath = path: default:
|
||||||
|
if path != null
|
||||||
|
then toString path
|
||||||
|
else default;
|
||||||
|
|
||||||
|
publicKey = mkPath cfg.providerKey.public "${dataDir}/public.key";
|
||||||
|
secretKey = mkPath cfg.providerKey.secret "${dataDir}/secret.key";
|
||||||
|
|
||||||
daemonArgs = with cfg; [
|
daemonArgs = with cfg; [
|
||||||
"--listen-address=${address}:${toString port}"
|
"--listen-address=${address}:${toString port}"
|
||||||
"--resolver-address=${upstream.address}:${toString upstream.port}"
|
"--resolver-address=${upstream.address}:${toString upstream.port}"
|
||||||
"--provider-name=${providerName}"
|
"--provider-name=${providerName}"
|
||||||
"--provider-publickey-file=public.key"
|
"--provider-publickey-file=${publicKey}"
|
||||||
"--provider-secretkey-file=secret.key"
|
"--provider-secretkey-file=${secretKey}"
|
||||||
"--provider-cert-file=${providerName}.crt"
|
"--provider-cert-file=${providerName}.crt"
|
||||||
"--crypt-secretkey-file=${providerName}.key"
|
"--crypt-secretkey-file=${providerName}.key"
|
||||||
];
|
];
|
||||||
@ -24,17 +32,19 @@ let
|
|||||||
dnscrypt-wrapper --gen-cert-file \
|
dnscrypt-wrapper --gen-cert-file \
|
||||||
--crypt-secretkey-file=${cfg.providerName}.key \
|
--crypt-secretkey-file=${cfg.providerName}.key \
|
||||||
--provider-cert-file=${cfg.providerName}.crt \
|
--provider-cert-file=${cfg.providerName}.crt \
|
||||||
--provider-publickey-file=public.key \
|
--provider-publickey-file=${publicKey} \
|
||||||
--provider-secretkey-file=secret.key \
|
--provider-secretkey-file=${secretKey} \
|
||||||
--cert-file-expire-days=${toString cfg.keys.expiration}
|
--cert-file-expire-days=${toString cfg.keys.expiration}
|
||||||
}
|
}
|
||||||
|
|
||||||
cd ${dataDir}
|
cd ${dataDir}
|
||||||
|
|
||||||
# generate provider keypair (first run only)
|
# generate provider keypair (first run only)
|
||||||
if [ ! -f public.key ] || [ ! -f secret.key ]; then
|
${optionalString (cfg.providerKey.public == null || cfg.providerKey.secret == null) ''
|
||||||
|
if [ ! -f ${publicKey} ] || [ ! -f ${secretKey} ]; then
|
||||||
dnscrypt-wrapper --gen-provider-keypair
|
dnscrypt-wrapper --gen-provider-keypair
|
||||||
fi
|
fi
|
||||||
|
''}
|
||||||
|
|
||||||
# generate new keys for rotation
|
# generate new keys for rotation
|
||||||
if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then
|
if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then
|
||||||
@ -64,6 +74,47 @@ let
|
|||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
||||||
|
# This is the fork of the original dnscrypt-proxy maintained by Dyne.org.
|
||||||
|
# dnscrypt-proxy2 doesn't provide the `--test` feature that is needed to
|
||||||
|
# correctly implement key rotation of dnscrypt-wrapper ephemeral keys.
|
||||||
|
dnscrypt-proxy1 = pkgs.callPackage
|
||||||
|
({ stdenv, fetchFromGitHub, autoreconfHook
|
||||||
|
, pkgconfig, libsodium, ldns, openssl, systemd }:
|
||||||
|
|
||||||
|
stdenv.mkDerivation rec {
|
||||||
|
pname = "dnscrypt-proxy";
|
||||||
|
version = "2019-08-20";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "dyne";
|
||||||
|
repo = "dnscrypt-proxy";
|
||||||
|
rev = "07ac3825b5069adc28e2547c16b1d983a8ed8d80";
|
||||||
|
sha256 = "0c4mq741q4rpmdn09agwmxap32kf0vgfz7pkhcdc5h54chc3g3xy";
|
||||||
|
};
|
||||||
|
|
||||||
|
configureFlags = optional stdenv.isLinux "--with-systemd";
|
||||||
|
|
||||||
|
nativeBuildInputs = [ autoreconfHook pkgconfig ];
|
||||||
|
|
||||||
|
# <ldns/ldns.h> depends on <openssl/ssl.h>
|
||||||
|
buildInputs = [ libsodium openssl.dev ldns ] ++ optional stdenv.isLinux systemd;
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
# Previous versions required libtool files to load plugins; they are
|
||||||
|
# now strictly optional.
|
||||||
|
rm $out/lib/dnscrypt-proxy/*.la
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "A tool for securing communications between a client and a DNS resolver";
|
||||||
|
homepage = "https://github.com/dyne/dnscrypt-proxy";
|
||||||
|
license = licenses.isc;
|
||||||
|
maintainers = with maintainers; [ rnhmjoj ];
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
}) { };
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
|
||||||
|
|
||||||
@ -98,6 +149,26 @@ in {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
providerKey.public = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
example = "/etc/secrets/public.key";
|
||||||
|
description = ''
|
||||||
|
The filepath to the provider public key. If not given a new
|
||||||
|
provider key pair will be generated on the first run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
providerKey.secret = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
example = "/etc/secrets/secret.key";
|
||||||
|
description = ''
|
||||||
|
The filepath to the provider secret key. If not given a new
|
||||||
|
provider key pair will be generated on the first run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
upstream.address = mkOption {
|
upstream.address = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "127.0.0.1";
|
default = "127.0.0.1";
|
||||||
@ -179,7 +250,7 @@ in {
|
|||||||
requires = [ "dnscrypt-wrapper.service" ];
|
requires = [ "dnscrypt-wrapper.service" ];
|
||||||
description = "Rotates DNSCrypt wrapper keys if soon to expire";
|
description = "Rotates DNSCrypt wrapper keys if soon to expire";
|
||||||
|
|
||||||
path = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy gawk ];
|
path = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy1 gawk ];
|
||||||
script = rotateKeys;
|
script = rotateKeys;
|
||||||
serviceConfig.User = "dnscrypt-wrapper";
|
serviceConfig.User = "dnscrypt-wrapper";
|
||||||
};
|
};
|
||||||
@ -196,6 +267,13 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assertions = with cfg; [
|
||||||
|
{ assertion = (providerKey.public == null && providerKey.secret == null) ||
|
||||||
|
(providerKey.secret != null && providerKey.public != null);
|
||||||
|
message = "The secret and public provider key must be set together.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||||
|
@ -70,6 +70,7 @@ in
|
|||||||
deluge = handleTest ./deluge.nix {};
|
deluge = handleTest ./deluge.nix {};
|
||||||
dhparams = handleTest ./dhparams.nix {};
|
dhparams = handleTest ./dhparams.nix {};
|
||||||
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
|
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
|
||||||
|
dnscrypt-wrapper = handleTestOn ["x86_64-linux"] ./dnscrypt-wrapper {};
|
||||||
doas = handleTest ./doas.nix {};
|
doas = handleTest ./doas.nix {};
|
||||||
docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
|
docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
|
||||||
oci-containers = handleTestOn ["x86_64-linux"] ./oci-containers.nix {};
|
oci-containers = handleTestOn ["x86_64-linux"] ./oci-containers.nix {};
|
||||||
|
71
nixos/tests/dnscrypt-wrapper/default.nix
Normal file
71
nixos/tests/dnscrypt-wrapper/default.nix
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import ../make-test-python.nix ({ pkgs, ... }: {
|
||||||
|
name = "dnscrypt-wrapper";
|
||||||
|
meta = with pkgs.stdenv.lib.maintainers; {
|
||||||
|
maintainers = [ rnhmjoj ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
server = { lib, ... }:
|
||||||
|
{ services.dnscrypt-wrapper = with builtins;
|
||||||
|
{ enable = true;
|
||||||
|
address = "192.168.1.1";
|
||||||
|
keys.expiration = 5; # days
|
||||||
|
keys.checkInterval = 2; # min
|
||||||
|
# The keypair was generated by the command:
|
||||||
|
# dnscrypt-wrapper --gen-provider-keypair \
|
||||||
|
# --provider-name=2.dnscrypt-cert.server \
|
||||||
|
# --ext-address=192.168.1.1:5353
|
||||||
|
providerKey.public = toFile "public.key" (readFile ./public.key);
|
||||||
|
providerKey.secret = toFile "secret.key" (readFile ./secret.key);
|
||||||
|
};
|
||||||
|
services.tinydns.enable = true;
|
||||||
|
services.tinydns.data = ''
|
||||||
|
..:192.168.1.1:a
|
||||||
|
+it.works:1.2.3.4
|
||||||
|
'';
|
||||||
|
networking.firewall.allowedUDPPorts = [ 5353 ];
|
||||||
|
networking.firewall.allowedTCPPorts = [ 5353 ];
|
||||||
|
networking.interfaces.eth1.ipv4.addresses = lib.mkForce
|
||||||
|
[ { address = "192.168.1.1"; prefixLength = 24; } ];
|
||||||
|
};
|
||||||
|
|
||||||
|
client = { lib, ... }:
|
||||||
|
{ services.dnscrypt-proxy2.enable = true;
|
||||||
|
services.dnscrypt-proxy2.settings = {
|
||||||
|
server_names = [ "server" ];
|
||||||
|
static.server.stamp = "sdns://AQAAAAAAAAAAEDE5Mi4xNjguMS4xOjUzNTMgFEHYOv0SCKSuqR5CDYa7-58cCBuXO2_5uTSVU9wNQF0WMi5kbnNjcnlwdC1jZXJ0LnNlcnZlcg";
|
||||||
|
};
|
||||||
|
networking.nameservers = [ "127.0.0.1" ];
|
||||||
|
networking.interfaces.eth1.ipv4.addresses = lib.mkForce
|
||||||
|
[ { address = "192.168.1.2"; prefixLength = 24; } ];
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
with subtest("The server can generate the ephemeral keypair"):
|
||||||
|
server.wait_for_unit("dnscrypt-wrapper")
|
||||||
|
server.wait_for_file("/var/lib/dnscrypt-wrapper/2.dnscrypt-cert.server.key")
|
||||||
|
server.wait_for_file("/var/lib/dnscrypt-wrapper/2.dnscrypt-cert.server.crt")
|
||||||
|
|
||||||
|
with subtest("The client can connect to the server"):
|
||||||
|
server.wait_for_unit("tinydns")
|
||||||
|
client.wait_for_unit("dnscrypt-proxy2")
|
||||||
|
assert "1.2.3.4" in client.succeed(
|
||||||
|
"host it.works"
|
||||||
|
), "The IP address of 'it.works' does not match 1.2.3.4"
|
||||||
|
|
||||||
|
with subtest("The server rotates the ephemeral keys"):
|
||||||
|
# advance time by a little less than 5 days
|
||||||
|
server.succeed("date -s \"$(date --date '4 days 6 hours')\"")
|
||||||
|
client.succeed("date -s \"$(date --date '4 days 6 hours')\"")
|
||||||
|
server.wait_for_file("/var/lib/dnscrypt-wrapper/oldkeys")
|
||||||
|
|
||||||
|
with subtest("The client can still connect to the server"):
|
||||||
|
server.wait_for_unit("dnscrypt-wrapper")
|
||||||
|
client.succeed("host it.works")
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
|
1
nixos/tests/dnscrypt-wrapper/public.key
Normal file
1
nixos/tests/dnscrypt-wrapper/public.key
Normal file
@ -0,0 +1 @@
|
|||||||
|
A<>:<3A><08><><EFBFBD>B
<0A><><EFBFBD><EFBFBD><1B>;o<><6F>4<EFBFBD>S<EFBFBD>
@]
|
1
nixos/tests/dnscrypt-wrapper/secret.key
Normal file
1
nixos/tests/dnscrypt-wrapper/secret.key
Normal file
@ -0,0 +1 @@
|
|||||||
|
G½>ֶ©» ל>׀א¥(ׂ²‡¼J•«÷=<3D>„<EFBFBD>ֱ<EFBFBD>lלA״:₪®©B
†»<E280A0><C2BB>—;oש¹4•S<E280A2>
@]
|
Loading…
x
Reference in New Issue
Block a user