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