8 cfg = config.services.kresd;
10 # Convert systemd-style address specification to kresd config line(s).
11 # On Nix level we don't attempt to precisely validate the address specifications.
12 # The optional IPv6 scope spec comes *after* port, perhaps surprisingly.
16 al_v4 = builtins.match "([0-9.]+):([0-9]+)($)" addr;
17 al_v6 = builtins.match "\\[(.+)]:([0-9]+)(%.*|$)" addr;
18 al_portOnly = builtins.match "(^)([0-9]+)" addr;
20 lib.findFirst (a: a != null) (throw "services.kresd.*: incorrect address specification '${addr}'")
26 port = lib.elemAt al 1;
28 if al_portOnly == null then "'${lib.head al}${lib.elemAt al 2}'" else "{'::', '0.0.0.0'}";
30 # freebind is set for compatibility with earlier kresd services;
31 # it could be configurable, for example.
33 net.listen(${addrSpec}, ${port}, { kind = '${kind}', freebind = true })
36 configFile = pkgs.writeText "kresd.conf" (
38 + lib.concatMapStrings (mkListen "dns") cfg.listenPlain
39 + lib.concatMapStrings (mkListen "tls") cfg.listenTLS
40 + lib.concatMapStrings (mkListen "doh2") cfg.listenDoH
46 lib.maintainers.vcunat # upstream developer
50 (lib.mkChangedOptionModule [ "services" "kresd" "interfaces" ] [ "services" "kresd" "listenPlain" ]
54 value = lib.getAttrFromPath [ "services" "kresd" "interfaces" ] config;
56 map (iface: if lib.elem ":" (lib.stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") # Syntax depends on being IPv6 or IPv4.
60 (lib.mkRemovedOptionModule [ "services" "kresd" "cacheDir" ] "Please use (bind-)mounting instead.")
64 options.services.kresd = {
65 enable = lib.mkOption {
66 type = lib.types.bool;
69 Whether to enable knot-resolver domain name server.
70 DNSSEC validation is turned on by default.
71 You can run `sudo nc -U /run/knot-resolver/control/1`
72 and give commands interactively to kresd@1.service.
75 package = lib.mkPackageOption pkgs "knot-resolver" {
76 example = "knot-resolver.override { extraFeatures = true; }";
78 extraConfig = lib.mkOption {
79 type = lib.types.lines;
82 Extra lines to be added verbatim to the generated configuration file.
83 See upstream documentation <https://www.knot-resolver.cz/documentation/stable/config-overview.html> for more details.
86 listenPlain = lib.mkOption {
87 type = with lib.types; listOf str;
94 What addresses and ports the server should listen on.
95 For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
98 listenTLS = lib.mkOption {
99 type = with lib.types; listOf str;
107 Addresses and ports on which kresd should provide DNS over TLS (see RFC 7858).
108 For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
111 listenDoH = lib.mkOption {
112 type = with lib.types; listOf str;
120 Addresses and ports on which kresd should provide DNS over HTTPS/2 (see RFC 8484).
121 For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
124 instances = lib.mkOption {
125 type = lib.types.ints.unsigned;
128 The number of instances to start. They will be called kresd@{1,2,...}.service.
129 Knot Resolver uses no threads, so this is the way to scale.
130 You can dynamically start/stop them at will, so this is just system default.
133 # TODO: perhaps options for more common stuff like cache size or forwarding
136 ###### implementation
137 config = lib.mkIf cfg.enable {
138 environment.etc."knot-resolver/kresd.conf".source = configFile; # not required
140 networking.resolvconf.useLocalResolver = lib.mkDefault true;
142 users.users.knot-resolver = {
144 group = "knot-resolver";
145 description = "Knot-resolver daemon user";
147 users.groups.knot-resolver.gid = null;
149 systemd.packages = [ cfg.package ]; # the units are patched inside the package a bit
151 systemd.targets.kresd = {
152 # configure units started by default
153 wantedBy = [ "multi-user.target" ];
155 "kres-cache-gc.service"
156 ] ++ map (i: "kresd@${toString i}.service") (lib.range 1 cfg.instances);
158 systemd.services."kresd@".serviceConfig = {
160 "${cfg.package}/bin/kresd --noninteractive "
161 + "-c ${cfg.package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}";
162 # Ensure /run/knot-resolver exists
163 RuntimeDirectory = "knot-resolver";
164 RuntimeDirectoryMode = "0770";
165 # Ensure /var/lib/knot-resolver exists
166 StateDirectory = "knot-resolver";
167 StateDirectoryMode = "0770";
168 # Ensure /var/cache/knot-resolver exists
169 CacheDirectory = "knot-resolver";
170 CacheDirectoryMode = "0770";
172 # We don't mind running stop phase from wrong version. It seems less racy.
173 systemd.services."kresd@".stopIfChanged = false;