{ config, lib, ... }: let cfg = config.modules.network.vpn-client; # NOLINT(let_order) in { options.modules.network.vpn-client = { enable = lib.mkEnableOption "a WireGuard VPN client"; interface = lib.mkOption { type = lib.types.str; default = "wg-home"; description = "WireGuard interface name."; }; address = lib.mkOption { type = lib.types.str; example = "10.23.100.3/32"; description = "Client tunnel address in CIDR notation."; }; privateKeySecret = lib.mkOption { type = lib.types.str; default = "wireguard-home-private-key"; description = "SOPS secret containing the WireGuard client private key."; }; autoStart = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to start the VPN tunnel during boot."; }; mtu = lib.mkOption { type = lib.types.nullOr lib.types.int; default = 1280; description = "Tunnel MTU, or null to use the WireGuard default."; }; sopsFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; description = "Encrypted secrets file that contains privateKeySecret."; }; peer = { publicKey = lib.mkOption { type = lib.types.str; description = "WireGuard public key for the remote peer."; }; endpoint = lib.mkOption { type = lib.types.str; example = "203.0.113.10:51820"; description = "WireGuard endpoint for the remote peer."; }; allowedIPs = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "10.23.0.0/16" ]; description = "Routes to send through the VPN tunnel."; }; persistentKeepalive = lib.mkOption { type = lib.types.nullOr lib.types.int; default = 25; description = "Persistent keepalive interval in seconds, or null to disable it."; }; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = cfg.sopsFile != null; message = "modules.network.vpn-client.sopsFile must name the encrypted secrets file containing the WireGuard private key."; } ]; sops.secrets.${cfg.privateKeySecret}.sopsFile = cfg.sopsFile; systemd.targets."wireguard-${cfg.interface}" = lib.mkIf (!cfg.autoStart) { wantedBy = lib.mkForce [ ]; }; networking.wireguard.interfaces.${cfg.interface} = { ips = [ cfg.address ]; inherit (cfg) mtu; privateKeyFile = config.sops.secrets.${cfg.privateKeySecret}.path; peers = [ ( { inherit (cfg.peer) publicKey endpoint allowedIPs; } // lib.optionalAttrs (cfg.peer.persistentKeepalive != null) { inherit (cfg.peer) persistentKeepalive; } ) ]; }; }; }