Merge #286596: nixos/knot: add support for XDP setups

This commit is contained in:
Vladimír Čunát 2024-02-15 09:19:59 +01:00
commit aaca7a186f
No known key found for this signature in database
GPG Key ID: E747DF1F9575A3AA
2 changed files with 103 additions and 19 deletions

View File

@ -1,8 +1,36 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, utils, ... }:
with lib;
let let
inherit (lib)
attrNames
concatMapStrings
concatMapStringsSep
concatStrings
concatStringsSep
elem
filter
flip
hasAttr
hasPrefix
isAttrs
isBool
isDerivation
isList
mapAttrsToList
mkChangedOptionModule
mkEnableOption
mkIf
mkOption
mkPackageOption
optionals
types
;
inherit (utils)
escapeSystemdExecArgs
;
cfg = config.services.knot; cfg = config.services.knot;
yamlConfig = let yamlConfig = let
@ -113,8 +141,7 @@ let
mkConfigFile = configString: pkgs.writeTextFile { mkConfigFile = configString: pkgs.writeTextFile {
name = "knot.conf"; name = "knot.conf";
text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + configString; text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + configString;
# TODO: maybe we could do some checks even when private keys complicate this? checkPhase = lib.optionalString cfg.checkConfig ''
checkPhase = lib.optionalString (cfg.keyFiles == []) ''
${cfg.package}/bin/knotc --config=$out conf-check ${cfg.package}/bin/knotc --config=$out conf-check
''; '';
}; };
@ -142,12 +169,45 @@ let
in { in {
options = { options = {
services.knot = { services.knot = {
enable = mkEnableOption (lib.mdDoc "Knot authoritative-only DNS server"); enable = mkEnableOption "Knot authoritative-only DNS server";
enableXDP = mkOption {
type = types.bool;
default = lib.hasAttrByPath [ "xdp" "listen" ] cfg.settings;
defaultText = ''
Enabled when the `xdp.listen` setting is configured through `settings`.
'';
example = true;
description = ''
Extends the systemd unit with permissions to allow for the use of
the eXpress Data Path (XDP).
::: {.note}
Make sure to read up on functional [limitations](https://www.knot-dns.cz/docs/latest/singlehtml/index.html#mode-xdp-limitations)
when running in XDP mode.
:::
'';
};
checkConfig = mkOption {
type = types.bool;
# TODO: maybe we could do some checks even when private keys complicate this?
# conf-check fails hard on missing IPs/devices with XDP
default = cfg.keyFiles == [] && !cfg.enableXDP;
defaultText = ''
Disabled when the config uses `keyFiles` or `enableXDP`.
'';
example = false;
description = ''
Toggles the configuration test at build time. It runs in a
sandbox, and therefore cannot be used in all scenarios.
'';
};
extraArgs = mkOption { extraArgs = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [];
description = lib.mdDoc '' description = ''
List of additional command line parameters for knotd List of additional command line parameters for knotd
''; '';
}; };
@ -155,7 +215,7 @@ in {
keyFiles = mkOption { keyFiles = mkOption {
type = types.listOf types.path; type = types.listOf types.path;
default = []; default = [];
description = lib.mdDoc '' description = ''
A list of files containing additional configuration A list of files containing additional configuration
to be included using the include directive. This option to be included using the include directive. This option
allows to include configuration like TSIG keys without allows to include configuration like TSIG keys without
@ -168,7 +228,7 @@ in {
settings = mkOption { settings = mkOption {
type = types.attrs; type = types.attrs;
default = {}; default = {};
description = lib.mdDoc '' description = ''
Extra configuration as nix values. Extra configuration as nix values.
''; '';
}; };
@ -176,7 +236,7 @@ in {
settingsFile = mkOption { settingsFile = mkOption {
type = types.nullOr types.path; type = types.nullOr types.path;
default = null; default = null;
description = lib.mdDoc '' description = ''
As alternative to ``settings``, you can provide whole configuration As alternative to ``settings``, you can provide whole configuration
directly in the almost-YAML format of Knot DNS. directly in the almost-YAML format of Knot DNS.
You might want to utilize ``pkgs.writeText "knot.conf" "longConfigString"`` for this. You might want to utilize ``pkgs.writeText "knot.conf" "longConfigString"`` for this.
@ -210,19 +270,35 @@ in {
wants = [ "network.target" ]; wants = [ "network.target" ];
after = ["network.target" ]; after = ["network.target" ];
serviceConfig = { serviceConfig = let
# https://www.knot-dns.cz/docs/3.3/singlehtml/index.html#pre-requisites
xdpCapabilities = lib.optionals (cfg.enableXDP) [
"CAP_NET_ADMIN"
"CAP_NET_RAW"
"CAP_SYS_ADMIN"
"CAP_IPC_LOCK"
] ++ lib.optionals (lib.versionOlder config.boot.kernelPackages.kernel.version "5.11") [
"CAP_SYS_RESOURCE"
];
in {
Type = "notify"; Type = "notify";
ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}"; ExecStart = escapeSystemdExecArgs ([
ExecReload = "${knot-cli-wrappers}/bin/knotc reload"; (lib.getExe cfg.package)
"--config=${configFile}"
"--socket=${socketFile}"
] ++ cfg.extraArgs);
ExecReload = escapeSystemdExecArgs [
"${knot-cli-wrappers}/bin/knotc" "reload"
];
User = "knot"; User = "knot";
Group = "knot"; Group = "knot";
AmbientCapabilities = [ AmbientCapabilities = [
"CAP_NET_BIND_SERVICE" "CAP_NET_BIND_SERVICE"
]; ] ++ xdpCapabilities;
CapabilityBoundingSet = [ CapabilityBoundingSet = [
"CAP_NET_BIND_SERVICE" "CAP_NET_BIND_SERVICE"
]; ] ++ xdpCapabilities;
DeviceAllow = ""; DeviceAllow = "";
DevicePolicy = "closed"; DevicePolicy = "closed";
LockPersonality = true; LockPersonality = true;
@ -247,6 +323,9 @@ in {
"AF_INET" "AF_INET"
"AF_INET6" "AF_INET6"
"AF_UNIX" "AF_UNIX"
] ++ optionals (cfg.enableXDP) [
"AF_NETLINK"
"AF_XDP"
]; ];
RestrictNamespaces = true; RestrictNamespaces = true;
RestrictRealtime =true; RestrictRealtime =true;
@ -258,6 +337,8 @@ in {
SystemCallFilter = [ SystemCallFilter = [
"@system-service" "@system-service"
"~@privileged" "~@privileged"
] ++ optionals (cfg.enableXDP) [
"bpf"
]; ];
UMask = "0077"; UMask = "0077";
}; };

View File

@ -114,13 +114,16 @@ in {
services.knot.extraArgs = [ "-v" ]; services.knot.extraArgs = [ "-v" ];
services.knot.settings = { services.knot.settings = {
server = { server = {
listen = [
"0.0.0.0@53"
"::@53"
];
automatic-acl = true; automatic-acl = true;
}; };
xdp = {
listen = [
"eth1"
];
tcp = true;
};
remote.primary = { remote.primary = {
address = "192.168.0.1@53"; address = "192.168.0.1@53";
key = "xfr_key"; key = "xfr_key";
@ -140,7 +143,7 @@ in {
"sub.example.com".file = "sub.example.com.zone"; "sub.example.com".file = "sub.example.com.zone";
}; };
log.syslog.any = "info"; log.syslog.any = "debug";
}; };
}; };
client = { lib, nodes, ... }: { client = { lib, nodes, ... }: {