nixos-config/nixos/modules/networking/netns/common.nix

188 lines
6.1 KiB
Nix

{
config,
lib,
pkgs,
...
}:
with lib;
let
allNetns = config.networking.netns;
nonDefaultNetns = filterAttrs (name: _cfg: name != "default") allNetns;
in
{
options.networking.netns = mkOption {
type = types.attrsOf (
types.submodule (
{ name, config, ... }:
{
options = {
netnsPath = mkOption {
type = types.str;
default = if name == "default" then "/proc/1/ns/net" else "/run/netns/${name}";
readOnly = true;
description = ''
Path to the network namespace.
'';
};
mntnsPath = mkOption {
type = types.str;
default = if name == "default" then "/proc/1/ns/mnt" else "/run/netns-${name}/mntns/${name}";
readOnly = true;
description = ''
Path to the auxiliary mount namespace.
'';
};
interface = mkOption {
type = types.str;
default = name;
description = ''
Name of the dummy interface to add the address.
'';
};
address = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Address to be added into the network namespace as source address.
'';
};
enableIPv4Forwarding = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable IPv4 packet forwarding in the network namespace.
'';
};
enableIPv6Forwarding = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable IPv6 packet forwarding in the network namespace.
'';
};
serviceConfig = mkOption {
type = types.attrs;
default =
if name == "default" then
{ }
else
{
NetworkNamespacePath = config.netnsPath;
BindReadOnlyPaths = optionals config.enableDNSIsolation [
"/etc/netns/${name}/resolv.conf:/etc/resolv.conf:norbind"
"/etc/netns/${name}/nsswitch.conf:/etc/nsswitch.conf:norbind"
"/run/netns-${name}/nscd:/run/nscd:norbind"
];
};
readOnly = true;
description = ''
Systemd service configuration for entering the network namespace.
'';
};
};
}
)
);
description = ''
Network namespace configuration.
'';
};
config = {
networking.netns.default = { };
systemd.services = listToAttrs (
mapAttrsToList (
name: cfg:
let
inherit (cfg) interface address;
enableIPv4Forwarding = if cfg.enableIPv4Forwarding then "1" else "0";
enableIPv6Forwarding = if cfg.enableIPv6Forwarding then "1" else "0";
in
nameValuePair "netns-${name}" {
path = with pkgs; [
coreutils
iproute2
procps
];
script = ''
ip netns add ${name}
ip -n ${name} link add ${interface} type dummy
ip -n ${name} link set lo up
ip -n ${name} link set ${interface} up
ip netns exec ${name} sysctl -w net.ipv4.conf.default.forwarding=${enableIPv4Forwarding}
ip netns exec ${name} sysctl -w net.ipv4.conf.all.forwarding=${enableIPv4Forwarding}
ip netns exec ${name} sysctl -w net.ipv6.conf.default.forwarding=${enableIPv6Forwarding}
ip netns exec ${name} sysctl -w net.ipv6.conf.all.forwarding=${enableIPv6Forwarding}
${concatMapStringsSep "\n" (addr: "ip -n ${name} addr add ${addr} dev ${interface}") address}
'';
preStop = ''
ip netns del ${name}
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
}
) nonDefaultNetns
++ mapAttrsToList (
name: cfg:
let
inherit (cfg) mntnsPath enableDNSIsolation;
in
nameValuePair "netns-${name}-mntns" {
path = with pkgs; [
coreutils
util-linux
];
script = ''
touch ${mntnsPath} || echo "${mntnsPath} already exists"
unshare --mount=${mntnsPath} --propagation slave true
${optionalString enableDNSIsolation ''
nsenter --mount=${mntnsPath} mount --bind --read-only /etc/netns/${name}/resolv.conf /etc/resolv.conf
nsenter --mount=${mntnsPath} mount --bind --read-only /etc/netns/${name}/nsswitch.conf /etc/nsswitch.conf
nsenter --mount=${mntnsPath} mount --bind --read-only /run/netns-${name}/nscd /run/nscd
''}
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
RuntimeDirectory = "netns-${name}/mntns";
};
after =
[
"netns-${name}.service"
"network.target"
]
++ optionals enableDNSIsolation [
"netns-${name}-nscd.service"
];
partOf = [ "netns-${name}.service" ];
wantedBy = [
"multi-user.target"
"netns-${name}.service"
];
}
) nonDefaultNetns
);
environment.systemPackages = mkIf (nonDefaultNetns != { }) (
mapAttrsToList (
name: cfg:
let
inherit (cfg) netnsPath mntnsPath;
in
pkgs.writeShellApplication {
name = "netns-run-${name}";
runtimeInputs = with pkgs; [ util-linux ];
text = ''
pkexec nsenter -t $$ -e --mount=${mntnsPath} --net=${netnsPath} -S "$(id -u)" -G "$(id -g)" --wdns="$PWD" "$@"
'';
}
) allNetns
);
};
}