{ config, lib, pkgs, ... }: let isValidPeer = p: (p ? syscfg.net.wg.enable) && (p.syscfg.net.wg.enable == true) && (p.syscfg.net.wg.pubkey != config.syscfg.net.wg.pubkey); activePeers = builtins.filter isValidPeer config.syscfg.peers; isServer = config.syscfg.server ? wireguard && config.syscfg.server.wireguard; vpnPeer = { name = "vpn-helcel"; publicKey = "NFBJvYXZC+bd62jhrKnM7/pugidWhgR6+C5qIiUiq3Q="; allowedIPs = [ "10.10.1.0/24" "fd10:10:10::0/64" ]; }; vpnTable = "51820"; vpnDown = pkgs.writeShellScript "wg0-vpn-down" '' PATH=${lib.makeBinPath [ pkgs.iproute2 pkgs.wireguard-tools ]} for f in -4 -6; do ip $f rule del pref 31000 2>/dev/null ip $f rule del pref 31001 2>/dev/null ip $f route flush table ${vpnTable} 2>/dev/null done wg set wg0 fwmark off peer ${vpnPeer.publicKey} allowed-ips ${lib.concatStringsSep "," vpnPeer.allowedIPs} 2>/dev/null true ''; vpnUp = pkgs.writeShellScript "wg0-vpn-up" '' set -e PATH=${lib.makeBinPath [ pkgs.iproute2 pkgs.wireguard-tools pkgs.procps ]} ${vpnDown} sysctl -q net.ipv4.conf.all.src_valid_mark=1 wg set wg0 fwmark ${vpnTable} peer ${vpnPeer.publicKey} allowed-ips 0.0.0.0/0,::/0 for f in -4 -6; do ip $f route replace default dev wg0 table ${vpnTable} ip $f rule add table main suppress_prefixlength 0 pref 31000 ip $f rule add not fwmark ${vpnTable} table ${vpnTable} pref 31001 done ''; in { config = lib.mkIf (config.syscfg.net.wg.enable) { networking.wireguard = { enable = true; interfaces = { wg0 = { ips = [ config.syscfg.net.wg.ip4 config.syscfg.net.wg.ip6 ]; privateKeyFile = config.sops.secrets."${config.syscfg.hostname}_wg_priv".path; listenPort = 1515; mtu = 1340; peers = if isServer then map (p: { name = p.syscfg.hostname; publicKey = p.syscfg.net.wg.pubkey; allowedIPs = [ p.syscfg.net.wg.ip4 p.syscfg.net.wg.ip6 ]; }) activePeers else [(vpnPeer // { endpoint = "vpn.helcel.net:1515"; persistentKeepalive = 30; })]; }; }; }; # Let the desktop (eww network panel, scripts/net/wg-toggle) bring the # tunnel up and down. eww runs as a user service outside the login # session, where polkit cannot ask for a password, so wheel users may # start/stop wireguard-* units without one. security.polkit.extraConfig = '' polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && subject.isInGroup("wheel") && /^wireguard-/.test(action.lookup("unit") || "")) { return polkit.Result.YES; } }); ''; systemd.services."wireguard-wg0-peer-vpn-helcel" = { after = [ "network-online.target" "nss-lookup.target" ]; bindsTo = [ "network-online.target" ]; wantedBy = lib.mkForce [ "network-online.target" ]; before = lib.mkForce [ ]; serviceConfig = { Restart = "on-failure"; RestartSec = "10s"; # Wait 2 seconds before retrying the domain query }; startLimitIntervalSec = 0; }; systemd.services."wireguard-wg0-vpn" = lib.mkIf (!isServer) { description = "WireGuard wg0 - route all traffic"; bindsTo = [ "wireguard-wg0.service" ]; wants = [ "wireguard-wg0-peer-vpn-helcel.service" ]; partOf = [ "wireguard-wg0-peer-vpn-helcel.service" ]; after = [ "wireguard-wg0.service" "wireguard-wg0-peer-vpn-helcel.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = vpnUp; ExecStop = vpnDown; }; }; networking.firewall.checkReversePath = lib.mkIf (!isServer) "loose"; }; }