nixpkgs/nixos/tests/dnscrypt-wrapper/default.nix
rnhmjoj 7f71262745
nixos/tests/dnscrypt-wrapper: test TCP and run on port 443
- Test both TCP and UDP queries (tinydns → bind)
- Test IPv6 by default
- Run DNSCrypt on port 443 along with HTTPS
2023-11-15 21:42:40 +01:00

148 lines
5.3 KiB
Nix

{ lib, pkgs, ... }:
let
snakeoil = import ../common/acme/server/snakeoil-certs.nix;
hosts = lib.mkForce
{ "fd::a" = [ "server" snakeoil.domain ];
"fd::b" = [ "client" ];
};
in
{
name = "dnscrypt-wrapper";
meta = with pkgs.lib.maintainers; {
maintainers = [ rnhmjoj ];
};
nodes = {
server = {
networking.hosts = hosts;
networking.interfaces.eth1.ipv6.addresses = lib.singleton
{ address = "fd::a"; prefixLength = 64; };
services.dnscrypt-wrapper =
{ enable = true;
address = "[::]";
port = 5353;
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 \
providerKey.public = "${./public.key}";
providerKey.secret = "${./secret.key}";
};
# nameserver
services.bind.enable = true;
services.bind.zones = lib.singleton
{ name = ".";
master = true;
file = pkgs.writeText "root.zone" ''
$TTL 3600
. IN SOA example.org. admin.example.org. ( 1 3h 1h 1w 1d )
. IN NS example.org.
example.org. IN AAAA 2001:db8::1
'';
};
# webserver
services.nginx.enable = true;
services.nginx.virtualHosts.${snakeoil.domain} =
{ onlySSL = true;
listenAddresses = [ "localhost" ];
sslCertificate = snakeoil.${snakeoil.domain}.cert;
sslCertificateKey = snakeoil.${snakeoil.domain}.key;
locations."/ip".extraConfig = ''
default_type text/plain;
return 200 "Ciao $remote_addr!\n";
'';
};
# demultiplex HTTP and DNS from port 443
services.sslh =
{ enable = true;
method = "ev";
settings.transparent = true;
settings.listen = lib.mkForce
[ { host = "server"; port = "443"; is_udp = false; }
{ host = "server"; port = "443"; is_udp = true; }
];
settings.protocols =
[ # Send TLS to webserver (TCP)
{ name = "tls"; host= "localhost"; port= "443"; }
# Send DNSCrypt to dnscrypt-wrapper (TCP or UDP)
{ name = "anyprot"; host = "localhost"; port = "5353"; }
{ name = "anyprot"; host = "localhost"; port = "5353"; is_udp = true;}
];
};
networking.firewall.allowedTCPPorts = [ 443 ];
networking.firewall.allowedUDPPorts = [ 443 ];
};
client = {
networking.hosts = hosts;
networking.interfaces.eth1.ipv6.addresses = lib.singleton
{ address = "fd::b"; prefixLength = 64; };
services.dnscrypt-proxy2.enable = true;
services.dnscrypt-proxy2.upstreamDefaults = false;
services.dnscrypt-proxy2.settings =
{ server_names = [ "server" ];
listen_addresses = [ "[::1]:53" ];
cache = false;
# Computed using https://dnscrypt.info/stamps/
static.server.stamp =
"sdns://AQAAAAAAAAAADzE5Mi4xNjguMS4yOjQ0MyAUQdg6"
+"_RIIpK6pHkINhrv7nxwIG5c7b_m5NJVT3A1AXRYyLmRuc2NyeXB0LWNlcnQuc2VydmVy";
};
networking.nameservers = [ "::1" ];
security.pki.certificateFiles = [ snakeoil.ca.cert ];
};
};
testScript = ''
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")
almost_expiration = server.succeed("date --date '4days 23 hours 56min'").strip()
with subtest("The DNSCrypt client can connect to the server"):
server.wait_for_unit("sslh")
client.wait_until_succeeds("journalctl -u dnscrypt-proxy2 --grep '\[server\] OK'")
with subtest("HTTP client can connect to the server"):
server.wait_for_unit("nginx")
client.succeed("curl -s --fail https://${snakeoil.domain}/ip | grep -q fd::b")
with subtest("DNS queries over UDP are working"):
server.wait_for_unit("bind")
client.wait_for_open_port(53)
assert "2001:db8::1" in client.wait_until_succeeds(
"host -U example.org"
), "The IP address of 'example.org' does not match 2001:db8::1"
with subtest("DNS queries over TCP are working"):
server.wait_for_unit("bind")
client.wait_for_open_port(53)
assert "2001:db8::1" in client.wait_until_succeeds(
"host -T example.org"
), "The IP address of 'example.org' does not match 2001:db8::1"
with subtest("The server rotates the ephemeral keys"):
# advance time by a little less than 5 days
server.succeed(f"date -s '{almost_expiration}'")
client.succeed(f"date -s '{almost_expiration}'")
server.wait_for_file("/var/lib/dnscrypt-wrapper/oldkeys")
with subtest("The client can still connect to the server"):
client.systemctl("restart dnscrypt-proxy2")
client.wait_until_succeeds("host -T example.org")
client.wait_until_succeeds("host -U example.org")
'';
}