{ config, lib, pkgs, ... }: with lib; let cfg = config.services.namespaced-vpn; in { options.services.namespaced-vpn = { enable = mkEnableOption "namespaced vpn"; ips = mkOption { type = types.listOf types.str; }; publicKey = mkOption { type = types.str; }; endpoint = mkOption { type = types.str; }; privateKeyFile = mkOption { type = types.path; }; namespace = mkOption { type = types.str; default = "vpn"; }; wgInterface = mkOption { type = types.str; default = "wg0"; }; hostVethIP = mkOption { type = types.str; default = "10.10.10.1/30"; }; namespaceVethIP = mkOption { type = types.str; default = "10.10.10.2/30"; }; dns = mkOption { type = types.str; default = "9.9.9.9"; }; }; config = mkIf cfg.enable { networking.iproute2.enable = true; systemd.services."netns@" = { description = "Network namespace %i"; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = "${pkgs.iproute2}/bin/ip netns add %i"; ExecStop = "${pkgs.iproute2}/bin/ip netns del %i"; }; }; systemd.services."veth-setup@" = { description = "Setup veth pair for %i namespace"; after = [ "netns@%i.service" ]; requires = [ "netns@%i.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = let script = pkgs.writers.writeBash "veth-up" '' ns="$1" ${pkgs.iproute2}/bin/ip link add veth-init-$ns type veth peer name veth-ns-$ns ${pkgs.iproute2}/bin/ip link set veth-ns-$ns netns $ns ${pkgs.iproute2}/bin/ip link set veth-init-$ns up ${pkgs.iproute2}/bin/ip netns exec $ns ${pkgs.iproute2}/bin/ip link set veth-ns-$ns up ${pkgs.iproute2}/bin/ip netns exec $ns ${pkgs.iproute2}/bin/ip addr add $NS_IP dev veth-ns-$ns ${pkgs.iproute2}/bin/ip netns exec $ns ${pkgs.iproute2}/bin/ip route add $HOST_IP via $(echo $NS_IP | sed -E 's/\/[0-9]+$//') dev veth-ns-$ns ''; in "${script} %i"; ExecStop = "${pkgs.iproute2}/bin/ip link del veth-%i"; }; }; systemd.services."veth-setup@${cfg.namespace}" = { overrideStrategy = "asDropin"; environment = { HOST_IP = cfg.hostVethIP; NS_IP = cfg.namespaceVethIP; }; }; systemd.network.networks."50-init-${cfg.namespace}" = { matchConfig.Name = "veth-init-${cfg.namespace}"; networkConfig = { Address = "${cfg.hostVethIP}"; }; }; systemd.services."wireguard-${cfg.wgInterface}".requires = [ "netns@${cfg.namespace}.service" "veth-setup@${cfg.namespace}.service" ]; systemd.network.wait-online.ignoredInterfaces = [ cfg.wgInterface "veth-init-${cfg.namespace}" "veth-ns-${cfg.namespace}" ]; networking.wireguard = { enable = true; useNetworkd = false; interfaces."${cfg.wgInterface}" = { ips = cfg.ips; peers = [ { publicKey = cfg.publicKey; allowedIPs = [ "0.0.0.0/0" "::0/0" ]; endpoint = cfg.endpoint; persistentKeepalive = 25; } ]; listenPort = 51820; privateKeyFile = cfg.privateKeyFile; socketNamespace = "init"; interfaceNamespace = cfg.namespace; }; }; environment.etc."netns/${cfg.namespace}/resolv.conf" = { text = '' nameserver ${cfg.dns} ''; }; }; }