diff --git a/hosts/vps-monitor/default.nix b/hosts/vps-monitor/default.nix index 62d91a7..af385b3 100644 --- a/hosts/vps-monitor/default.nix +++ b/hosts/vps-monitor/default.nix @@ -29,6 +29,7 @@ routableAddress = "188.245.223.145"; isLighthouse = true; isServer = true; + dns.enable = true; }; }; diff --git a/hosts/vps-private/default.nix b/hosts/vps-private/default.nix index 9ee3814..547d9c1 100644 --- a/hosts/vps-private/default.nix +++ b/hosts/vps-private/default.nix @@ -36,6 +36,7 @@ routableAddress = "49.13.231.235"; isLighthouse = true; isServer = true; + dns.enable = true; }; syncthing = { diff --git a/modules/system/services/nebula/default.nix b/modules/system/services/nebula/default.nix index f8532e9..20d4017 100644 --- a/modules/system/services/nebula/default.nix +++ b/modules/system/services/nebula/default.nix @@ -5,23 +5,41 @@ ... }: let - cfg = config.custom.services.nebula.node; - peers = config.custom.services.nebula.peers; + nebulaCfg = config.custom.services.nebula; + cfg = nebulaCfg.node; hostname = config.networking.hostName; - - lighthouses = peers |> lib.filter (node: node.isLighthouse); - - routablePeers = peers |> lib.filter (node: node.routableAddress != null); in { options.custom.services.nebula = { + network = { + address = lib.mkOption { + type = lib.types.nonEmptyStr; + default = "10.254.250.0"; + readOnly = true; + }; + prefixLength = lib.mkOption { + type = lib.types.ints.between 0 32; + default = 24; + readOnly = true; + }; + domain = lib.mkOption { + type = lib.types.nonEmptyStr; + default = "splitleaf.de"; + readOnly = true; + }; + }; + node = { enable = lib.mkEnableOption ""; name = lib.mkOption { type = lib.types.nonEmptyStr; default = hostname; }; + interface = lib.mkOption { + type = lib.types.nonEmptyStr; + default = "nebula.mesh"; + }; address = lib.mkOption { type = lib.types.nonEmptyStr; default = ""; @@ -49,16 +67,20 @@ in }; }; - peers = lib.mkOption { + nodes = lib.mkOption { type = lib.types.anything; default = self.nixosConfigurations - |> lib.filterAttrs (name: _: name != hostname) |> lib.attrValues |> lib.map (value: value.config.custom.services.nebula.node) |> lib.filter (node: node.enable); readOnly = true; }; + peers = lib.mkOption { + type = lib.types.anything; + default = nebulaCfg.nodes |> lib.filter (node: node.name != hostname); + readOnly = true; + }; }; config = lib.mkIf cfg.enable { @@ -83,13 +105,14 @@ in listen.port = cfg.routablePort; - isLighthouse = cfg.isLighthouse; + inherit (cfg) isLighthouse; lighthouses = lib.mkIf (!cfg.isLighthouse) ( - lighthouses |> lib.map (lighthouse: lighthouse.address) + nebulaCfg.peers |> lib.filter (node: node.isLighthouse) |> lib.map (lighthouse: lighthouse.address) ); staticHostMap = - routablePeers + nebulaCfg.peers + |> lib.filter (node: node.routableAddress != null) |> lib.map (lighthouse: { name = lighthouse.address; value = lib.singleton "${lighthouse.routableAddress}:${toString lighthouse.routablePort}"; @@ -116,6 +139,13 @@ in }; }; - networking.firewall.trustedInterfaces = [ "nebula.mesh" ]; + networking.firewall.trustedInterfaces = [ cfg.interface ]; + + systemd.network.networks."40-nebula" = { + matchConfig.Name = cfg.interface; + address = [ "${cfg.address}/${toString nebulaCfg.network.prefixLength}" ]; + dns = nebulaCfg.peers |> lib.filter (node: node.dns.enable) |> lib.map (node: node.address); + domains = [ nebulaCfg.network.domain ]; + }; }; } diff --git a/modules/system/services/nebula/dns.nix b/modules/system/services/nebula/dns.nix new file mode 100644 index 0000000..f750fc1 --- /dev/null +++ b/modules/system/services/nebula/dns.nix @@ -0,0 +1,54 @@ +{ config, lib, ... }: +let + nebulaCfg = config.custom.services.nebula; + cfg = nebulaCfg.node; +in +{ + options.custom.services.nebula.node.dns.enable = lib.mkEnableOption ""; + + config = lib.mkIf (cfg.enable && cfg.dns.enable) { + # meta.ports = { + # tcp = [ 53 ]; + # udp = [ 53 ]; + # }; + + services = { + unbound = { + enable = true; + + settings = { + server = { + interface = [ cfg.interface ]; + access-control = [ + "${nebulaCfg.network.address}/${toString nebulaCfg.network.prefixLength} allow" + ]; + + local-zone = "\"${nebulaCfg.network.domain}.\" static"; + local-data = + nebulaCfg.nodes + |> lib.map (node: "\"${node.name}.${nebulaCfg.network.domain}. A ${node.address}\""); + }; + + forward-zone = lib.singleton { + name = "."; + forward-addr = [ + "1.1.1.1" + "8.8.8.8" + ]; + }; + }; + }; + + nebula.networks.mesh.firewall.inbound = lib.singleton { + port = 53; + proto = "any"; + host = "any"; + }; + }; + + systemd.services.unbound = { + requires = [ "nebula@mesh.service" ]; + after = [ "nebula@mesh.service" ]; + }; + }; +} diff --git a/modules/system/services/tailscale.nix b/modules/system/services/tailscale.nix index 28fc6e1..8f37da5 100644 --- a/modules/system/services/tailscale.nix +++ b/modules/system/services/tailscale.nix @@ -35,5 +35,13 @@ in systemd.services.tailscaled-set.after = [ "tailscaled-autoconnect.service" ]; custom.persistence.directories = [ "/var/lib/tailscale" ]; + + # Disable search domain when nebula is in use + systemd.network.networks."50-tailscale" = lib.mkIf config.custom.services.nebula.node.enable { + matchConfig.Name = config.services.tailscale.interfaceName; + linkConfig.Unmanaged = lib.mkForce false; + dns = [ "100.100.100.100" ]; + domains = [ ]; + }; }; }