{ config, lib, pkgs, ... }: with lib; let hostname = config.instance.hostname; host = config.fudo.hosts.${hostname}; host-secrets = config.fudo.secrets.host-secrets.${hostname}; cfg = config.fudo.services.wireguard-gateway; peerOpts = { name, ... }: { options = with types; { public-key = mkOption { type = str; description = "Peer public key."; }; assigned-ip = mkOption { type = str; description = "IP address assigned to this peer."; }; }; }; in { options.fudo.services.wireguard-gateway = with types; { enable = mkEnableOption "Enable WireGuard gateway: let external clients join the local network."; network = mkOption { type = str; description = "IP address range to use for clients."; default = "172.16.0.0/24"; }; listen-port = mkOption { type = port; description = "Port on which to listen for incoming connections."; default = 51820; }; peers = mkOption { type = attrsOf (submodule peerOpts); description = "Map of peer to peer options."; default = {}; }; }; config = mkIf cfg.enable { assertions = [ { assertion = all (peerOpts: pkgs.lib.ip.ipv4OnNetwork peerOpts.assigned-ip cfg.network) (attrValues cfg.peers); message = "Peer IPs must be on the assigned network."; } { assertion = host.wireguard.private-key-file != null; message = "WireGuard gateway server private key file must be set."; } ]; fudo.secrets.host-secrets.${hostname} = { wireguard-gateway-privkey-file = { source-file = host.wireguard.private-key-file; target-file = "/run/wireguard-gateway/key"; }; }; networking = { firewall.allowedUDPPorts = [ cfg.listen-port ]; wireguard.interfaces.wg-gw0 = { ips = [ cfg.network ]; listenPort = cfg.listen-port; postSetup = "${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s ${cfg.network} -o ${cfg.external-interface} -j MASQUERADE"; postShutdown = "${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s ${cfg.network} -o ${cfg.external-interface} -j MASQUERADE"; privateKeyFile = host-secrets.wireguard-gateway-privkey-file.target-file; peers = let peerList = attrValues cfg.peers; in map (peerOpts: { publicKey = peerOpts.public-key; allowedIPs = [ "${peerOpts.ip}/32" ]; }) cfg.peers; }; }; }; }