vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / kresd.nix
blob892f863aacfeb2a1f19e34f9b283bca1abe8dbe1
1 { config, lib, pkgs, ... }:
2 let
3   cfg = config.services.kresd;
5   # Convert systemd-style address specification to kresd config line(s).
6   # On Nix level we don't attempt to precisely validate the address specifications.
7   # The optional IPv6 scope spec comes *after* port, perhaps surprisingly.
8   mkListen = kind: addr: let
9     al_v4 = builtins.match "([0-9.]+):([0-9]+)($)" addr;
10     al_v6 = builtins.match "\\[(.+)]:([0-9]+)(%.*|$)" addr;
11     al_portOnly = builtins.match "(^)([0-9]+)" addr;
12     al = lib.findFirst (a: a != null)
13       (throw "services.kresd.*: incorrect address specification '${addr}'")
14       [ al_v4 al_v6 al_portOnly ];
15     port = lib.elemAt al 1;
16     addrSpec = if al_portOnly == null then "'${lib.head al}${lib.elemAt al 2}'" else "{'::', '0.0.0.0'}";
17     in # freebind is set for compatibility with earlier kresd services;
18        # it could be configurable, for example.
19       ''
20         net.listen(${addrSpec}, ${port}, { kind = '${kind}', freebind = true })
21       '';
23   configFile = pkgs.writeText "kresd.conf" (
24     ""
25     + lib.concatMapStrings (mkListen "dns") cfg.listenPlain
26     + lib.concatMapStrings (mkListen "tls") cfg.listenTLS
27     + lib.concatMapStrings (mkListen "doh2") cfg.listenDoH
28     + cfg.extraConfig
29   );
30 in {
31   meta.maintainers = [ lib.maintainers.vcunat /* upstream developer */ ];
33   imports = [
34     (lib.mkChangedOptionModule [ "services" "kresd" "interfaces" ] [ "services" "kresd" "listenPlain" ]
35       (config:
36         let value = lib.getAttrFromPath [ "services" "kresd" "interfaces" ] config;
37         in map
38           (iface: if lib.elem ":" (lib.stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") # Syntax depends on being IPv6 or IPv4.
39           value
40       )
41     )
42     (lib.mkRemovedOptionModule [ "services" "kresd" "cacheDir" ] "Please use (bind-)mounting instead.")
43   ];
45   ###### interface
46   options.services.kresd = {
47     enable = lib.mkOption {
48       type = lib.types.bool;
49       default = false;
50       description = ''
51         Whether to enable knot-resolver domain name server.
52         DNSSEC validation is turned on by default.
53         You can run `sudo nc -U /run/knot-resolver/control/1`
54         and give commands interactively to kresd@1.service.
55       '';
56     };
57     package = lib.mkPackageOption pkgs "knot-resolver" {
58       example = "knot-resolver.override { extraFeatures = true; }";
59     };
60     extraConfig = lib.mkOption {
61       type = lib.types.lines;
62       default = "";
63       description = ''
64         Extra lines to be added verbatim to the generated configuration file.
65       '';
66     };
67     listenPlain = lib.mkOption {
68       type = with lib.types; listOf str;
69       default = [ "[::1]:53" "127.0.0.1:53" ];
70       example = [ "53" ];
71       description = ''
72         What addresses and ports the server should listen on.
73         For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
74       '';
75     };
76     listenTLS = lib.mkOption {
77       type = with lib.types; listOf str;
78       default = [];
79       example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ];
80       description = ''
81         Addresses and ports on which kresd should provide DNS over TLS (see RFC 7858).
82         For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
83       '';
84     };
85     listenDoH = lib.mkOption {
86       type = with lib.types; listOf str;
87       default = [];
88       example = [ "198.51.100.1:443" "[2001:db8::1]:443" "443" ];
89       description = ''
90         Addresses and ports on which kresd should provide DNS over HTTPS/2 (see RFC 8484).
91         For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
92       '';
93     };
94     instances = lib.mkOption {
95       type = lib.types.ints.unsigned;
96       default = 1;
97       description = ''
98         The number of instances to start.  They will be called kresd@{1,2,...}.service.
99         Knot Resolver uses no threads, so this is the way to scale.
100         You can dynamically start/stop them at will, so this is just system default.
101       '';
102     };
103     # TODO: perhaps options for more common stuff like cache size or forwarding
104   };
106   ###### implementation
107   config = lib.mkIf cfg.enable {
108     environment.etc."knot-resolver/kresd.conf".source = configFile; # not required
110     networking.resolvconf.useLocalResolver = lib.mkDefault true;
112     users.users.knot-resolver =
113       { isSystemUser = true;
114         group = "knot-resolver";
115         description = "Knot-resolver daemon user";
116       };
117     users.groups.knot-resolver.gid = null;
119     systemd.packages = [ cfg.package ]; # the units are patched inside the package a bit
121     systemd.targets.kresd = { # configure units started by default
122       wantedBy = [ "multi-user.target" ];
123       wants = [ "kres-cache-gc.service" ]
124         ++ map (i: "kresd@${toString i}.service") (lib.range 1 cfg.instances);
125     };
126     systemd.services."kresd@".serviceConfig = {
127       ExecStart = "${cfg.package}/bin/kresd --noninteractive "
128         + "-c ${cfg.package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}";
129       # Ensure /run/knot-resolver exists
130       RuntimeDirectory = "knot-resolver";
131       RuntimeDirectoryMode = "0770";
132       # Ensure /var/lib/knot-resolver exists
133       StateDirectory = "knot-resolver";
134       StateDirectoryMode = "0770";
135       # Ensure /var/cache/knot-resolver exists
136       CacheDirectory = "knot-resolver";
137       CacheDirectoryMode = "0770";
138     };
139     # We don't mind running stop phase from wrong version.  It seems less racy.
140     systemd.services."kresd@".stopIfChanged = false;
141   };