about summary refs log tree commit diff
path: root/nixos/modules/services/networking
diff options
context:
space:
mode:
authorMartin Weinelt <hexa@darmstadt.ccc.de>2024-02-06 13:25:54 +0100
committerMartin Weinelt <hexa@darmstadt.ccc.de>2024-02-13 13:44:31 +0100
commitd1d8dd3e55ee84f188bc543bf262f29da65dde3e (patch)
tree49304af6048320df78adf30f1f8dbb3d17f52a95 /nixos/modules/services/networking
parent29f575d7e680cae88c1863fb0a029accd0a1a01d (diff)
downloadnixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.tar
nixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.tar.gz
nixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.tar.bz2
nixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.tar.lz
nixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.tar.xz
nixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.tar.zst
nixlib-d1d8dd3e55ee84f188bc543bf262f29da65dde3e.zip
nixos/knot: add support for XDP setups
The Express Data Path (XDP) is a way to circumvent the traditional Linux
networking stack and instead run an eBPF program on your NIC, that makes
the decision to provide Knot with certain packets. This is way faster
and more scalable but comes at the cost of reduced introspection.

Unfortunately the `knotc conf-check` command fails hard with missing
interfaces or IP addresses configured in `xdp.listen`, so we disable it
for now, once the `xdp` config section is set. We also promote the config
check condition to a proper option, so our conditions become public
documentation, and we allow users to deal with corner cases, that we have
not thought of yet.

We follow the pre-requisites documented in the Knot 3.3 manual, and set
up the required capabilities and allow the AF_XDP address family.

But on top of that, due to our strict hardening, we found two more
requirements, that were communicated upstream while debugging this.

- There is a requirement on AF_NETLINK, likely to query for and configure
  the relevant network interface
- Running eBPF programs requires access to the `bpf` syscall, which we
  deny through the `~@privileged` configuration.

In summary We now conditionally loosen the hardening of the unit once we
detect that an XDP configuration is wanted. And since we cannot
introspect arbitrary files from the `settingsFiles` option, we expose XDP
support through the `enableXDP` toggle option on the module.
Diffstat (limited to 'nixos/modules/services/networking')
-rw-r--r--nixos/modules/services/networking/knot.nix57
1 files changed, 52 insertions, 5 deletions
diff --git a/nixos/modules/services/networking/knot.nix b/nixos/modules/services/networking/knot.nix
index 94c32586736a..1646e4c491b0 100644
--- a/nixos/modules/services/networking/knot.nix
+++ b/nixos/modules/services/networking/knot.nix
@@ -113,8 +113,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
     '';
   };
@@ -144,6 +143,39 @@ in {
     services.knot = {
       enable = mkEnableOption (lib.mdDoc "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 = [];
@@ -210,7 +242,17 @@ 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";
@@ -219,10 +261,10 @@ in {
 
         AmbientCapabilities = [
           "CAP_NET_BIND_SERVICE"
-        ];
+        ] ++ xdpCapabilities;
         CapabilityBoundingSet = [
           "CAP_NET_BIND_SERVICE"
-        ];
+        ] ++ xdpCapabilities;
         DeviceAllow = "";
         DevicePolicy = "closed";
         LockPersonality = true;
@@ -247,6 +289,9 @@ in {
           "AF_INET"
           "AF_INET6"
           "AF_UNIX"
+        ] ++ lib.optionals (cfg.enableXDP) [
+          "AF_NETLINK"
+          "AF_XDP"
         ];
         RestrictNamespaces = true;
         RestrictRealtime =true;
@@ -258,6 +303,8 @@ in {
         SystemCallFilter = [
           "@system-service"
           "~@privileged"
+        ] ++ optionals (cfg.enableXDP) [
+          "bpf"
         ];
         UMask = "0077";
       };