1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.kresd;
8 package = pkgs.knot-resolver;
9
10 configFile = pkgs.writeText "kresd.conf" cfg.extraConfig;
11in
12
13{
14 meta.maintainers = [ maintainers.vcunat /* upstream developer */ ];
15
16 ###### interface
17 options.services.kresd = {
18 enable = mkOption {
19 type = types.bool;
20 default = false;
21 description = ''
22 Whether to enable knot-resolver domain name server.
23 DNSSEC validation is turned on by default.
24 You can run <literal>sudo nc -U /run/kresd/control</literal>
25 and give commands interactively to kresd.
26 '';
27 };
28 extraConfig = mkOption {
29 type = types.lines;
30 default = "";
31 description = ''
32 Extra lines to be added verbatim to the generated configuration file.
33 '';
34 };
35 cacheDir = mkOption {
36 type = types.path;
37 default = "/var/cache/kresd";
38 description = ''
39 Directory for caches. They are intended to survive reboots.
40 '';
41 };
42 interfaces = mkOption {
43 type = with types; listOf str;
44 default = [ "::1" "127.0.0.1" ];
45 description = ''
46 What addresses the server should listen on.
47 '';
48 };
49 # TODO: perhaps options for more common stuff like cache size or forwarding
50 };
51
52 ###### implementation
53 config = mkIf cfg.enable {
54 environment.etc."kresd.conf".source = configFile; # not required
55
56 users.extraUsers = singleton
57 { name = "kresd";
58 uid = config.ids.uids.kresd;
59 group = "kresd";
60 description = "Knot-resolver daemon user";
61 };
62 users.extraGroups = singleton
63 { name = "kresd";
64 gid = config.ids.gids.kresd;
65 };
66
67 systemd.sockets.kresd = rec {
68 wantedBy = [ "sockets.target" ];
69 before = wantedBy;
70 listenStreams = map
71 # Syntax depends on being IPv6 or IPv4.
72 (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
73 cfg.interfaces;
74 socketConfig.ListenDatagram = listenStreams;
75 };
76
77 systemd.sockets.kresd-control = rec {
78 wantedBy = [ "sockets.target" ];
79 before = wantedBy;
80 partOf = [ "kresd.socket" ];
81 listenStreams = [ "/run/kresd/control" ];
82 socketConfig = {
83 FileDescriptorName = "control";
84 Service = "kresd.service";
85 SocketMode = "0660"; # only root user/group may connect
86 };
87 };
88
89 # Create the cacheDir; tmpfiles don't work on nixos-rebuild switch.
90 systemd.services.kresd-cachedir = {
91 serviceConfig.Type = "oneshot";
92 script = ''
93 if [ ! -d '${cfg.cacheDir}' ]; then
94 mkdir -p '${cfg.cacheDir}'
95 chown kresd:kresd '${cfg.cacheDir}'
96 fi
97 '';
98 };
99
100 systemd.services.kresd = {
101 description = "Knot-resolver daemon";
102
103 serviceConfig = {
104 User = "kresd";
105 Type = "notify";
106 WorkingDirectory = cfg.cacheDir;
107 };
108
109 script = ''
110 exec '${package}/bin/kresd' --config '${configFile}' \
111 -k '${cfg.cacheDir}/root.key'
112 '';
113
114 after = [ "kresd-cachedir.service" ];
115 requires = [ "kresd.socket" "kresd-cachedir.service" ];
116 wantedBy = [ "sockets.target" ];
117 };
118 };
119}