{ config, lib, pkgs, ... }: with lib; let cfg = config.profiles.clerie.wg-clerie; in { options = { profiles.clerie.wg-clerie = { enable = mkEnableOption "VPN for public static IP"; privateKeyFile = mkOption { type = with types; nullOr str; default = null; description = "Path to file containing private key for wireguard interface"; }; ipv6s = mkOption { type = with types; listOf str; default = []; description = "IPv6 interface addresses"; }; ipv4s = mkOption { type = with types; listOf str; default = []; description = "IPv4 interface addresses"; }; defaultViaVPN = mkOption { type = types.bool; default = true; description = "Use VPN default route for a protocol, if that protocol is unavailable in the underlay"; }; }; }; config = mkIf cfg.enable { systemd.network.config.routeTables = { wg-clerie = 200; }; systemd.network.config.addRouteTablesToIPRoute2 = true; sops = (mkIf (cfg.privateKeyFile == null) { secrets.wg-clerie = { owner = "systemd-network"; group = "systemd-network"; }; }); networking.networkmanager.unmanaged = [ "interface-name:wg-clerie" ]; systemd.network.netdevs."10-wg-clerie" = { netdevConfig = { Kind = "wireguard"; Name = "wg-clerie"; }; wireguardConfig = { PrivateKeyFile = if cfg.privateKeyFile != null then cfg.privateKeyFile else config.sops.secrets.wg-clerie.path; RouteTable = "wg-clerie"; }; wireguardPeers = [ { PublicKey = "2p1Jqs3bkXbXHFWE6vp1yxHIFoUaZQEARS2nJzbkuBA="; AllowedIPs = [ "0.0.0.0/0" "::/0" "10.20.30.0/24" "2a01:4f8:c0c:15f1::/113" ]; PersistentKeepalive = 25; } ]; }; systemd.network.networks."10-wg-clerie" = { matchConfig.Name = "wg-clerie"; address = cfg.ipv6s ++ cfg.ipv4s; linkConfig.RequiredForOnline = "no"; routingPolicyRules = (builtins.map (ip: { Priority = 19000; Family = "ipv6"; From = ip; #Type = "table"; Table = "wg-clerie"; }) cfg.ipv6s ) ++ (builtins.map (ip: { Priority = 19001; Family = "ipv6"; From = ip; Type = "unreachable"; }) cfg.ipv6s ) ++ (builtins.map (ip: { Priority = 19000; Family = "ipv4"; From = ip; #Type = "table"; Table = "wg-clerie"; }) cfg.ipv4s ) ++ (builtins.map (ip: { Priority = 19001; Family = "ipv4"; From = ip; Type = "unreachable"; }) cfg.ipv4s ) ++ [ { Priority = 20000; Family = "ipv6"; To = "2a01:4f8:c0c:15f1::1/128"; IPProtocol = "udp"; DestinationPort = 51820; #Type = "table"; Table = "main"; } { Priority = 20001; Family = "ipv6"; To = "2a01:4f8:c0c:15f1::1/128"; IPProtocol = "udp"; DestinationPort = 51820; Type = "unreachable"; } { Priority = 20000; Family = "ipv4"; To = "78.47.183.82/32"; IPProtocol = "udp"; DestinationPort = 51820; #Type = "table"; Table = "main"; } { Priority = 20001; Family = "ipv4"; To = "78.47.183.82/32"; IPProtocol = "udp"; DestinationPort = 51820; Type = "unreachable"; } { Priority = 21000; Family = "both"; #Type = "table"; Table = "main"; } ] ++ (if cfg.defaultViaVPN then [ { Priority = 21001; Family = "both"; #Type = "table"; Table = "wg-clerie"; } ] else []) ++ [ { Priority = 22000; Family = "both"; Type = "unreachable"; } ]; }; systemd.services."wg-clerie-endpoint-refresh" = { serviceConfig = { Type = "oneshot"; }; path = [ pkgs.wireguard-tools pkgs.iproute2 ]; script = '' set -euo pipefail # Don't do anything as long as interface is not configured if ! wg show wg-clerie endpoints > /dev/null; then exit 0 fi endpoint="" if ip route get 2a01:4f8:c0c:15f1::1 ipproto udp dport 51820 &>/dev/null; then endpoint="[2a01:4f8:c0c:15f1::1]:51820" else endpoint="78.47.183.82:51820" fi wg set wg-clerie peer "2p1Jqs3bkXbXHFWE6vp1yxHIFoUaZQEARS2nJzbkuBA=" endpoint "''${endpoint}" ''; requires = [ "network-online.target" ]; after = [ "network-online.target" ]; }; systemd.timers."wg-clerie-endpoint-refresh" = { wantedBy = [ "timers.target" ]; timerConfig = { OnCalendar = "*-*-* *:*:0/5"; RandomizedDelaySec = "5s"; }; requires = [ "network-online.target" ]; after = [ "network-online.target" ]; }; environment.systemPackages = [ pkgs.wireguard-tools ]; }; }