Merge pull request #22043 from rnhmjoj/dnscrypt-wrapper

dnscrypt-wrapper: add service

Changed files
+188
nixos
modules
+1
nixos/modules/module-list.nix
···
./services/networking/dhcpd.nix
./services/networking/dnschain.nix
./services/networking/dnscrypt-proxy.nix
+
./services/networking/dnscrypt-wrapper.nix
./services/networking/dnsmasq.nix
./services/networking/ejabberd.nix
./services/networking/fan.nix
+187
nixos/modules/services/networking/dnscrypt-wrapper.nix
···
+
{ config, lib, pkgs, ... }:
+
with lib;
+
+
let
+
cfg = config.services.dnscrypt-wrapper;
+
dataDir = "/var/lib/dnscrypt-wrapper";
+
+
daemonArgs = with cfg; [
+
"--listen-address=${address}:${toString port}"
+
"--resolver-address=${upstream.address}:${toString upstream.port}"
+
"--provider-name=${providerName}"
+
"--provider-publickey-file=public.key"
+
"--provider-secretkey-file=secret.key"
+
"--provider-cert-file=${providerName}.crt"
+
"--crypt-secretkey-file=${providerName}.key"
+
];
+
+
genKeys = ''
+
# generates time-limited keypairs
+
keyGen() {
+
dnscrypt-wrapper --gen-crypt-keypair \
+
--crypt-secretkey-file=${cfg.providerName}.key
+
+
dnscrypt-wrapper --gen-cert-file \
+
--crypt-secretkey-file=${cfg.providerName}.key \
+
--provider-cert-file=${cfg.providerName}.crt \
+
--provider-publickey-file=public.key \
+
--provider-secretkey-file=secret.key \
+
--cert-file-expire-days=${toString cfg.keys.expiration}
+
}
+
+
cd ${dataDir}
+
+
# generate provider keypair (first run only)
+
if [ ! -f public.key ] || [ ! -f secret.key ]; then
+
dnscrypt-wrapper --gen-provider-keypair
+
fi
+
+
# generate new keys for rotation
+
if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then
+
keyGen
+
fi
+
'';
+
+
rotateKeys = ''
+
# check if keys are not expired
+
keyValid() {
+
fingerprint=$(dnscrypt-wrapper --show-provider-publickey-fingerprint | awk '{print $(NF)}')
+
dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \
+
--resolver-address=127.0.0.1:${toString cfg.port} \
+
--provider-name=${cfg.providerName} \
+
--provider-key=$fingerprint
+
}
+
+
cd ${dataDir}
+
+
# archive old keys and restart the service
+
if ! keyValid; then
+
mkdir -p oldkeys
+
mv ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key
+
mv ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt
+
systemctl restart dnscrypt-wrapper
+
fi
+
'';
+
+
in {
+
+
+
###### interface
+
+
options.services.dnscrypt-wrapper = {
+
enable = mkEnableOption "DNSCrypt wrapper";
+
+
address = mkOption {
+
type = types.str;
+
default = "127.0.0.1";
+
description = ''
+
The DNSCrypt wrapper will bind to this IP address.
+
'';
+
};
+
+
port = mkOption {
+
type = types.int;
+
default = 5353;
+
description = ''
+
The DNSCrypt wrapper will listen for DNS queries on this port.
+
'';
+
};
+
+
providerName = mkOption {
+
type = types.str;
+
default = "2.dnscrypt-cert.${config.networking.hostName}";
+
example = "2.dnscrypt-cert.myresolver";
+
description = ''
+
The name that will be given to this DNSCrypt resolver.
+
Note: the resolver name must start with <literal>2.dnscrypt-cert.</literal>.
+
'';
+
};
+
+
upstream.address = mkOption {
+
type = types.str;
+
default = "127.0.0.1";
+
description = ''
+
The IP address of the upstream DNS server DNSCrypt will "wrap".
+
'';
+
};
+
+
upstream.port = mkOption {
+
type = types.int;
+
default = 53;
+
description = ''
+
The port of the upstream DNS server DNSCrypt will "wrap".
+
'';
+
};
+
+
keys.expiration = mkOption {
+
type = types.int;
+
default = 30;
+
description = ''
+
The duration (in days) of the time-limited secret key.
+
This will be automatically rotated before expiration.
+
'';
+
};
+
+
keys.checkInterval = mkOption {
+
type = types.int;
+
default = 1440;
+
description = ''
+
The time interval (in minutes) between key expiration checks.
+
'';
+
};
+
+
};
+
+
+
###### implementation
+
+
config = mkIf cfg.enable {
+
+
users.users.dnscrypt-wrapper = {
+
description = "dnscrypt-wrapper daemon user";
+
home = "${dataDir}";
+
createHome = true;
+
};
+
users.groups.dnscrypt-wrapper = { };
+
+
+
systemd.services.dnscrypt-wrapper = {
+
description = "dnscrypt-wrapper daemon";
+
after = [ "network.target" ];
+
wantedBy = [ "multi-user.target" ];
+
path = [ pkgs.dnscrypt-wrapper ];
+
+
serviceConfig = {
+
User = "dnscrypt-wrapper";
+
WorkingDirectory = dataDir;
+
Restart = "on-failure";
+
ExecStart = "${pkgs.dnscrypt-wrapper}/bin/dnscrypt-wrapper ${toString daemonArgs}";
+
};
+
+
preStart = genKeys;
+
};
+
+
+
systemd.services.dnscrypt-wrapper-rotate = {
+
after = [ "network.target" ];
+
requires = [ "dnscrypt-wrapper.service" ];
+
description = "Rotates DNSCrypt wrapper keys if soon to expire";
+
+
path = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy gawk ];
+
script = rotateKeys;
+
};
+
+
+
systemd.timers.dnscrypt-wrapper-rotate = {
+
description = "Periodically check DNSCrypt wrapper keys for expiration";
+
wantedBy = [ "multi-user.target" ];
+
+
timerConfig = {
+
Unit = "dnscrypt-wrapper-rotate.service";
+
OnBootSec = "1min";
+
OnUnitActiveSec = cfg.keys.checkInterval * 60;
+
};
+
};
+
+
};
+
}