···
1
+
{ config, lib, pkgs, ... }:
5
+
cfg = config.services.dnscrypt-wrapper;
6
+
dataDir = "/var/lib/dnscrypt-wrapper";
8
+
daemonArgs = with cfg; [
9
+
"--listen-address=${address}:${toString port}"
10
+
"--resolver-address=${upstream.address}:${toString upstream.port}"
11
+
"--provider-name=${providerName}"
12
+
"--provider-publickey-file=public.key"
13
+
"--provider-secretkey-file=secret.key"
14
+
"--provider-cert-file=${providerName}.crt"
15
+
"--crypt-secretkey-file=${providerName}.key"
19
+
# generates time-limited keypairs
21
+
dnscrypt-wrapper --gen-crypt-keypair \
22
+
--crypt-secretkey-file=${cfg.providerName}.key
24
+
dnscrypt-wrapper --gen-cert-file \
25
+
--crypt-secretkey-file=${cfg.providerName}.key \
26
+
--provider-cert-file=${cfg.providerName}.crt \
27
+
--provider-publickey-file=public.key \
28
+
--provider-secretkey-file=secret.key \
29
+
--cert-file-expire-days=${toString cfg.keys.expiration}
34
+
# generate provider keypair (first run only)
35
+
if [ ! -f public.key ] || [ ! -f secret.key ]; then
36
+
dnscrypt-wrapper --gen-provider-keypair
39
+
# generate new keys for rotation
40
+
if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then
46
+
# check if keys are not expired
48
+
fingerprint=$(dnscrypt-wrapper --show-provider-publickey-fingerprint | awk '{print $(NF)}')
49
+
dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \
50
+
--resolver-address=127.0.0.1:${toString cfg.port} \
51
+
--provider-name=${cfg.providerName} \
52
+
--provider-key=$fingerprint
57
+
# archive old keys and restart the service
60
+
mv ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key
61
+
mv ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt
62
+
systemctl restart dnscrypt-wrapper
71
+
options.services.dnscrypt-wrapper = {
72
+
enable = mkEnableOption "DNSCrypt wrapper";
74
+
address = mkOption {
76
+
default = "127.0.0.1";
78
+
The DNSCrypt wrapper will bind to this IP address.
86
+
The DNSCrypt wrapper will listen for DNS queries on this port.
90
+
providerName = mkOption {
92
+
default = "2.dnscrypt-cert.${config.networking.hostName}";
93
+
example = "2.dnscrypt-cert.myresolver";
95
+
The name that will be given to this DNSCrypt resolver.
96
+
Note: the resolver name must start with <literal>2.dnscrypt-cert.</literal>.
100
+
upstream.address = mkOption {
102
+
default = "127.0.0.1";
104
+
The IP address of the upstream DNS server DNSCrypt will "wrap".
108
+
upstream.port = mkOption {
112
+
The port of the upstream DNS server DNSCrypt will "wrap".
116
+
keys.expiration = mkOption {
120
+
The duration (in days) of the time-limited secret key.
121
+
This will be automatically rotated before expiration.
125
+
keys.checkInterval = mkOption {
129
+
The time interval (in minutes) between key expiration checks.
136
+
###### implementation
138
+
config = mkIf cfg.enable {
140
+
users.users.dnscrypt-wrapper = {
141
+
description = "dnscrypt-wrapper daemon user";
142
+
home = "${dataDir}";
145
+
users.groups.dnscrypt-wrapper = { };
148
+
systemd.services.dnscrypt-wrapper = {
149
+
description = "dnscrypt-wrapper daemon";
150
+
after = [ "network.target" ];
151
+
wantedBy = [ "multi-user.target" ];
152
+
path = [ pkgs.dnscrypt-wrapper ];
155
+
User = "dnscrypt-wrapper";
156
+
WorkingDirectory = dataDir;
157
+
Restart = "on-failure";
158
+
ExecStart = "${pkgs.dnscrypt-wrapper}/bin/dnscrypt-wrapper ${toString daemonArgs}";
161
+
preStart = genKeys;
165
+
systemd.services.dnscrypt-wrapper-rotate = {
166
+
after = [ "network.target" ];
167
+
requires = [ "dnscrypt-wrapper.service" ];
168
+
description = "Rotates DNSCrypt wrapper keys if soon to expire";
170
+
path = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy gawk ];
171
+
script = rotateKeys;
175
+
systemd.timers.dnscrypt-wrapper-rotate = {
176
+
description = "Periodically check DNSCrypt wrapper keys for expiration";
177
+
wantedBy = [ "multi-user.target" ];
180
+
Unit = "dnscrypt-wrapper-rotate.service";
181
+
OnBootSec = "1min";
182
+
OnUnitActiveSec = cfg.keys.checkInterval * 60;