diff --git a/modules/netns.nix b/modules/netns.nix
index 61c9c95f..96493974 100644
--- a/modules/netns.nix
+++ b/modules/netns.nix
@@ -26,9 +26,10 @@ let
};
};
mkNetNsConfig = name: opts: with opts; {
- networking.localCommands = let
- iptables = "${pkgs.iptables}/bin/iptables";
- in-ns = "ip netns exec ${name}";
+ systemd.services."netns-${name}" = let
+ ip = lib.getExe' pkgs.iproute2 "ip";
+ iptables = lib.getExe pkgs.iptables;
+ in-ns = "${ip} netns exec ${name}";
bridgePort = port: proto: ''
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${netnsPubIpv4} \
-j DNAT --to-destination ${hostVethIpv4}
@@ -41,55 +42,59 @@ let
config.sane.ports.ports
)
;
- in ''
- ip netns add ${name} || (test -e /run/netns/${name} && echo "${name} already exists")
- # DOCS:
- # - some of this approach is described here:
- # - iptables primer:
- # create veth pair
- ip link add ${name}-veth-a type veth peer name ${name}-veth-b || echo "${name}-veth-{a,b} aleady exists"
- ip addr add ${hostVethIpv4}/24 dev ${name}-veth-a || echo "${name}-veth-a aleady has IP address"
- ip link set ${name}-veth-a up
+ in {
+ description = "create a network namespace which will selectively bridge traffic with the init namespace";
+ # specifically, we need to set these up before wireguard-wg-*,
+ wantedBy = [ "network-pre.target" ];
+ before = [ "network-pre.target" ];
+ serviceConfig.Type = "oneshot";
+ serviceConfig.RemainAfterExit = true;
+ script = ''
+ ${ip} netns add ${name} || (test -e /run/netns/${name} && echo "${name} already exists")
+ # DOCS:
+ # - some of this approach is described here:
+ # - iptables primer:
+ # create veth pair
+ ${ip} link add ${name}-veth-a type veth peer name ${name}-veth-b || echo "${name}-veth-{a,b} aleady exists"
+ ${ip} addr add ${hostVethIpv4}/24 dev ${name}-veth-a || echo "${name}-veth-a aleady has IP address"
+ ${ip} link set ${name}-veth-a up
- # move veth-b into the namespace
- ip link set ${name}-veth-b netns ${name} || echo "${name}-veth-b was already moved into its netns"
- ${in-ns} ip addr add ${netnsVethIpv4}/24 dev ${name}-veth-b || echo "${name}-veth-b aleady has IP address"
- ${in-ns} ip link set ${name}-veth-b up
+ # move veth-b into the namespace
+ ${ip} link set ${name}-veth-b netns ${name} || echo "${name}-veth-b was already moved into its netns"
+ ${in-ns} ${ip} addr add ${netnsVethIpv4}/24 dev ${name}-veth-b || echo "${name}-veth-b aleady has IP address"
+ ${in-ns} ${ip} link set ${name}-veth-b up
- # make it so traffic originating from the host side of the veth
- # is sent over the veth no matter its destination.
- ip rule add from ${hostVethIpv4} lookup ${name} pref 50 || echo "${name} already has ip rules (pref 50)"
+ # make it so traffic originating from the host side of the veth
+ # is sent over the veth no matter its destination.
+ ${ip} rule add from ${hostVethIpv4} lookup ${name} pref 50 || echo "${name} already has ip rules (pref 50)"
- # for traffic originating at the host veth to the WAN, use the veth as our gateway
- # not sure if the metric 1002 matters.
- ip route add default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || \
- echo "${name} already has default route"
- # give the default route lower priority
- ip rule add from all lookup local pref 100 || echo "${name}: already has ip rules (pref 100)"
- ip rule del from all lookup local pref 0 || echo "${name}: already removed ip rule of default lookup (pref 0)"
+ # for traffic originating at the host veth to the WAN, use the veth as our gateway
+ # not sure if the metric 1002 matters.
+ ${ip} route add default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || \
+ echo "${name} already has default route"
+ # give the default route lower priority
+ ${ip} rule add from all lookup local pref 100 || echo "${name}: already has ip rules (pref 100)"
+ ${ip} rule del from all lookup local pref 0 || echo "${name}: already removed ip rule of default lookup (pref 0)"
- # in order to access DNS in this netns, we need to route it to the VPN's nameservers
- # - alternatively, we could fix DNS servers like 1.1.1.1.
- ${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
- -j DNAT --to-destination ${dns}:53
- '' + (lib.concatStringsSep "\n" bridgeStatements);
+ # in order to access DNS in this netns, we need to route it to the VPN's nameservers
+ # - alternatively, we could fix DNS servers like 1.1.1.1.
+ ${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
+ -j DNAT --to-destination ${dns}:53
+ '' + (lib.concatStringsSep "\n" bridgeStatements);
+ preStop = ''
+ ${in-ns} ${ip} link del ${name}-veth-b || echo "couldn't delete ${name}-veth-b"
+ ${ip} link del ${name}-veth-a || echo "couldn't delete ${name}-veth-a"
+ ${ip} netns delete ${name} || echo "couldn't delete ${name}"
+ # restore rules/routes
+ ${ip} rule del from ${hostVethIpv4} lookup ${name} pref 50 || echo "couldn't delete init -> ${name} rule"
+ ${ip} route del default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || echo "couldn't delete init > ${name} route"
+ # FIXME: if there are other net namespaces active, changing the prefs here may break those!
+ ${ip} rule add from all lookup local pref 0
+ ${ip} rule del from all lookup local pref 100
+ '';
+ };
- # postShutdown = ''
- # ${in-ns} ip link del ${name}-veth-b || echo "couldn't delete ${name}-veth-b"
- # ip link del ${name}-veth-a || echo "couldn't delete ${name}-veth-a"
- # ip netns delete ${name} || echo "couldn't delete ${name}"
- # # restore rules/routes
- # ip rule del from ${veth-host-ip} lookup ${name} pref 50 || echo "couldn't delete init -> ${name} rule"
- # ip route del default via ${veth-local-ip} dev ${name}-veth-a proto kernel src ${veth-host-ip} metric 1002 table ${name} || echo "couldn't delete init > #{name} route"
- # ip rule add from all lookup local pref 0
- # ip rule del from all lookup local pref 100
- # '';
-
- # specifically, we need to set these up before wireguard-wg-*,
- # whose unit files are BEFORE "network.target", and therefore
- # ordered ambiguously w.r.t. network-local-commands (a dep of "network.target").
- systemd.services.network-local-commands.wantedBy = [ "network-pre.target" ];
- systemd.services.network-local-commands.before = [ "network-pre.target" ];
+ # for some reason network-pre doesn't actually get run before network.target by default??
systemd.targets.network-pre.wantedBy = [ "network.target" ];
systemd.targets.network-pre.before = [ "network.target" ];
@@ -116,7 +121,7 @@ in
networking.localCommands = f.networking.localCommands;
networking.iproute2.rttablesExtraConfig = f.networking.iproute2.rttablesExtraConfig;
networking.iproute2.enable = f.networking.iproute2.enable;
- systemd.services.network-local-commands = f.systemd.services.network-local-commands;
+ systemd.services = f.systemd.services;
systemd.targets.network-pre = f.systemd.targets.network-pre;
};
in take (sane-lib.mkTypedMerge take configs);