1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.dnsdist;
toLua = lib.generators.toLua {};
mkBind = cfg: toLua "${cfg.listenAddress}:${toString cfg.listenPort}";
configFile = pkgs.writeText "dnsdist.conf" ''
setLocal(${mkBind cfg})
${lib.optionalString cfg.dnscrypt.enable dnscryptSetup}
${cfg.extraConfig}
'';
dnscryptSetup = ''
last_rotation = 0
cert_serial = 0
provider_key = ${toLua cfg.dnscrypt.providerKey}
cert_lifetime = ${toLua cfg.dnscrypt.certLifetime} * 60
function file_exists(name)
local f = io.open(name, "r")
return f ~= nil and io.close(f)
end
function dnscrypt_setup()
-- generate provider keys on first run
if provider_key == nil then
provider_key = "/var/lib/dnsdist/private.key"
if not file_exists(provider_key) then
generateDNSCryptProviderKeys("/var/lib/dnsdist/public.key",
"/var/lib/dnsdist/private.key")
print("DNSCrypt: generated provider keypair")
end
end
-- generate resolver certificate
local now = os.time()
generateDNSCryptCertificate(
provider_key, "/run/dnsdist/resolver.cert", "/run/dnsdist/resolver.key",
cert_serial, now - 60, now + cert_lifetime)
addDNSCryptBind(
${mkBind cfg.dnscrypt}, ${toLua cfg.dnscrypt.providerName},
"/run/dnsdist/resolver.cert", "/run/dnsdist/resolver.key")
end
function maintenance()
-- certificate rotation
local now = os.time()
local dnscrypt = getDNSCryptBind(0)
if ((now - last_rotation) > 0.9 * cert_lifetime) then
-- generate and start using a new certificate
dnscrypt:generateAndLoadInMemoryCertificate(
provider_key, cert_serial + 1,
now - 60, now + cert_lifetime)
-- stop advertising the last certificate
dnscrypt:markInactive(cert_serial)
-- remove the second to last certificate
if (cert_serial > 1) then
dnscrypt:removeInactiveCertificate(cert_serial - 1)
end
print("DNSCrypt: rotated certificate")
-- increment serial number
cert_serial = cert_serial + 1
last_rotation = now
end
end
dnscrypt_setup()
'';
in {
options = {
services.dnsdist = {
enable = mkEnableOption "dnsdist domain name server";
listenAddress = mkOption {
type = types.str;
description = "Listen IP address";
default = "0.0.0.0";
};
listenPort = mkOption {
type = types.port;
description = "Listen port";
default = 53;
};
dnscrypt = {
enable = mkEnableOption "a DNSCrypt endpoint to dnsdist";
listenAddress = mkOption {
type = types.str;
description = "Listen IP address of the endpoint";
default = "0.0.0.0";
};
listenPort = mkOption {
type = types.port;
description = "Listen port of the endpoint";
default = 443;
};
providerName = mkOption {
type = types.str;
default = "2.dnscrypt-cert.${config.networking.hostName}";
defaultText = literalExpression "2.dnscrypt-cert.\${config.networking.hostName}";
example = "2.dnscrypt-cert.myresolver";
description = ''
The name that will be given to this DNSCrypt resolver.
::: {.note}
The provider name must start with `2.dnscrypt-cert.`.
:::
'';
};
providerKey = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
The filepath to the provider secret key.
If not given a new provider key pair will be generated in
/var/lib/dnsdist on the first run.
::: {.note}
The file must be readable by the dnsdist user/group.
:::
'';
};
certLifetime = mkOption {
type = types.ints.positive;
default = 15;
description = ''
The lifetime (in minutes) of the resolver certificate.
This will be automatically rotated before expiration.
'';
};
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Extra lines to be added verbatim to dnsdist.conf.
'';
};
};
};
config = mkIf cfg.enable {
users.users.dnsdist = {
description = "dnsdist daemons user";
isSystemUser = true;
group = "dnsdist";
};
users.groups.dnsdist = {};
systemd.packages = [ pkgs.dnsdist ];
systemd.services.dnsdist = {
wantedBy = [ "multi-user.target" ];
startLimitIntervalSec = 0;
serviceConfig = {
User = "dnsdist";
Group = "dnsdist";
RuntimeDirectory = "dnsdist";
StateDirectory = "dnsdist";
# upstream overrides for better nixos compatibility
ExecStartPre = [ "" "${pkgs.dnsdist}/bin/dnsdist --check-config --config ${configFile}" ];
ExecStart = [ "" "${pkgs.dnsdist}/bin/dnsdist --supervised --disable-syslog --config ${configFile}" ];
};
};
};
}
|