{ pkgs, lib, config, otherNodes, ... }: with lib; let cfg = config.xyno.services.wireguard; wgServer = cfg.server; ula = cfg.ula; ulaPrefix = "${ula}:1337"; # /64 for normal vpn monitoringUlaPrefix = "${ula}:2337"; # /64 for monitoring v4Subnet = "10.13.12.0/24"; # 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) (range 0 3); localPart = concatStringsSep ":" localParts; in "${prefix}:${localPart}"; # peers list for networkd wgPeersLists = attrValues (mapAttrs ( c: let hasV4 = c.xyno.services.wireguard.v4 && cfg.v4; isServer = c.xyno.services.wireguard.server; publicHostname = c.deployment.targetHost; pubKey = c.xyno.services.wireguard.pubKey; in ( (optional (publicHostname != null) { # if peer is publicly on the internet AllowedIPs = (optionals (isServer) [ "::/0" ]) ++ (optionals (isServer && hasV4) [ "0.0.0.0/0" ]) ++ (optionals (!isServer) [ "${genUlaForHost ulaPrefix c.networking.hostName}/128" # if a host is reachable but shouldn't play server, send only to the hosts ip ]) ++ (optionals ((!isServer) && hasV4) [ "${c.wg.v4}/32" ]); RouteTable = 1000; Endpoint = "${publicHostname}:51820"; PersistentKeepalive = 25; PublicKey = pubKey; PresharedKeyFile = config.sops.secrets."wg/psk".path; }) ++ (optional ((publicHostname == null) && wgServer && (pubKey != null)) { # 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" ] ++ (optionals (hasV4) [ "${c.wg.v4}/32" ]); PublicKey = pubKey; PresharedKeyFile = config.sops.secrets."wg/psk".path; }) ) ) otherNodes); wgPeers = flatten wgPeersLists; in { options.xyno.services.wireguard.enable = mkEnableOption "enables wireguard"; options.xyno.services.wireguard.monHostsDomain = mkOption { type = types.str; default = "mon.wg.hailsatan.eu"; }; options.xyno.services.wireguard.hostsDomain = mkOption { type = types.str; default = "wg.hailsatan.eu"; }; options.xyno.services.wireguard.ula = mkOption { type = types.str; default = "fd68:b6a4:36e4"; }; options.xyno.services.wireguard.ip6 = mkOption { type = types.str; default = genUlaForHost ulaPrefix config.networking.hostName; }; options.xyno.services.wireguard.monIp6 = mkOption { type = types.str; default = genUlaForHost monitoringUlaPrefix config.networking.hostName; }; options.xyno.services.wireguard.pubKey = mkOption { type = types.nullOr types.str; default = null; }; options.xyno.services.wireguard.server = mkOption { type = types.bool; default = false; }; options.xyno.services.wireguard.v4 = mkOption { type = types.nullOr types.str; default = null; }; config = mkIf cfg.enable { # TODO: add a all traffic through this network networking.hosts = (mapAttrs' ( n: v: nameValuePair (genUlaForHost ulaPrefix v.networking.hostName) [ "${v.networking.hostName}.${cfg.hostsDomain}" ] ) otherNodes) // (mapAttrs' ( n: v: nameValuePair (genUlaForHost monitoringUlaPrefix v.networking.hostName) [ "${v.networking.hostName}.${cfg.monHostsDomain}" ] ) otherNodes); networking.firewall.allowedUDPPorts = optional wgServer 51820; networking.firewall.interfaces."wg0".allowedUDPPorts = optional wgServer 53; systemd.network.netdevs."99-wg0" = { netdevConfig = { Name = "wg0"; Kind = "wireguard"; Description = "main wireguard tunnel"; }; wireguardConfig = { ListenPort = mkIf wgServer 51820; PrivateKeyFile = config.sops.secrets."wg/privkey".path; FirewallMark = 34952; }; wireguardPeers = wgPeers; }; systemd.network.networks."50-wg0" = { matchConfig.Name = "wg0"; networkConfig = { Description = "xyno wireguard"; IPMasquerade = mkIf wgServer "both"; IPv4Forwarding = wgServer; IPv6Forwarding = wgServer; }; address = [ "${(genUlaForHost ulaPrefix config.networking.hostName)}/64" "${(genUlaForHost monitoringUlaPrefix config.networking.hostName)}/64" ] ++ (optionals (cfg.v4) [ "${cfg.v4}/24" ]); }; systemd.network.networks."51-wg0-all-traffic" = { matchConfig.Name = "wg0"; networkConfig = { Description = "xyno wireguard all traffic"; DNSDefaultRoute = true; DNS = "2a07:e340::2#dns.mullvad.net"; DNSOverTLS = true; }; routingPolicyRules = [ { FirewallMark = 34952; InvertRule = true; Table = 1000; Priority = 10; } ]; }; services.prometheus.exporters.wireguard = mkIf (wgServer && config.xyno.services.monitoring.enable) { enable = true; interfaces = [ "wg0" ]; }; services.coredns = 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 = mkIf wgServer 9153; xyno.services.monitoring.exporters.wireguard = mkIf wgServer config.services.prometheus.exporters.wireguard.port; sops.secrets."wg/privkey" = { reloadUnits = [ "systemd-networkd.service" ]; sopsFile = ../../instances/${config.networking.hostName}/secrets/wg.yaml; }; sops.secrets."wg/psk" = { reloadUnits = [ "systemd-networkd.service" ]; }; }; }