···
cfg = config.services.knot;
8
-
configFile = pkgs.writeTextFile {
9
+
result = assert secsCheck; nix2yaml cfg.settings;
11
+
secAllow = n: hasPrefix "mod-" n || elem n [
13
+
"server" "xdp" "control"
15
+
"statistics" "database"
16
+
"keystore" "key" "remote" "remotes" "acl" "submission" "policy"
22
+
secsBad = filter (n: !secAllow n) (attrNames cfg.settings);
23
+
in if secsBad == [] then true else throw
24
+
("services.knot.settings contains unknown sections: " + toString secsBad);
26
+
nix2yaml = nix_def: concatStrings (
27
+
# We output the config section in the upstream-mandated order.
28
+
# Ordering is important due to forward-references not being allowed.
29
+
# See definition of conf_export and 'const yp_item_t conf_schema'
30
+
# upstream for reference. Last updated for 3.3.
31
+
# When changing the set of sections, also update secAllow above.
32
+
[ (sec_list_fa "id" nix_def "module") ]
33
+
++ map (sec_plain nix_def)
34
+
[ "server" "xdp" "control" ]
35
+
++ [ (sec_list_fa "target" nix_def "log") ]
36
+
++ map (sec_plain nix_def)
37
+
[ "statistics" "database" ]
38
+
++ map (sec_list_fa "id" nix_def)
39
+
[ "keystore" "key" "remote" "remotes" "acl" "submission" "policy" ]
41
+
# Export module sections before the template section.
42
+
++ map (sec_list_fa "id" nix_def) (filter (hasPrefix "mod-") (attrNames nix_def))
44
+
++ [ (sec_list_fa "id" nix_def "template") ]
45
+
++ [ (sec_list_fa "domain" nix_def "zone") ]
46
+
++ [ (sec_plain nix_def "include") ]
49
+
# A plain section contains directly attributes (we don't really check that ATM).
50
+
sec_plain = nix_def: sec_name: if !hasAttr sec_name nix_def then "" else
51
+
n2y "" { ${sec_name} = nix_def.${sec_name}; };
53
+
# This section contains a list of attribute sets. In each of the sets
54
+
# there's an attribute (`fa_name`, typically "id") that must exist and come first.
55
+
# Alternatively we support using attribute sets instead of lists; example diff:
56
+
# -template = [ { id = "default"; /* other attributes */ } { id = "foo"; } ]
57
+
# +template = { default = { /* those attributes */ }; foo = { }; }
58
+
sec_list_fa = fa_name: nix_def: sec_name: if !hasAttr sec_name nix_def then "" else
60
+
elem2yaml = fa_val: other_attrs:
61
+
" - " + n2y "" { ${fa_name} = fa_val; }
62
+
+ " " + n2y " " other_attrs
64
+
sec = nix_def.${sec_name};
68
+
then flip concatMapStrings sec
69
+
(elem: elem2yaml elem.${fa_name} (removeAttrs elem [ fa_name ]))
70
+
else concatStrings (mapAttrsToList elem2yaml sec)
73
+
# This convertor doesn't care about ordering of attributes.
74
+
# TODO: it could probably be simplified even more, now that it's not
75
+
# to be used directly, but we might want some other tweaks, too.
77
+
if doRecurse val then concatStringsSep "\n${indent}"
79
+
# This is a bit wacky - set directly under a set would start on bad indent,
80
+
# so we start those on a new line, but not other types of attribute values.
81
+
(aname: aval: "${aname}:${if doRecurse aval then "\n${indent} " else " "}"
82
+
+ n2y (indent + " ") aval)
88
+
if isList val && stringLength indent < 4 then concatMapStrings
89
+
(elem: "\n${indent}- " + n2y (indent + " ") elem)
93
+
if isList val /* and long indent */ then
94
+
"[ " + concatMapStringsSep ", " quoteString val + " ]" else
95
+
if isBool val then (if val then "on" else "off") else
98
+
# We don't want paths like ./my-zone.txt be converted to plain strings.
99
+
quoteString = s: ''"${if builtins.typeOf s == "path" then s else toString s}"'';
100
+
# We don't want to walk the insides of derivation attributes.
101
+
doRecurse = val: isAttrs val && !isDerivation val;
105
+
configFile = if cfg.settingsFile != null then
106
+
assert cfg.settings == {} && cfg.keyFiles == [];
108
+
else pkgs.writeTextFile {
10
-
text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" +
110
+
text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + yamlConfig;
111
+
# TODO: maybe we could do some checks even when private keys complicate this?
checkPhase = lib.optionalString (cfg.keyFiles == []) ''
${cfg.package}/bin/knotc --config=$out conf-check
···
63
-
extraConfig = mkOption {
163
+
settings = mkOption {
164
+
type = types.attrs;
description = lib.mdDoc ''
67
-
Extra lines to be added verbatim to knot.conf
167
+
Extra configuration as nix values.
171
+
settingsFile = mkOption {
172
+
type = types.nullOr types.path;
174
+
description = lib.mdDoc ''
175
+
As alternative to ``settings``, you can provide whole configuration
176
+
directly in the almost-YAML format of Knot DNS.
177
+
You might want to utilize ``writeTextFile`` for this.
···
192
+
# Compatibility with NixOS 23.05. At least partial, as it fails assert if used with keyFiles.
193
+
(mkChangedOptionModule [ "services" "knot" "extraConfig" ] [ "services" "knot" "settingsFile" ]
194
+
(config: pkgs.writeText "knot.conf" config.services.knot.extraConfig)
config = mkIf config.services.knot.enable {
···
description = "Knot daemon user";
206
+
environment.etc."knot/knot.conf".source = configFile; # just for user's convenience
systemd.services.knot = {
unitConfig.Documentation = "man:knotd(8) man:knot.conf(5) man:knotc(8) https://www.knot-dns.cz/docs/${cfg.package.version}/html/";