services/enthalpy: complete migration to networking.netns

This commit is contained in:
Lu Wang 2024-12-08 16:34:34 +08:00
parent e173247742
commit aff0796cd3
Signed by: rebmit
SSH key fingerprint: SHA256:3px8QV1zEerIrEWHaqtH5rR9kjetyRST5EipOPrd+bU
16 changed files with 181 additions and 345 deletions

View file

@ -1,12 +1,15 @@
{ profiles, lib, ... }: {
config,
profiles,
lib,
...
}:
{ {
imports = with profiles; [ imports = with profiles; [
services.enthalpy services.enthalpy
]; ];
services.enthalpy = { services.enthalpy = {
users.rebmit = { };
services.nix-daemon = { };
ipsec.interfaces = [ "enp14s0" ]; ipsec.interfaces = [ "enp14s0" ];
clat = { clat = {
enable = true; enable = true;
@ -15,6 +18,19 @@
gost.enable = true; gost.enable = true;
}; };
systemd.services.nix-daemon = {
serviceConfig = config.networking.netns.enthalpy.serviceConfig;
after = [ "netns-enthalpy.service" ];
requires = [ "netns-enthalpy.service" ];
};
systemd.services."user@${toString config.users.users.rebmit.uid}" = {
overrideStrategy = "asDropin";
serviceConfig = config.networking.netns.enthalpy.serviceConfig;
after = [ "netns-enthalpy.service" ];
requires = [ "netns-enthalpy.service" ];
};
systemd.network = { systemd.network = {
enable = true; enable = true;
wait-online.anyInterface = true; wait-online.anyInterface = true;

View file

@ -1,12 +1,15 @@
{ profiles, lib, ... }: {
config,
profiles,
lib,
...
}:
{ {
imports = with profiles; [ imports = with profiles; [
services.enthalpy services.enthalpy
]; ];
services.enthalpy = { services.enthalpy = {
users.rebmit = { };
services.nix-daemon = { };
ipsec = { ipsec = {
interfaces = [ "wlan0" ]; interfaces = [ "wlan0" ];
whitelist = [ "rebmit's edge network" ]; whitelist = [ "rebmit's edge network" ];
@ -18,6 +21,19 @@
gost.enable = true; gost.enable = true;
}; };
systemd.services.nix-daemon = {
serviceConfig = config.networking.netns.enthalpy.serviceConfig;
after = [ "netns-enthalpy.service" ];
requires = [ "netns-enthalpy.service" ];
};
systemd.services."user@${toString config.users.users.rebmit.uid}" = {
overrideStrategy = "asDropin";
serviceConfig = config.networking.netns.enthalpy.serviceConfig;
after = [ "netns-enthalpy.service" ];
requires = [ "netns-enthalpy.service" ];
};
systemd.network = { systemd.network = {
enable = true; enable = true;
wait-online.anyInterface = true; wait-online.anyInterface = true;

View file

@ -1,7 +1,6 @@
{ {
profiles, profiles,
data, data,
lib,
... ...
}: }:
{ {
@ -57,12 +56,6 @@
dhcpV6Config.RouteMetric = 1024; dhcpV6Config.RouteMetric = 1024;
ipv6AcceptRAConfig.RouteMetric = 1024; ipv6AcceptRAConfig.RouteMetric = 1024;
}; };
"50-enthalpy" = {
routes = lib.singleton {
Destination = data.enthalpy_network_prefix;
Gateway = "fe80::ff:fe00:0";
};
};
}; };
}; };
} }

View file

@ -26,7 +26,7 @@ in
}; };
mntnsPath = mkOption { mntnsPath = mkOption {
type = types.str; type = types.str;
default = if name == "default" then "/proc/1/ns/mnt" else "/run/netns-${name}/mntns/${name}"; default = if name == "default" then "/proc/1/ns/mnt" else "/run/${name}/mntns/${name}";
readOnly = true; readOnly = true;
description = '' description = ''
Path to the auxiliary mount namespace. Path to the auxiliary mount namespace.
@ -71,7 +71,7 @@ in
BindReadOnlyPaths = optionals config.enableDNSIsolation [ BindReadOnlyPaths = optionals config.enableDNSIsolation [
"/etc/netns/${name}/resolv.conf:/etc/resolv.conf:norbind" "/etc/netns/${name}/resolv.conf:/etc/resolv.conf:norbind"
"/etc/netns/${name}/nsswitch.conf:/etc/nsswitch.conf:norbind" "/etc/netns/${name}/nsswitch.conf:/etc/nsswitch.conf:norbind"
"/run/netns-${name}/nscd:/run/nscd:norbind" "/run/${name}/nscd:/run/nscd:norbind"
]; ];
}; };
readOnly = true; readOnly = true;
@ -143,13 +143,13 @@ in
${optionalString enableDNSIsolation '' ${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}/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 /etc/netns/${name}/nsswitch.conf /etc/nsswitch.conf
nsenter --mount=${mntnsPath} mount --bind --read-only /run/netns-${name}/nscd /run/nscd nsenter --mount=${mntnsPath} mount --bind --read-only /run/${name}/nscd /run/nscd
''} ''}
''; '';
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
RuntimeDirectory = "netns-${name}/mntns"; RuntimeDirectory = "${name}/mntns";
}; };
after = after =
[ [

View file

@ -1,127 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
allNetns = config.networking.netns;
allExtraVeths = flatten (mapAttrsToList (_name: cfg: cfg.extraVeths) allNetns);
in
{
options.networking.netns = mkOption {
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
options.extraVeths = mkOption {
type = types.listOf (
types.submodule (
{ config, ... }:
{
options = {
sourceNetns = mkOption {
type = types.str;
default = name;
readOnly = true;
description = ''
The current network namespace.
'';
};
targetNetns = mkOption {
type = types.str;
description = ''
The network namespace to connect to.
'';
};
sourceInterface = mkOption {
type = types.str;
default = if config.targetNetns == "default" then "host" else config.targetNetns;
description = ''
The interface name in the current network namespace;
'';
};
targetInterface = mkOption {
type = types.str;
default = if config.sourceNetns == "default" then "host" else config.sourceNetns;
description = ''
The interface name in the other network namespace;
'';
};
};
}
)
);
default = [ ];
description = ''
Extra veth-pairs to be created for enabling link-scope connectivity
between inter-network namespaces.
Note that a veth-pair only needs to be defined on one end.
'';
};
}
)
);
};
config = {
systemd.services = listToAttrs (
map (
ev:
let
inherit (ev)
sourceNetns
targetNetns
sourceInterface
targetInterface
;
sourceNetnsPath = config.networking.netns.${sourceNetns}.netnsPath;
targetNetnsPath = config.networking.netns.${targetNetns}.netnsPath;
serviceDeps = map (ns: "netns-${ns}.service") (
filter (ns: ns != "default") [
sourceNetns
targetNetns
]
);
mkSetup =
netns: _netnsPath: interface:
if netns == "default" then
"ip link set ${interface} up"
else
"ip -n ${netns} link set ${interface} up";
mkDrop =
netns: _netnsPath: interface:
if netns == "default" then "ip link del ${interface}" else "ip -n ${netns} link del ${interface}";
in
nameValuePair "netns-extra-veth-1-${sourceNetns}-${targetNetns}" {
path = with pkgs; [
coreutils
iproute2
procps
];
script = ''
ip link add ${sourceInterface} mtu 1400 address 02:00:00:00:00:01 netns ${sourceNetnsPath} type veth \
peer ${targetInterface} mtu 1400 address 02:00:00:00:00:00 netns ${targetNetnsPath}
${mkSetup sourceNetns sourceNetnsPath sourceInterface}
${mkSetup targetNetns targetNetnsPath targetInterface}
'';
preStop = ''
${mkDrop sourceNetns sourceNetnsPath sourceInterface}
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
after = [
"network.target"
] ++ serviceDeps;
partOf = serviceDeps;
wantedBy = [
"multi-user.target"
] ++ serviceDeps;
}
) allExtraVeths
);
};
}

View file

@ -43,12 +43,12 @@ in
"/etc/netns/${name}/resolv.conf:/etc/resolv.conf:norbind" "/etc/netns/${name}/resolv.conf:/etc/resolv.conf:norbind"
"/etc/netns/${name}/nsswitch.conf:/etc/nsswitch.conf:norbind" "/etc/netns/${name}/nsswitch.conf:/etc/nsswitch.conf:norbind"
]; ];
BindPaths = [ "/run/netns-${name}/nscd:/run/nscd:norbind" ]; BindPaths = [ "/run/${name}/nscd:/run/nscd:norbind" ];
Type = "notify"; Type = "notify";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 5; RestartSec = 5;
User = "${name}-nscd"; User = "${name}-nscd";
RuntimeDirectory = "netns-${name}/nscd"; RuntimeDirectory = "${name}/nscd";
RuntimeDirectoryPreserve = true; RuntimeDirectoryPreserve = true;
ExecStart = "${pkgs.nsncd}/bin/nsncd"; ExecStart = "${pkgs.nsncd}/bin/nsncd";
}; };

View file

@ -84,7 +84,7 @@ in
] ]
); );
in in
nameValuePair "netns-${name}-port-forward-${toString index}" { nameValuePair "netns-${name}-port-forward-${toString index}-${netns}-${protocol}" {
serviceConfig = serviceConfig =
mylib.misc.serviceHardened mylib.misc.serviceHardened
// cfg.serviceConfig // cfg.serviceConfig

View file

@ -16,7 +16,7 @@ in
enable = mkEnableOption "bird for site-scope connectivity"; enable = mkEnableOption "bird for site-scope connectivity";
socket = mkOption { socket = mkOption {
type = types.str; type = types.str;
default = "/run/netns-${cfg.netns}/bird/bird.ctl"; default = "/run/enthalpy/bird/bird.ctl";
description = '' description = ''
Path to the bird control socket. Path to the bird control socket.
''; '';
@ -43,7 +43,7 @@ in
}; };
config = mkIf (cfg.enable && cfg.bird.enable) { config = mkIf (cfg.enable && cfg.bird.enable) {
environment.etc."netns/${cfg.netns}/bird.conf".source = pkgs.writeTextFile { environment.etc."enthalpy/bird/bird.conf".source = pkgs.writeTextFile {
name = "bird"; name = "bird";
text = cfg.bird.config; text = cfg.bird.config;
checkPhase = optionalString cfg.bird.checkConfig '' checkPhase = optionalString cfg.bird.checkConfig ''
@ -55,14 +55,14 @@ in
systemd.services.enthalpy-bird = { systemd.services.enthalpy-bird = {
serviceConfig = serviceConfig =
mylib.misc.serviceHardened mylib.misc.serviceHardened
// config.networking.netns.${cfg.netns}.serviceConfig // config.networking.netns.enthalpy.serviceConfig
// { // {
Type = "forking"; Type = "forking";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 5; RestartSec = 5;
DynamicUser = true; DynamicUser = true;
RuntimeDirectory = "netns-${cfg.netns}/bird"; RuntimeDirectory = "enthalpy/bird";
ExecStart = "${pkgs.bird}/bin/bird -s ${cfg.bird.socket} -c /etc/netns/${cfg.netns}/bird.conf"; ExecStart = "${pkgs.bird}/bin/bird -s ${cfg.bird.socket} -c /etc/enthalpy/bird/bird.conf";
ExecReload = "${pkgs.bird}/bin/birdc -s ${cfg.bird.socket} configure"; ExecReload = "${pkgs.bird}/bin/birdc -s ${cfg.bird.socket} configure";
ExecStop = "${pkgs.bird}/bin/birdc -s ${cfg.bird.socket} down"; ExecStop = "${pkgs.bird}/bin/birdc -s ${cfg.bird.socket} down";
CapabilityBoundingSet = [ CapabilityBoundingSet = [
@ -82,13 +82,13 @@ in
"AF_NETLINK" "AF_NETLINK"
]; ];
}; };
after = [ "netns-${cfg.netns}.service" ]; after = [ "netns-enthalpy.service" ];
partOf = [ "netns-${cfg.netns}.service" ]; partOf = [ "netns-enthalpy.service" ];
wantedBy = [ wantedBy = [
"multi-user.target" "multi-user.target"
"netns-${cfg.netns}.service" "netns-enthalpy.service"
]; ];
reloadTriggers = [ config.environment.etc."netns/${cfg.netns}/bird.conf".source ]; reloadTriggers = [ config.environment.etc."enthalpy/bird/bird.conf".source ];
}; };
services.enthalpy.bird.config = mkBefore '' services.enthalpy.bird.config = mkBefore ''

View file

@ -11,7 +11,7 @@ with lib;
let let
inherit (mylib.network) cidr; inherit (mylib.network) cidr;
cfg = config.services.enthalpy; cfg = config.services.enthalpy;
interface = config.networking.netns.${cfg.netns}.interface; interface = config.networking.netns.enthalpy.interface;
in in
{ {
options.services.enthalpy.clat = { options.services.enthalpy.clat = {
@ -61,7 +61,7 @@ in
''; '';
serviceConfig = serviceConfig =
mylib.misc.serviceHardened mylib.misc.serviceHardened
// config.networking.netns.${cfg.netns}.serviceConfig // config.networking.netns.enthalpy.serviceConfig
// { // {
Type = "forking"; Type = "forking";
Restart = "on-failure"; Restart = "on-failure";
@ -77,11 +77,11 @@ in
]; ];
PrivateDevices = false; PrivateDevices = false;
}; };
after = [ "netns-${cfg.netns}.service" ]; after = [ "netns-enthalpy.service" ];
partOf = [ "netns-${cfg.netns}.service" ]; partOf = [ "netns-enthalpy.service" ];
wantedBy = [ wantedBy = [
"multi-user.target" "multi-user.target"
"netns-${cfg.netns}.service" "netns-enthalpy.service"
]; ];
}; };
}; };

View file

@ -27,13 +27,6 @@ in
Address to be added into the enthalpy network as source address. Address to be added into the enthalpy network as source address.
''; '';
}; };
netns = mkOption {
type = types.str;
default = "enthalpy";
description = ''
Name of the network namespace for enthalpy interfaces.
'';
};
network = mkOption { network = mkOption {
type = types.str; type = types.str;
description = '' description = ''
@ -43,8 +36,8 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
networking.netns."${cfg.netns}" = { networking.netns.enthalpy = {
interface = cfg.netns; interface = "enthalpy";
address = singleton "${cfg.address}/128"; address = singleton "${cfg.address}/128";
enableIPv4Forwarding = false; enableIPv4Forwarding = false;
enableIPv6Forwarding = true; enableIPv6Forwarding = true;

View file

@ -10,10 +10,7 @@ with lib;
let let
cfg = config.services.enthalpy; cfg = config.services.enthalpy;
birdPrefix = filter (p: p.type == "bird") cfg.exit.prefix; birdPrefix = filter (p: p.type == "bird") cfg.exit.prefix;
staticPrefix = subtractLists birdPrefix cfg.exit.prefix; staticPrefix = filter (p: p.type == "static") cfg.exit.prefix;
staticRoutes = map (
p: "${p.destination} from ${p.source} via fe80::ff:fe00:1 dev enthalpy"
) staticPrefix;
in in
{ {
options.services.enthalpy.exit = { options.services.enthalpy.exit = {
@ -45,29 +42,53 @@ in
}; };
config = mkIf (cfg.enable && cfg.exit.enable) { config = mkIf (cfg.enable && cfg.exit.enable) {
systemd.network.networks."50-enthalpy" = {
matchConfig.Name = "enthalpy";
routes = singleton {
Destination = cfg.network;
Gateway = "fe80::ff:fe00:2";
};
linkConfig.RequiredForOnline = false;
};
services.enthalpy.bird.config = '' services.enthalpy.bird.config = ''
protocol static { protocol static {
ipv6 sadr; ipv6 sadr;
${ ${
concatMapStringsSep "\n" (p: '' concatMapStringsSep "\n" (p: ''
route ${p.destination} from ${p.source} via fe80::ff:fe00:1 dev "enthalpy"; route ${p.destination} from ${p.source} via fe80::ff:fe00:1 dev "host";
'') birdPrefix '') birdPrefix
} }
} }
''; '';
systemd.services.enthalpy-exit = mkIf (staticRoutes != [ ]) { systemd.services.enthalpy-exit = {
path = with pkgs; [
coreutils
iproute2
];
script = ''
ip link add enthalpy mtu 1400 address 02:00:00:00:00:01 type veth \
peer host mtu 1400 address 02:00:00:00:00:02 netns enthalpy
ip link set enthalpy up
ip -n enthalpy link set host up
${concatMapStringsSep "\n" (
p: "ip -n enthalpy -6 route add ${p.destination} from ${p.source} via fe80::ff:fe00:1 dev host"
) staticPrefix}
'';
preStop = ''
ip link del enthalpy
'';
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStart = builtins.map (route: "${pkgs.iproute2}/bin/ip -6 route add ${route}") staticRoutes;
ExecStop = builtins.map (route: "${pkgs.iproute2}/bin/ip -6 route del ${route}") staticRoutes;
}; };
after = [ "network.target" ]; after = [ "netns-enthalpy.service" ];
wants = [ "network.target" ]; partOf = [ "netns-enthalpy.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [
"multi-user.target"
"netns-enthalpy.service"
];
}; };
services.enthalpy.services.enthalpy-exit = mkIf (staticRoutes != [ ]) { };
}; };
} }

View file

@ -25,15 +25,15 @@ in
DynamicUser = true; DynamicUser = true;
ExecStart = "${pkgs.gost}/bin/gost -L=socks5://[::1]:${toString config.networking.ports.enthalpy-gost}"; ExecStart = "${pkgs.gost}/bin/gost -L=socks5://[::1]:${toString config.networking.ports.enthalpy-gost}";
}; };
after = [ "netns-${cfg.netns}.service" ]; after = [ "netns-enthalpy.service" ];
partOf = [ "netns-${cfg.netns}.service" ]; partOf = [ "netns-enthalpy.service" ];
wantedBy = [ wantedBy = [
"multi-user.target" "multi-user.target"
"netns-${cfg.netns}.service" "netns-enthalpy.service"
]; ];
}; };
networking.netns."${cfg.netns}".forwardPorts = singleton { networking.netns.enthalpy.forwardPorts = singleton {
protocol = "tcp"; protocol = "tcp";
netns = "default"; netns = "default";
source = "[::1]:${toString config.networking.ports.enthalpy-gost}"; source = "[::1]:${toString config.networking.ports.enthalpy-gost}";

View file

@ -4,6 +4,7 @@
config, config,
lib, lib,
pkgs, pkgs,
mylib,
... ...
}: }:
with lib; with lib;
@ -42,14 +43,6 @@ in
List of endpoints available on this node. List of endpoints available on this node.
''; '';
}; };
port = mkOption {
type = types.port;
default = config.networking.ports.enthalpy-ipsec;
readOnly = true;
description = ''
UDP port used by IKEv2. NAT-T is enabled by default.
'';
};
interfaces = mkOption { interfaces = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ ]; default = [ ];
@ -99,25 +92,25 @@ in
} }
]; ];
environment.systemPackages = with pkgs; [ strongswan ]; environment.etc."enthalpy/ranet/config.json".source =
(pkgs.formats.json { }).generate "enthalpy-ranet-config-json"
environment.etc."ranet/config.json".source = (pkgs.formats.json { }).generate "config.json" { {
organization = cfg.ipsec.organization; organization = cfg.ipsec.organization;
common_name = cfg.ipsec.commonName; common_name = cfg.ipsec.commonName;
endpoints = builtins.map (ep: { endpoints = builtins.map (ep: {
serial_number = ep.serialNumber; serial_number = ep.serialNumber;
address_family = ep.addressFamily; address_family = ep.addressFamily;
address = ep.address; address = ep.address;
port = cfg.ipsec.port; port = config.networking.ports.enthalpy-ipsec;
updown = pkgs.writeShellScript "updown" '' updown = pkgs.writeShellScript "updown" ''
LINK=enta$(printf '%08x\n' "$PLUTO_IF_ID_OUT") LINK=enta$(printf '%08x\n' "$PLUTO_IF_ID_OUT")
case "$PLUTO_VERB" in case "$PLUTO_VERB" in
up-client) up-client)
ip link add "$LINK" type xfrm if_id "$PLUTO_IF_ID_OUT" ip link add "$LINK" type xfrm if_id "$PLUTO_IF_ID_OUT"
ip link set "$LINK" netns ${cfg.netns} multicast on mtu 1400 up ip link set "$LINK" netns enthalpy multicast on mtu 1400 up
;; ;;
down-client) down-client)
ip -n ${cfg.netns} link del "$LINK" ip -n enthalpy link del "$LINK"
;; ;;
esac esac
''; '';
@ -130,7 +123,7 @@ in
charon { charon {
interfaces_use = ${strings.concatStringsSep "," cfg.ipsec.interfaces} interfaces_use = ${strings.concatStringsSep "," cfg.ipsec.interfaces}
port = 0 port = 0
port_nat_t = ${toString cfg.ipsec.port} port_nat_t = ${toString config.networking.ports.enthalpy-ipsec}
retransmit_base = 1 retransmit_base = 1
plugins { plugins {
socket-default { socket-default {
@ -168,7 +161,7 @@ in
'' ''
else else
cfg.ipsec.registry; cfg.ipsec.registry;
command = "ranet -c /etc/ranet/config.json -r ${registry} -k ${cfg.ipsec.privateKeyPath}"; command = "ranet -c /etc/enthalpy/ranet/config.json -r ${registry} -k ${cfg.ipsec.privateKeyPath}";
in in
{ {
path = with pkgs; [ path = with pkgs; [
@ -178,7 +171,7 @@ in
script = "${command} up"; script = "${command} up";
reload = "${command} up"; reload = "${command} up";
preStop = "${command} down"; preStop = "${command} down";
serviceConfig = { serviceConfig = mylib.misc.serviceHardened // {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
}; };
@ -189,16 +182,17 @@ in
"network-online.target" "network-online.target"
"strongswan-swanctl.service" "strongswan-swanctl.service"
]; ];
requires = [
"netns-enthalpy.service"
];
after = [ after = [
"network-online.target" "network-online.target"
"netns-enthalpy.service"
"strongswan-swanctl.service" "strongswan-swanctl.service"
];
partOf = [ "netns-enthalpy.service" ];
wantedBy = [
"multi-user.target"
"netns-enthalpy.service" "netns-enthalpy.service"
]; ];
wantedBy = [ "multi-user.target" ]; reloadTriggers = [ config.environment.etc."enthalpy/ranet/config.json".source ];
reloadTriggers = [ config.environment.etc."ranet/config.json".source ];
}; };
}; };
} }

View file

@ -4,6 +4,7 @@
config, config,
lib, lib,
pkgs, pkgs,
mylib,
... ...
}: }:
with lib; with lib;
@ -13,14 +14,6 @@ in
{ {
options.services.enthalpy.nat64 = { options.services.enthalpy.nat64 = {
enable = mkEnableOption "NAT64"; enable = mkEnableOption "NAT64";
table = mkOption {
type = types.int;
default = config.networking.routingTables.nat64;
readOnly = true;
description = ''
Routing table used for NAT64 entries.
'';
};
prefix = mkOption { prefix = mkOption {
type = types.str; type = types.str;
default = "64:ff9b::/96"; default = "64:ff9b::/96";
@ -38,19 +31,14 @@ in
}; };
config = mkIf (cfg.enable && cfg.nat64.enable) { config = mkIf (cfg.enable && cfg.nat64.enable) {
systemd.network.config = { systemd.network.config.networkConfig.IPv6Forwarding = true;
networkConfig = {
IPv6Forwarding = true;
ManageForeignRoutes = false;
};
};
systemd.network.networks."70-nat64" = { systemd.network.networks."70-nat64" = {
matchConfig.Name = "nat64"; matchConfig.Name = "nat64";
routes = [ routes = [
{ {
Destination = cfg.nat64.prefix; Destination = cfg.nat64.prefix;
Table = cfg.nat64.table; Table = config.networking.routingTables.nat64;
} }
{ Destination = cfg.nat64.dynamicPool; } { Destination = cfg.nat64.dynamicPool; }
]; ];
@ -59,7 +47,7 @@ in
}; };
systemd.services.enthalpy-nat64 = { systemd.services.enthalpy-nat64 = {
serviceConfig = { serviceConfig = mylib.misc.serviceHardened // {
Type = "forking"; Type = "forking";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = 5; RestartSec = 5;
@ -73,25 +61,19 @@ in
''}"; ''}";
CapabilityBoundingSet = [ "CAP_NET_ADMIN" ]; CapabilityBoundingSet = [ "CAP_NET_ADMIN" ];
AmbientCapabilities = [ "CAP_NET_ADMIN" ]; AmbientCapabilities = [ "CAP_NET_ADMIN" ];
ProtectSystem = "full"; PrivateDevices = false;
ProtectHome = "yes";
ProtectKernelTunables = true;
ProtectControlGroups = true;
PrivateTmp = true;
SystemCallFilter = "~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io";
MemoryDenyWriteExecute = "yes";
}; };
wants = [ "network.target" ]; after = [ "netns-enthalpy.service" ];
after = [ partOf = [ "netns-enthalpy.service" ];
wantedBy = [
"multi-user.target"
"netns-enthalpy.service" "netns-enthalpy.service"
"network.target"
]; ];
requires = [ "netns-enthalpy.service" ];
wantedBy = [ "multi-user.target" ];
}; };
networking.nftables.enable = true; networking.nftables = {
networking.nftables.tables.enthalpy4 = { enable = true;
tables.enthalpy4 = {
family = "ip"; family = "ip";
content = '' content = ''
chain forward { chain forward {
@ -101,4 +83,5 @@ in
''; '';
}; };
}; };
};
} }

View file

@ -1,63 +0,0 @@
# Portions of this file are sourced from
# https://github.com/NickCao/flakes/blob/3b03efb676ea602575c916b2b8bc9d9cd13b0d85/modules/gravity/default.nix
{
config,
lib,
...
}:
with lib;
let
cfg = config.services.enthalpy;
in
{
options.services.enthalpy = {
services = mkOption {
type = types.attrsOf (
types.submodule {
options = {
overrideStrategy = mkOption {
type = types.str;
default = "asDropinIfExists";
};
};
}
);
default = { };
description = ''
Services that need to run inside the enthalpy network namespace.
'';
};
users = mkOption {
type = types.attrsOf (types.submodule { });
default = { };
description = ''
Users utilizing the enthalpy network namespace.
'';
};
};
config = mkIf cfg.enable (mkMerge [
{
systemd.services = mapAttrs (_name: value: {
inherit (value) overrideStrategy;
serviceConfig = {
NetworkNamespacePath = "/run/netns/${cfg.netns}";
BindReadOnlyPaths = [
"/etc/netns/${cfg.netns}/resolv.conf:/etc/resolv.conf:norbind"
"/etc/netns/${cfg.netns}/nsswitch.conf:/etc/nsswitch.conf:norbind"
"/run/netns-${cfg.netns}/nscd:/run/nscd:norbind"
];
};
after = [ "netns-enthalpy.service" ];
requires = [ "netns-enthalpy.service" ];
}) cfg.services;
services.enthalpy.services = mapAttrs' (
name: _value:
nameValuePair "user@${toString config.users.users.${name}.uid}" {
overrideStrategy = "asDropin";
}
) cfg.users;
}
]);
}

View file

@ -24,8 +24,11 @@ in
}; };
actions = mkOption { actions = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = [ default =
[
"${cidr.host 1 cfg.srv6.prefix} encap seg6local action End.DT6 table main dev enthalpy table localsid" "${cidr.host 1 cfg.srv6.prefix} encap seg6local action End.DT6 table main dev enthalpy table localsid"
]
++ optionals cfg.nat64.enable [
"${cidr.host 2 cfg.srv6.prefix} encap seg6local action End.DT6 table nat64 dev enthalpy table localsid" "${cidr.host 2 cfg.srv6.prefix} encap seg6local action End.DT6 table nat64 dev enthalpy table localsid"
]; ];
description = '' description = ''
@ -57,27 +60,34 @@ in
}; };
}; };
services.enthalpy.exit = {
enable = true;
prefix = singleton {
type = "static";
destination = cfg.srv6.prefix;
source = cfg.network;
};
};
systemd.services.enthalpy-srv6 = { systemd.services.enthalpy-srv6 = {
path = with pkgs; [ iproute2 ]; path = with pkgs; [
iproute2
];
script = concatMapStringsSep "\n" (p: "ip -6 route add ${p}") cfg.srv6.actions;
preStop = concatMapStringsSep "\n" (p: "ip -6 route del ${p}") cfg.srv6.actions;
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStartPre = [
"${pkgs.iproute2}/bin/ip -n ${cfg.netns} -6 r a ${cfg.srv6.prefix} from ${cfg.network} via fe80::ff:fe00:1 dev enthalpy"
];
ExecStart = builtins.map (route: "${pkgs.iproute2}/bin/ip -6 r a ${route}") cfg.srv6.actions;
ExecStop = builtins.map (route: "${pkgs.iproute2}/bin/ip -6 r d ${route}") cfg.srv6.actions;
ExecStopPost = [
"${pkgs.iproute2}/bin/ip -n ${cfg.netns} -6 r d ${cfg.srv6.prefix} from ${cfg.network} via fe80::ff:fe00:1 dev enthalpy"
];
}; };
after = [ after = [ "netns-enthalpy.service" ];
partOf = [
"netns-enthalpy.service"
"enthalpy-exit.service"
];
wantedBy = [
"multi-user.target"
"netns-enthalpy.service" "netns-enthalpy.service"
"network-online.target"
]; ];
requires = [ "netns-enthalpy.service" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
}; };
}; };
} }