{ config, lib, pkgs, ... }: # Take note this service is heavily based on the hardware networking setup of # this host so better stay focused on the hardware configuration on this host. let hostCfg = config.hosts.plover; cfg = hostCfg.services.wireguard; inherit (import ../hardware/networks.nix) interfaces wireguardPort wireguardPeers; wireguardIFName = interfaces.wireguard0.ifname; desktopPeerAddresses = with wireguardPeers.desktop; [ "${IPv4}/32" "${IPv6}/128" ]; phonePeerAddresses = with wireguardPeers.phone; [ "${IPv4}/32" "${IPv6}/128" ]; in { options.hosts.plover.services.wireguard.enable = lib.mkEnableOption "Wireguard VPN setup"; config = lib.mkIf cfg.enable (lib.mkMerge [ { environment.systemPackages = [ pkgs.wireguard-tools ]; sops.secrets = let systemdNetworkdPermission = { group = config.users.users.systemd-network.group; reloadUnits = [ "systemd-networkd.service" ]; mode = "0640"; }; in lib.private.getSecrets ../../secrets/secrets.yaml { "wireguard/private-key" = systemdNetworkdPermission; "wireguard/preshared-keys/ni" = systemdNetworkdPermission; "wireguard/preshared-keys/phone" = systemdNetworkdPermission; }; # Since we're using systemd-networkd to configure interfaces, we can control # how each interface can handle things such as IP masquerading so no need for # modifying sysctl settings like 'ipv4.ip_forward' or similar. systemd.network = { wait-online.ignoredInterfaces = [ wireguardIFName ]; netdevs."99-${wireguardIFName}" = { netdevConfig = { Name = wireguardIFName; Kind = "wireguard"; }; wireguardConfig = { PrivateKeyFile = config.sops.secrets."wireguard/private-key".path; ListenPort = wireguardPort; }; wireguardPeers = [ # Desktop workstation. { wireguardPeerConfig = { PublicKey = lib.readFile ../../../ni/files/wireguard/wireguard-public-key-ni; PresharedKeyFile = config.sops.secrets."wireguard/preshared-keys/ni".path; AllowedIPs = lib.concatStringsSep "," desktopPeerAddresses; }; } # Phone. { wireguardPeerConfig = { PublicKey = lib.readFile ../../files/wireguard/wireguard-public-key-phone; PresharedKeyFile = config.sops.secrets."wireguard/preshared-keys/phone".path; AllowedIPs = lib.concatStringsSep "," phonePeerAddresses; }; } ]; }; networks."99-${wireguardIFName}" = with interfaces.wireguard0; { matchConfig.Name = ifname; address = [ "${IPv4.address}/14" "${IPv6.address}/64" ]; routes = [ { routeConfig.Gateway = IPv4.gateway; } ]; }; }; } (lib.mkIf hostCfg.services.firewall.enable { networking.firewall = { # Allow the UDP traffic for the Wireguard service. allowedUDPPorts = [ wireguardPort ]; # IP forwarding for specific interfaces. filterForward = true; extraForwardRules = '' iifname ${wireguardIFName} accept comment "IP forward from Wireguard interface to LAN" ''; }; networking.nftables.ruleset = '' table ip wireguard-${wireguardIFName} { chain prerouting { type nat hook prerouting priority filter; policy accept; } chain postrouting { type nat hook postrouting priority srcnat; policy accept; iifname ${wireguardIFName} snat to ${interfaces.lan.IPv4.address} comment "Make packets from Wireguard interface appear as coming from the LAN interface" } } ''; }) ]); }