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
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;
yamlConfig = let
@ -113,8 +141,7 @@ let
mkConfigFile = configString: pkgs.writeTextFile {
name = "knot.conf";
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.keyFiles == []) ''
checkPhase = lib.optionalString cfg.checkConfig ''
${cfg.package}/bin/knotc --config=$out conf-check
'';
};
@ -142,12 +169,45 @@ let
in {
options = {
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 {
type = types.listOf types.str;
default = [];
description = lib.mdDoc ''
description = ''
List of additional command line parameters for knotd
'';
};
@ -155,7 +215,7 @@ in {
keyFiles = mkOption {
type = types.listOf types.path;
default = [];
description = lib.mdDoc ''
description = ''
A list of files containing additional configuration
to be included using the include directive. This option
allows to include configuration like TSIG keys without
@ -168,7 +228,7 @@ in {
settings = mkOption {
type = types.attrs;
default = {};
description = lib.mdDoc ''
description = ''
Extra configuration as nix values.
'';
};
@ -176,7 +236,7 @@ in {
settingsFile = mkOption {
type = types.nullOr types.path;
default = null;
description = lib.mdDoc ''
description = ''
As alternative to ``settings``, you can provide whole configuration
directly in the almost-YAML format of Knot DNS.
You might want to utilize ``pkgs.writeText "knot.conf" "longConfigString"`` for this.
@ -210,19 +270,35 @@ in {
wants = [ "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";
ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}";
ExecReload = "${knot-cli-wrappers}/bin/knotc reload";
ExecStart = escapeSystemdExecArgs ([
(lib.getExe cfg.package)
"--config=${configFile}"
"--socket=${socketFile}"
] ++ cfg.extraArgs);
ExecReload = escapeSystemdExecArgs [
"${knot-cli-wrappers}/bin/knotc" "reload"
];
User = "knot";
Group = "knot";
AmbientCapabilities = [
"CAP_NET_BIND_SERVICE"
];
] ++ xdpCapabilities;
CapabilityBoundingSet = [
"CAP_NET_BIND_SERVICE"
];
] ++ xdpCapabilities;
DeviceAllow = "";
DevicePolicy = "closed";
LockPersonality = true;
@ -247,6 +323,9 @@ in {
"AF_INET"
"AF_INET6"
"AF_UNIX"
] ++ optionals (cfg.enableXDP) [
"AF_NETLINK"
"AF_XDP"
];
RestrictNamespaces = true;
RestrictRealtime =true;
@ -258,6 +337,8 @@ in {
SystemCallFilter = [
"@system-service"
"~@privileged"
] ++ optionals (cfg.enableXDP) [
"bpf"
];
UMask = "0077";
};

View file

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