basic netns management

This commit is contained in:
Robin Appelman 2025-10-30 18:16:28 +01:00
commit a4c7b3c1c9
17 changed files with 1555 additions and 0 deletions

73
nix/module.nix Normal file
View file

@ -0,0 +1,73 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.networking.netnsd;
hasNamespaces = cfg.namespaces != {};
format = pkgs.formats.toml {};
configFile = format.generate "netnsd.toml" {
inherit (cfg) namespaces;
};
in {
options.networking.netnsd = {
package = mkOption {
type = types.package;
description = "package to use";
};
namespaces = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "target port inside the namespace";
};
forward = mkOption {
type = types.listOf (types.submodule ({config, ...}: {
options = {
source = mkOption {
type = types.oneOf[types.port types.str];
default = config.destination;
defaultText = "<destination>";
description = "source port, address or socket outside the namespace";
};
destination = mkOption {
type = types.oneOf[types.port types.str];
description = "target port or address inside the namespace";
};
};
}));
description = "ports to forward into the namespace";
};
};
}));
description = "namespaces to setup";
};
};
config = mkIf hasNamespaces {
# symlink instead of passing `configFile` directly to netnsd to allow changing the config without changing the path
environment.etc."netnsd/netnsd.toml".source = configFile;
systemd.services.netcsctl = {
reloadTriggers = [configFile];
wantedBy = ["multi-user.target"];
serviceConfig = {
Restart = "on-failure";
Type = "notify";
ExecStart = "${getExec cfg.pkg} daemon -c /etc/netnsd/netnsd.toml";
ExecReload = "${getExec cfg.pkg} reload";
PrivateTmp = true;
ProtectSystem = "full";
ProtectHome = true;
NoNewPrivileges = true;
};
};
};
}