From 364ef3c59b8b532ad9f71d892a7aba11f5f201fd Mon Sep 17 00:00:00 2001 From: Cobalt Date: Mon, 28 Jul 2025 03:19:17 +0200 Subject: [PATCH 1/3] nixos/nebula: enable reloadable configuration optional support for reloading instead of restarting. This also moves the config into /etc/nebula/${netName}.yml, if stateVersion >= 25.11, regardless of enableReload. --- .../manual/release-notes/rl-2511.section.md | 2 + nixos/modules/services/networking/nebula.nix | 122 ++++++++++++------ 2 files changed, 83 insertions(+), 41 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md index 96659c6cbdd0..724c84fe3811 100644 --- a/nixos/doc/manual/release-notes/rl-2511.section.md +++ b/nixos/doc/manual/release-notes/rl-2511.section.md @@ -446,6 +446,8 @@ and [release notes for v18](https://goteleport.com/docs/changelog/#1800-070325). - `linux_libre` & `linux_latest_libre` have been removed due to a lack of maintenance. +- `services.nebula.networks.` will now store configuration files in `/etc/nebula/.yml` and supports config reloading. + - `services.pds` has been renamed to `services.bluesky-pds`. - `services.xserver.desktopManager.deepin` and associated packages have been removed due to being unmaintained. See issue [#422090](https://github.com/NixOS/nixpkgs/issues/422090) for more details. diff --git a/nixos/modules/services/networking/nebula.nix b/nixos/modules/services/networking/nebula.nix index 81a50f60bd44..d309054f50b0 100644 --- a/nixos/modules/services/networking/nebula.nix +++ b/nixos/modules/services/networking/nebula.nix @@ -9,8 +9,54 @@ let cfg = config.services.nebula; enabledNetworks = lib.filterAttrs (n: v: v.enable) cfg.networks; + genSettings = + netName: netCfg: + lib.recursiveUpdate { + pki = { + ca = netCfg.ca; + cert = netCfg.cert; + key = netCfg.key; + }; + static_host_map = netCfg.staticHostMap; + lighthouse = { + am_lighthouse = netCfg.isLighthouse; + hosts = netCfg.lighthouses; + serve_dns = netCfg.lighthouse.dns.enable; + dns.host = netCfg.lighthouse.dns.host; + dns.port = netCfg.lighthouse.dns.port; + }; + relay = { + am_relay = netCfg.isRelay; + relays = netCfg.relays; + use_relays = true; + }; + listen = { + host = netCfg.listen.host; + port = resolveFinalPort netCfg; + }; + tun = { + disabled = netCfg.tun.disable; + dev = if (netCfg.tun.device != null) then netCfg.tun.device else "nebula.${netName}"; + }; + firewall = { + inbound = netCfg.firewall.inbound; + outbound = netCfg.firewall.outbound; + }; + } netCfg.settings; format = pkgs.formats.yaml { }; + genConfigFile = + netName: settings: + format.generate "nebula-config-${netName}.yml" ( + lib.warnIf + ((settings.lighthouse.am_lighthouse || settings.relay.am_relay) && settings.listen.port == 0) + '' + Nebula network '${netName}' is configured as a lighthouse or relay, and its port is ${builtins.toString settings.listen.port}. + You will likely experience connectivity issues: https://nebula.defined.net/docs/config/listen/#listenport + '' + settings + ); + nameToId = netName: "nebula-${netName}"; resolveFinalPort = @@ -60,6 +106,16 @@ in example = "/etc/nebula/host.key"; }; + enableReload = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable automatic config reload on config change. + This setting is not enabled by default as nix cannot determine if the config change is reloadable. + Please refer to the [config reference](https://nebula.defined.net/docs/config/) for documentation on reloadable changes. + ''; + }; + staticHostMap = lib.mkOption { type = lib.types.attrsOf (lib.types.listOf (lib.types.str)); default = { }; @@ -212,47 +268,13 @@ in netName: netCfg: let networkId = nameToId netName; - settings = lib.recursiveUpdate { - pki = { - ca = netCfg.ca; - cert = netCfg.cert; - key = netCfg.key; - }; - static_host_map = netCfg.staticHostMap; - lighthouse = { - am_lighthouse = netCfg.isLighthouse; - hosts = netCfg.lighthouses; - serve_dns = netCfg.lighthouse.dns.enable; - dns.host = netCfg.lighthouse.dns.host; - dns.port = netCfg.lighthouse.dns.port; - }; - relay = { - am_relay = netCfg.isRelay; - relays = netCfg.relays; - use_relays = true; - }; - listen = { - host = netCfg.listen.host; - port = resolveFinalPort netCfg; - }; - tun = { - disabled = netCfg.tun.disable; - dev = if (netCfg.tun.device != null) then netCfg.tun.device else "nebula.${netName}"; - }; - firewall = { - inbound = netCfg.firewall.inbound; - outbound = netCfg.firewall.outbound; - }; - } netCfg.settings; - configFile = format.generate "nebula-config-${netName}.yml" ( - lib.warnIf - ((settings.lighthouse.am_lighthouse || settings.relay.am_relay) && settings.listen.port == 0) - '' - Nebula network '${netName}' is configured as a lighthouse or relay, and its port is ${builtins.toString settings.listen.port}. - You will likely experience connectivity issues: https://nebula.defined.net/docs/config/listen/#listenport - '' - settings - ); + settings = genSettings netName netCfg; + generatedConfigFile = genConfigFile netName settings; + configFile = + if ((lib.versionAtLeast config.system.stateVersion "25.11") || netCfg.enableReload) then + "/etc/nebula/${netName}.yml" + else + generatedConfigFile; capabilities = let nebulaPort = if !settings.tun.disabled then settings.listen.port else 0; @@ -278,6 +300,8 @@ in ]; before = [ "sshd.service" ]; wantedBy = [ "multi-user.target" ]; + restartTriggers = lib.optional (!netCfg.enableReload) generatedConfigFile; + reloadTriggers = lib.optional netCfg.enableReload generatedConfigFile; serviceConfig = { Type = "notify"; Restart = "always"; @@ -313,6 +337,22 @@ in ) enabledNetworks ); + environment.etc = lib.mkMerge ( + lib.mapAttrsToList + (netName: netCfg: { + "nebula/${netName}.yml" = { + source = genConfigFile netName (genSettings netName netCfg); + mode = "0440"; + user = nameToId netName; + }; + }) + ( + lib.filterAttrs ( + _: netCfg: netCfg.enableReload || (lib.versionAtLeast config.system.stateVersion "25.11") + ) enabledNetworks + ) + ); + # Open the chosen ports for UDP. networking.firewall.allowedUDPPorts = lib.unique ( lib.filter (port: port > 0) ( From 8fbaea70bdb75ecbc490a2c72051b33bfa0cd06c Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Sat, 27 Sep 2025 14:37:00 -0700 Subject: [PATCH 2/3] nixos/nebula: move test to subfolder --- nixos/tests/all-tests.nix | 2 +- nixos/tests/{nebula.nix => nebula/connectivity.nix} | 2 +- pkgs/by-name/ne/nebula/package.nix | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename nixos/tests/{nebula.nix => nebula/connectivity.nix} (99%) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 15f15e2fc212..4ade52acb47c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1011,7 +1011,7 @@ in defaults.services.ncps.cache.dataPath = "/path/to/ncps"; }; ndppd = runTest ./ndppd.nix; - nebula = runTest ./nebula.nix; + nebula.connectivity = runTest ./nebula/connectivity.nix; neo4j = runTest ./neo4j.nix; netbird = runTest ./netbird.nix; netbox-upgrade = runTest ./web-apps/netbox-upgrade.nix; diff --git a/nixos/tests/nebula.nix b/nixos/tests/nebula/connectivity.nix similarity index 99% rename from nixos/tests/nebula.nix rename to nixos/tests/nebula/connectivity.nix index 127f6b06a878..bf29838021e1 100644 --- a/nixos/tests/nebula.nix +++ b/nixos/tests/nebula/connectivity.nix @@ -2,7 +2,7 @@ let # We'll need to be able to trade cert files between nodes via scp. - inherit (import ./ssh-keys.nix pkgs) + inherit (import ../ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey ; diff --git a/pkgs/by-name/ne/nebula/package.nix b/pkgs/by-name/ne/nebula/package.nix index b17fb8a3651f..3ddad20ac370 100644 --- a/pkgs/by-name/ne/nebula/package.nix +++ b/pkgs/by-name/ne/nebula/package.nix @@ -26,7 +26,7 @@ buildGoModule rec { ldflags = [ "-X main.Build=${version}" ]; passthru.tests = { - inherit (nixosTests) nebula; + inherit (nixosTests.nebula) connectivity; }; meta = { From cf07ac9e15fc92d78e3a51cd4b58ce7764d23c74 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Sat, 27 Sep 2025 21:04:23 -0700 Subject: [PATCH 3/3] nixos/nebula: add test for reloadable configuration --- nixos/tests/all-tests.nix | 1 + nixos/tests/nebula/reload.nix | 92 ++++++++++++++++++++++++++++++ pkgs/by-name/ne/nebula/package.nix | 5 +- 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 nixos/tests/nebula/reload.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 4ade52acb47c..f37373c70cee 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1012,6 +1012,7 @@ in }; ndppd = runTest ./ndppd.nix; nebula.connectivity = runTest ./nebula/connectivity.nix; + nebula.reload = runTest ./nebula/reload.nix; neo4j = runTest ./neo4j.nix; netbird = runTest ./netbird.nix; netbox-upgrade = runTest ./web-apps/netbox-upgrade.nix; diff --git a/nixos/tests/nebula/reload.nix b/nixos/tests/nebula/reload.nix new file mode 100644 index 000000000000..b1a8bdc75745 --- /dev/null +++ b/nixos/tests/nebula/reload.nix @@ -0,0 +1,92 @@ +{ pkgs, lib, ... }: +let + + inherit (import ../ssh-keys.nix pkgs) + snakeOilPrivateKey + snakeOilPublicKey + ; + +in +{ + name = "nebula"; + + nodes = { + lighthouse = + { + pkgs, + lib, + config, + ... + }: + { + environment.systemPackages = [ pkgs.nebula ]; + environment.etc."nebula-key" = { + user = "nebula-smoke"; + group = "nebula-smoke"; + source = snakeOilPrivateKey; + mode = "0600"; + }; + + services.nebula.networks.smoke = { + # Note that these paths won't exist when the machine is first booted. + ca = "/etc/nebula/ca.crt"; + cert = "/etc/nebula/lighthouse.crt"; + key = "/etc/nebula/lighthouse.key"; + isLighthouse = true; + listen = { + host = "0.0.0.0"; + port = 4242; + }; + enableReload = true; + settings.sshd = { + enabled = true; + listen = "127.0.0.1:2222"; + host_key = "/etc/nebula-key"; + }; + }; + + # We will test that nebula is reloaded by switching specialisations. + specialisation.sshd-off.configuration = { + services.nebula.networks.smoke.settings.sshd.enabled = lib.mkForce false; + }; + specialisation.sshd-on.configuration = { + services.nebula.networks.smoke.settings.sshd.enabled = lib.mkForce true; + }; + }; + }; + + testScript = + { nodes, ... }: + let + sshd-on = "${nodes.lighthouse.system.build.toplevel}/specialisation/sshd-on"; + sshd-off = "${nodes.lighthouse.system.build.toplevel}/specialisation/sshd-off"; + in + '' + # Create the certificate and sign the lighthouse's keys. + lighthouse.succeed( + "mkdir -p /etc/nebula", + 'nebula-cert ca -duration $((10*365*24*60))m -name "Smoke Test" -out-crt /etc/nebula/ca.crt -out-key /etc/nebula/ca.key', + 'nebula-cert sign -duration $((365*24*60))m -ca-crt /etc/nebula/ca.crt -ca-key /etc/nebula/ca.key -name "lighthouse" -groups "lighthouse" -ip "10.0.100.1/24" -out-crt /etc/nebula/lighthouse.crt -out-key /etc/nebula/lighthouse.key', + 'chown -R nebula-smoke:nebula-smoke /etc/nebula' + ) + + # Restart nebula to pick up the keys. + lighthouse.systemctl("restart nebula@smoke.service") + lighthouse.succeed("ping -c5 10.0.100.1") + + # Verify that nebula's ssh interface is up. + lighthouse.succeed("${pkgs.nmap}/bin/nmap 127.0.0.1 | grep 2222/tcp") + + # Switch configuration, verify nebula was reloaded and not restarted. + lighthouse.succeed("${sshd-off}/bin/switch-to-configuration test 2>&1 | grep 'nebula' | grep 'reload'") + + # Verify that nebula's ssh interface is no longer up. + lighthouse.fail("${pkgs.nmap}/bin/nmap 127.0.0.1 | grep 2222/tcp") + + # Switch configuration, verify reload again. + lighthouse.succeed("${sshd-on}/bin/switch-to-configuration test 2>&1 | grep 'nebula' | grep 'reload'") + + # Verify that ssh is back. + lighthouse.succeed("${pkgs.nmap}/bin/nmap 127.0.0.1 | grep 2222/tcp") + ''; +} diff --git a/pkgs/by-name/ne/nebula/package.nix b/pkgs/by-name/ne/nebula/package.nix index 3ddad20ac370..80ce794aec44 100644 --- a/pkgs/by-name/ne/nebula/package.nix +++ b/pkgs/by-name/ne/nebula/package.nix @@ -26,7 +26,10 @@ buildGoModule rec { ldflags = [ "-X main.Build=${version}" ]; passthru.tests = { - inherit (nixosTests.nebula) connectivity; + inherit (nixosTests.nebula) + connectivity + reload + ; }; meta = {