1 { config, lib, pkgs, ... }:
3 cfg = config.services.dnsdist;
5 toLua = lib.generators.toLua {};
7 mkBind = cfg: toLua "${cfg.listenAddress}:${toString cfg.listenPort}";
9 configFile = pkgs.writeText "dnsdist.conf" ''
10 setLocal(${mkBind cfg})
11 ${lib.optionalString cfg.dnscrypt.enable dnscryptSetup}
18 provider_key = ${toLua cfg.dnscrypt.providerKey}
19 cert_lifetime = ${toLua cfg.dnscrypt.certLifetime} * 60
21 function file_exists(name)
22 local f = io.open(name, "r")
23 return f ~= nil and io.close(f)
26 function dnscrypt_setup()
27 -- generate provider keys on first run
28 if provider_key == nil then
29 provider_key = "/var/lib/dnsdist/private.key"
30 if not file_exists(provider_key) then
31 generateDNSCryptProviderKeys("/var/lib/dnsdist/public.key",
32 "/var/lib/dnsdist/private.key")
33 print("DNSCrypt: generated provider keypair")
37 -- generate resolver certificate
39 generateDNSCryptCertificate(
40 provider_key, "/run/dnsdist/resolver.cert", "/run/dnsdist/resolver.key",
41 cert_serial, now - 60, now + cert_lifetime)
43 ${mkBind cfg.dnscrypt}, ${toLua cfg.dnscrypt.providerName},
44 "/run/dnsdist/resolver.cert", "/run/dnsdist/resolver.key")
47 function maintenance()
48 -- certificate rotation
50 local dnscrypt = getDNSCryptBind(0)
52 if ((now - last_rotation) > 0.9 * cert_lifetime) then
53 -- generate and start using a new certificate
54 dnscrypt:generateAndLoadInMemoryCertificate(
55 provider_key, cert_serial + 1,
56 now - 60, now + cert_lifetime)
58 -- stop advertising the last certificate
59 dnscrypt:markInactive(cert_serial)
61 -- remove the second to last certificate
62 if (cert_serial > 1) then
63 dnscrypt:removeInactiveCertificate(cert_serial - 1)
66 print("DNSCrypt: rotated certificate")
68 -- increment serial number
69 cert_serial = cert_serial + 1
80 enable = lib.mkEnableOption "dnsdist domain name server";
82 listenAddress = lib.mkOption {
84 description = "Listen IP address";
87 listenPort = lib.mkOption {
88 type = lib.types.port;
89 description = "Listen port";
94 enable = lib.mkEnableOption "a DNSCrypt endpoint to dnsdist";
96 listenAddress = lib.mkOption {
98 description = "Listen IP address of the endpoint";
102 listenPort = lib.mkOption {
103 type = lib.types.port;
104 description = "Listen port of the endpoint";
108 providerName = lib.mkOption {
109 type = lib.types.str;
110 default = "2.dnscrypt-cert.${config.networking.hostName}";
111 defaultText = lib.literalExpression "2.dnscrypt-cert.\${config.networking.hostName}";
112 example = "2.dnscrypt-cert.myresolver";
114 The name that will be given to this DNSCrypt resolver.
117 The provider name must start with `2.dnscrypt-cert.`.
122 providerKey = lib.mkOption {
123 type = lib.types.nullOr lib.types.path;
126 The filepath to the provider secret key.
127 If not given a new provider key pair will be generated in
128 /var/lib/dnsdist on the first run.
131 The file must be readable by the dnsdist user/group.
136 certLifetime = lib.mkOption {
137 type = lib.types.ints.positive;
140 The lifetime (in minutes) of the resolver certificate.
141 This will be automatically rotated before expiration.
147 extraConfig = lib.mkOption {
148 type = lib.types.lines;
151 Extra lines to be added verbatim to dnsdist.conf.
157 config = lib.mkIf cfg.enable {
158 users.users.dnsdist = {
159 description = "dnsdist daemons user";
164 users.groups.dnsdist = {};
166 systemd.packages = [ pkgs.dnsdist ];
168 systemd.services.dnsdist = {
169 wantedBy = [ "multi-user.target" ];
171 startLimitIntervalSec = 0;
175 RuntimeDirectory = "dnsdist";
176 StateDirectory = "dnsdist";
177 # upstream overrides for better nixos compatibility
178 ExecStartPre = [ "" "${pkgs.dnsdist}/bin/dnsdist --check-config --config ${configFile}" ];
179 ExecStart = [ "" "${pkgs.dnsdist}/bin/dnsdist --supervised --disable-syslog --config ${configFile}" ];