{ config , pkgs , lib , ... }: { sops.secrets = { "wg_privkey" = { sopsFile = ./secrets/wg_privkey.yaml; }; "wg_preshared/nixy" = { sopsFile = ../common/secrets/wg_preshared.yaml; }; "wg_privkey_proton" = { sopsFile = ./secrets/wg_privkey_proton.yaml; }; "wg_endpoint_proton" = { sopsFile = ./secrets/wg_privkey_proton.yaml; }; }; # Personal mesh: dials into the magpie hub (10.100.0.0/24) networking.wireguard.interfaces = { wg0 = { ips = [ "10.100.0.6/24" ]; privateKeyFile = config.sops.secrets."wg_privkey".path; peers = [ { publicKey = builtins.readFile ../magpie/wg_pubkey; presharedKeyFile = config.sops.secrets."wg_preshared/nixy".path; allowedIPs = [ "10.100.0.0/24" ]; endpoint = "5.75.229.224:51820"; persistentKeepalive = 25; } ]; }; }; systemd = { services = { # ProtonVPN split tunnel: proton_wg lives inside the `wg` network # namespace; anything that should egress over Proton is launched # with `ip netns exec wg ...`. "netns@" = { description = "%I network namespace"; before = [ "network.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = "${pkgs.iproute2}/bin/ip netns add %I"; ExecStop = "${pkgs.iproute2}/bin/ip netns del %I"; }; }; "wg_proton" = { description = "wg network interface"; bindsTo = [ "netns@wg.service" ]; requires = [ "network-online.target" ]; wants = [ "dnscrypt-proxy_proton.service" ]; after = [ "netns@wg.service" ]; before = [ "dnscrypt-proxy_proton.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = pkgs.writers.writeBash "wg-up" '' set -e ENDPOINT_IP=$(${pkgs.coreutils-full}/bin/cat "${config.sops.secrets."wg_endpoint_proton".path}") ${pkgs.iproute2}/bin/ip link add proton_wg type wireguard ${pkgs.iproute2}/bin/ip link set proton_wg netns wg ${pkgs.iproute2}/bin/ip -n wg address add 10.2.0.2/32 dev proton_wg ${pkgs.iproute2}/bin/ip netns exec wg \ ${pkgs.wireguard-tools}/bin/wg set "proton_wg" private-key "${ config.sops.secrets."wg_privkey_proton".path }" ${pkgs.iproute2}/bin/ip netns exec wg \ ${pkgs.wireguard-tools}/bin/wg set "proton_wg" peer "${lib.fileContents ../common/wg_pubkey_proton}" \ endpoint "$ENDPOINT_IP:51820" \ persistent-keepalive "25" \ allowed-ips "0.0.0.0/0" ${pkgs.iproute2}/bin/ip -n wg link set lo up ${pkgs.iproute2}/bin/ip -n wg link set proton_wg up ${pkgs.iproute2}/bin/ip -n wg route add default dev proton_wg ''; ExecStop = pkgs.writers.writeBash "wg-down" '' ${pkgs.iproute2}/bin/ip -n wg route del default dev proton_wg ${pkgs.iproute2}/bin/ip -n wg link del proton_wg ''; }; }; "dnscrypt-proxy_proton" = { description = "DNSCrypt-proxy client proton"; wants = [ "network-online.target" "nss-lookup.target" ]; before = [ "nss-lookup.target" ]; after = [ "wg_proton.service" ]; partOf = [ "wg_proton.service" ]; serviceConfig = { AmbientCapabilities = "CAP_NET_BIND_SERVICE"; CacheDirectory = "dnscrypt-proxy"; DynamicUser = true; ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy -config ${config.services.dnscrypt-proxy.configFile}"; LockPersonality = true; LogsDirectory = "dnscrypt-proxy"; MemoryDenyWriteExecute = true; NetworkNamespacePath = "/var/run/netns/wg"; NonBlocking = true; NoNewPrivileges = true; PrivateDevices = true; ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectSystem = "strict"; Restart = "always"; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; RestrictNamespaces = true; RestrictRealtime = true; RuntimeDirectory = "dnscrypt-proxy"; StateDirectory = "dnscrypt-proxy"; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "@chown" "~@aio" "~@keyring" "~@memlock" "~@setuid" "~@timer" ]; }; }; }; # Don't bring wg0 up at boot targets."wireguard-wg0".wantedBy = lib.mkForce [ ]; }; }