{ pkgs, lib, config, instanceConfigs, instanceConfig, ... }: let wgServer = instanceConfig?wg.server && instanceConfig.wg.server; cfg = config.xyno.services.wireguard; ula = cfg.ula; ulaPrefix = "${ula}:1337"; # /64 for normal vpn monitoringUlaPrefix = "${ula}:2337"; # /64 for monitoring # uses a hash digest as the host identifier genUlaForHost = prefix: hostName: let hostHash = builtins.hashString "sha512" hostName; localParts = map (n: builtins.substring (n * 4) 4 hostHash) (lib.range 0 3); localPart = lib.concatStringsSep ":" localParts; in "${prefix}:${localPart}"; # peers list for networkd filteredConfigs = builtins.filter (x: x.hostName != config.networking.hostName ) (lib.attrValues instanceConfigs); wgPeersLists = map ( c: ( (lib.optional (c?publicHostname) { # if peer is publicly on the internet AllowedIPs = (lib.optionals ( c?wg.server && c.wg.server) [ "${ulaPrefix}::/48" # all traffic in the ula shall be sent to the server ]) ++ (lib.optionals (!c?wg.server || !c.wg.server) [ "${genUlaForHost ulaPrefix c.hostName}/128" # if a host is reachable but shouldn't play server, send only to the hosts ip ]); Endpoint = "${c.publicHostname}:51820"; PersistentKeepalive = 25; PublicKey = c.wg.pubKey; }) ++ (lib.optional ((!c?publicHostname) && wgServer && (c?wg.pubKey)) { # if this is the server and the peer isn't reachable on the internet AllowedIPs = [ "${genUlaForHost ulaPrefix c.hostName}/128" "${genUlaForHost monitoringUlaPrefix c.hostName}/128" ]; PublicKey = c.wg.pubKey; PresharedKeyFile = config.sops.secrets."wg/psk".path; # TODO } ) ) ) filteredConfigs; wgPeers = lib.flatten wgPeersLists; in { options.xyno.services.wireguard.enable = lib.mkEnableOption "enables wireguard"; options.xyno.services.wireguard.monHostsDomain = lib.mkOption { type = lib.types.str; default = "mon.wg.hailsatan.eu"; }; options.xyno.services.wireguard.hostsDomain = lib.mkOption { type = lib.types.str; default = "wg.hailsatan.eu"; }; options.xyno.services.wireguard.ula = lib.mkOption { type = lib.types.str; default = "fd68:b6a4:36e4"; }; options.xyno.services.wireguard.ip6 = lib.mkOption { type = lib.types.str; default = genUlaForHost ulaPrefix config.networking.hostName; }; options.xyno.services.wireguard.monIp6 = lib.mkOption { type = lib.types.str; default = genUlaForHost monitoringUlaPrefix config.networking.hostName; }; config = lib.mkIf cfg.enable { networking.hosts = (lib.mapAttrs' ( n: v: { value = ["${v.hostName}.${cfg.hostsDomain}"]; name = (genUlaForHost ulaPrefix v.hostName); } ) instanceConfigs) // (lib.mapAttrs' ( n: v: { value = ["${v.hostName}.${cfg.monHostsDomain}"]; name = (genUlaForHost monitoringUlaPrefix v.hostName); } ) instanceConfigs); networking.firewall.allowedUDPPorts = lib.optional wgServer 51820; networking.firewall.interfaces."wg0".allowedUDPPorts = lib.optional wgServer 53; systemd.network.netdevs."wg0" = { netdevConfig = { Name = "wg0"; Kind = "wireguard"; Description = "main wireguard tunnel"; }; wireguardConfig = { ListenPort = lib.mkIf wgServer 51820; PrivateKeyFile = config.sops.secrets."wg/privkey".path; # TODO }; wireguardPeers = wgPeers; }; systemd.network.networks."wg0" = { matchConfig.Name = "wg0"; networkConfig = { Description = "xyno wireguard"; }; address = [ "${(genUlaForHost ulaPrefix config.networking.hostName)}/128" "${(genUlaForHost monitoringUlaPrefix config.networking.hostName)}/128" ]; }; services.prometheus.exporters.wireguard = lib.mkIf (wgServer && config.xyno.services.monitoring.enable) { enable = true; interfaces = [ "wg0" ]; }; services.coredns = lib.mkIf wgServer { # for non nixos devices to be able to resolve vpn hostnames enable = true; config = '' . { bind wg0 prometheus hosts ${cfg.hostsDomain} forward . /etc/resolv.conf } ''; }; xyno.services.monitoring.exporters.coredns = lib.mkIf wgServer 9153; xyno.services.monitoring.exporters.wireguard = lib.mkIf wgServer config.services.prometheus.exporters.wireguard.port; sops.secrets."wg/privkey" = { reloadUnits = [ "systemd-networkd.service" ]; }; sops.secrets."wg/psk" = { reloadUnits = [ "systemd-networkd.service" ]; }; }; }