Unverified Commit aaca7a18 authored by Vladimír Čunát's avatar Vladimír Čunát
Browse files

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

parents 887207cf ec89463a
Loading
Loading
Loading
Loading
+95 −14
Original line number Diff line number Diff line
{ 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";
      };
+7 −4
Original line number Diff line number Diff line
@@ -114,11 +114,14 @@ in {
      services.knot.extraArgs = [ "-v" ];
      services.knot.settings = {
        server = {
          automatic-acl = true;
        };

        xdp = {
          listen = [
            "0.0.0.0@53"
            "::@53"
            "eth1"
          ];
          automatic-acl = true;
          tcp = true;
        };

        remote.primary = {
@@ -140,7 +143,7 @@ in {
          "sub.example.com".file = "sub.example.com.zone";
        };

        log.syslog.any = "info";
        log.syslog.any = "debug";
      };
    };
    client = { lib, nodes, ... }: {