{ config, lib, ... }: let cfg = config.services.codewreck.wireguard; in { options.services.codewreck.wireguard = { enabled = lib.mkOption { description = "enabled (on by default if local machine description is found, don't blame me if you enable it without one)"; default = builtins.hasAttr cfg.hostname cfg.machines; type = lib.types.bool; }; domain = lib.mkOption { description = "domain (used to shorten hosts for config and /etc/hosts"; default = config.networking.domain; type = lib.types.str; }; hostname = lib.mkOption { description = "short part of the hostname (select wireguard config from configs)"; default = lib.removeSuffix ".${config.networking.domain}" config.networking.hostName; type = lib.types.str; }; machines = lib.mkOption { description = "attr of machine configuration"; default = {}; example = { jormungand = { ip = "fd13:537e:dbbf:1210::1"; endpoint = "jormungand.codewreck.org"; listenPort = 51733; publicKey = "Hx5RnhfyP91LEgXAn4pLiOm4nMRZvVx+rsX0YhVzqAQ="; }; odin = { ip = "fd13:537e:dbbf:1210::2"; allowedIPs = [ "fd13:537e:dbbf:1211::/64" "10.17.42.0/24" ]; endpoint = "gaia.codewreck.org"; listenPort = 51432; publicKey = "7YALjkbDv6iId1VHJu4uTgVAj41VvAoQfaiVChJdZQ8="; }; fenrir = { ip = "fd13:537e:dbbf:1210::3"; allowedIPs = [ "fd13:537e:dbbf:1213::/64" "10.42.17.0/24" ]; publicKey = "SrLUKqoxYxFriLDenMwNHLqetxVCLmyCG606hg3h9mQ="; listenPort = 51123; keepalive = 55; }; }; type = lib.types.attrsOf (lib.types.submodule { options = { ip = lib.mkOption { description = "ip is used both to configure interface if target and for allowed IPs"; type = lib.types.str; }; allowedIPs = lib.mkOption { description = "extra list of allowed IPs for wg"; default = []; type = lib.types.listOf lib.types.str; }; publicKey = lib.mkOption { description = "wg public key"; type = lib.types.str; }; listenPort = lib.mkOption { description = "wg port (actual listen-port, firewall and endpoint)"; type = lib.types.int; }; endpoint = lib.mkOption { description = "wg endpoint without port component (taken from mandatory listenPort)"; default = null; type = lib.types.nullOr lib.types.str; }; keepalive = lib.mkOption { description = "wg keepalive for all peers if present on current machine"; default = null; type = lib.types.nullOr lib.types.int; }; only = lib.mkOption { description = "limit to single server"; default = null; type = lib.types.nullOr lib.types.str; }; };}); }; }; config = let current = builtins.getAttr cfg.hostname cfg.machines; others = builtins.removeAttrs cfg.machines [ cfg.hostname ]; peers = lib.mapAttrsToList (name: value: { allowedIPs = [ value.ip ] ++ value.allowedIPs; publicKey = value.publicKey; endpoint = (if value.endpoint != null then value.endpoint + ":" + (toString value.listenPort) else null); persistentKeepalive = current.keepalive; }) (lib.filterAttrs (n: v: current.only == n || (current.only == null && (v.endpoint != null || (current.endpoint != null && (v.only == null || v.only == cfg.hostname)) ) )) others); hosts = lib.mapAttrs' (name: value: lib.attrsets.nameValuePair (builtins.head (builtins.split "/" value.ip)) [ (name + ".wg.${cfg.domain}") (name + ".wg") ] ) others; in lib.mkIf cfg.enabled { networking.wireguard.interfaces.wg0 = { ips = [ current.ip ]; listenPort = current.listenPort; privateKeyFile = "/etc/nixos/secrets/wg0.key"; peers = peers; }; networking.hosts = hosts; networking.firewall.allowedUDPPorts = [ current.listenPort ]; }; }