From fc2d98e5031f008c6bc4ae305b18f7aab0570e29 Mon Sep 17 00:00:00 2001 From: Lander Van den Bulcke Date: Fri, 19 Sep 2025 14:46:07 +0200 Subject: [PATCH] refactor: put namespaced vm code in module Signed-off-by: Lander Van den Bulcke --- hosts/servers/hosting-02.nix | 92 +++----------------- lib/hetzner.nix | 3 +- modules/nixos/default.nix | 1 + modules/nixos/namespaced-vpn.nix | 142 +++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 80 deletions(-) create mode 100644 modules/nixos/namespaced-vpn.nix diff --git a/hosts/servers/hosting-02.nix b/hosts/servers/hosting-02.nix index 57b2490..9965477 100644 --- a/hosts/servers/hosting-02.nix +++ b/hosts/servers/hosting-02.nix @@ -1,4 +1,8 @@ -{ config, pkgs, ... }: +{ + config, + pkgs, + ... +}: { networking.firewall = { enable = true; @@ -11,85 +15,15 @@ ]; }; - networking.iproute2.enable = true; - systemd.network.config = { - routeTables = { - vpn = 133; - }; - addRouteTablesToIPRoute2 = 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 10.0.0.2/24 dev veth-ns-$ns - ''; - in - "${script} %i"; - ExecStop = "${pkgs.iproute2}/bin/ip link del veth-%i"; - }; - }; - - systemd.network.networks."50-veth" = { - matchConfig.Name = "veth-init-vpn"; - networkConfig = { - Address = "10.0.0.1/24"; - }; - }; - - systemd.services."wireguard-wg0".requires = [ - "netns@vpn.service" - "veth-setup@vpn.service" - ]; - networking.wireguard = { + services.namespaced-vpn = { enable = true; - useNetworkd = false; - - interfaces.wg0 = { - ips = [ - "10.64.244.95/32" - "fc00:bbbb:bbbb:bb01::1:f45e/128" - ]; - - peers = [ - { - publicKey = "KkShcqgwbkX2A9n1hhST6qu+m3ldxdJ2Lx8Eiw6mdXw="; - allowedIPs = [ - "0.0.0.0/0" - "::0/0" - ]; - endpoint = "146.70.117.226:51820"; - persistentKeepalive = 25; - } - ]; - - listenPort = 51820; - privateKeyFile = config.sops.secrets.wireguardKey.path; - socketNamespace = "init"; - interfaceNamespace = "vpn"; - }; + ips = [ + "10.64.244.95/32" + "fc00:bbbb:bbbb:bb01::1:f45e/128" + ]; + publicKey = "KkShcqgwbkX2A9n1hhST6qu+m3ldxdJ2Lx8Eiw6mdXw="; + endpoint = "146.70.117.226:51820"; + privateKeyFile = config.sops.secrets.wireguardKey.path; }; environment.systemPackages = with pkgs; [ diff --git a/lib/hetzner.nix b/lib/hetzner.nix index e1917da..d8c33bc 100644 --- a/lib/hetzner.nix +++ b/lib/hetzner.nix @@ -17,7 +17,7 @@ }: let diskConfig = import ./disk-config.nix { inherit disks nixpkgs; }; - machineConfig = ../hosts/servers/${hostname}.nix; + machineConfig = import ../hosts/servers/${hostname}.nix; in nixpkgs.lib.nixosSystem { inherit system; @@ -25,6 +25,7 @@ modules = [ inputs.disko.nixosModules.disko inputs.sops-nix.nixosModules.sops + inputs.self.nixosModules.namespaced-vpn diskConfig diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index a0e873d..1e14928 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -1,3 +1,4 @@ { # my-module = import ./my-module.nix + namespaced-vpn = import ./namespaced-vpn.nix; } diff --git a/modules/nixos/namespaced-vpn.nix b/modules/nixos/namespaced-vpn.nix new file mode 100644 index 0000000..b6f7a2a --- /dev/null +++ b/modules/nixos/namespaced-vpn.nix @@ -0,0 +1,142 @@ +{ + 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"; + }; + }; + + 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; + }; + }; + + }; +}