From 52e01114beb7e915d74eb97f1dce98d584dc5458 Mon Sep 17 00:00:00 2001 From: "Janik H." Date: Fri, 1 Mar 2024 01:38:35 +0100 Subject: [PATCH 1/5] nixosTests.networking: refactor and add NetworkManager support --- nixos/tests/all-tests.nix | 1 + nixos/tests/networking/networkmanager.nix | 169 ++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 nixos/tests/networking/networkmanager.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index a99fedaddd76..0d5b442a17bc 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -598,6 +598,7 @@ in { netdata = handleTest ./netdata.nix {}; networking.scripted = handleTest ./networking/networkd-and-scripted.nix { networkd = false; }; networking.networkd = handleTest ./networking/networkd-and-scripted.nix { networkd = true; }; + networking.networkmanager = handleTest ./networking/networkmanager.nix {}; netbox_3_6 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_6; }; netbox_3_7 = handleTest ./web-apps/netbox.nix { netbox = pkgs.netbox_3_7; }; netbox-upgrade = handleTest ./web-apps/netbox-upgrade.nix {}; diff --git a/nixos/tests/networking/networkmanager.nix b/nixos/tests/networking/networkmanager.nix new file mode 100644 index 000000000000..14dbafc6305d --- /dev/null +++ b/nixos/tests/networking/networkmanager.nix @@ -0,0 +1,169 @@ +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +}: + +with import ../../lib/testing-python.nix { inherit system pkgs; }; + +let + lib = pkgs.lib; + # this is intended as a client test since you shouldn't use NetworkManager for a router or server + # so using systemd-networkd for the router vm is fine in these tests. + router = import ./router.nix { networkd = true; }; + qemu-common = import ../../lib/qemu-common.nix { inherit (pkgs) lib pkgs; }; + clientConfig = extraConfig: lib.recursiveUpdate { + networking.useDHCP = false; + + # Make sure that only NetworkManager configures the interface + networking.interfaces = lib.mkForce { + eth1 = {}; + }; + networking.networkmanager = { + enable = true; + # this is needed so NM doesn't generate 'Wired Connection' profiles and instead uses the default one + settings.main.no-auto-default = "*"; + ensureProfiles.profiles.default = { + connection = { + id = "default"; + type = "ethernet"; + interface-name = "eth1"; + autoconnect = true; + }; + }; + }; + } extraConfig; + testCases = { + static = { + name = "static"; + nodes = { + inherit router; + client = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4.method = "manual"; + ipv4.addresses = "192.168.1.42/24"; + ipv4.gateway = "192.168.1.1"; + ipv6.method = "manual"; + ipv6.addresses = "fd00:1234:5678:1::42/64"; + ipv6.gateway = "fd00:1234:5678:1::1"; + }; + }; + }; + testScript = '' + start_all() + router.wait_for_unit("network-online.target") + client.wait_for_unit("NetworkManager.service") + + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + + with subtest("Test if icmp echo works"): + client.wait_until_succeeds("ping -c 1 192.168.3.1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1") + router.wait_until_succeeds("ping -c 1 192.168.1.42") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::42") + ''; + }; + auto = { + name = "auto"; + nodes = { + inherit router; + client = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4.method = "auto"; + ipv6.method = "auto"; + }; + }; + }; + testScript = '' + start_all() + router.wait_for_unit("network-online.target") + client.wait_for_unit("NetworkManager.service") + + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + + with subtest("Test if icmp echo works"): + client.wait_until_succeeds("ping -c 1 192.168.1.1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2") + ''; + }; + dns = { + name = "dns"; + nodes = { + inherit router; + dynamic = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4.method = "auto"; + }; + }; + static = clientConfig { + networking.networkmanager.ensureProfiles.profiles.default = { + ipv4 = { + method = "auto"; + ignore-auto-dns = "true"; + dns = "10.10.10.10"; + dns-search = ""; + }; + }; + }; + }; + testScript = '' + start_all() + router.wait_for_unit("network-online.target") + dynamic.wait_for_unit("NetworkManager.service") + static.wait_for_unit("NetworkManager.service") + + dynamic.wait_until_succeeds("cat /etc/resolv.conf | grep -q '192.168.1.1'") + static.wait_until_succeeds("cat /etc/resolv.conf | grep -q '10.10.10.10'") + static.wait_until_fails("cat /etc/resolv.conf | grep -q '192.168.1.1'") + ''; + }; + dispatcherScripts = { + name = "dispatcherScripts"; + nodes.client = clientConfig { + networking.networkmanager.dispatcherScripts = [{ + type = "pre-up"; + source = pkgs.writeText "testHook" '' + touch /tmp/dispatcher-scripts-are-working + ''; + }]; + }; + testScript = '' + start_all() + client.wait_for_unit("NetworkManager.service") + client.wait_until_succeeds("stat /tmp/dispatcher-scripts-are-working") + ''; + }; + envsubst = { + name = "envsubst"; + nodes.client = let + # you should never write secrets in to your nixos configuration, please use tools like sops-nix or agenix + secretFile = pkgs.writeText "my-secret.env" '' + MY_SECRET_IP=fd00:1234:5678:1::23/64 + ''; + in clientConfig { + networking.networkmanager.ensureProfiles.environmentFiles = [ secretFile ]; + networking.networkmanager.ensureProfiles.profiles.default = { + ipv6.method = "manual"; + ipv6.addresses = "$MY_SECRET_IP"; + }; + }; + testScript = '' + start_all() + client.wait_for_unit("NetworkManager.service") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::23") + ''; + }; + }; +in lib.mapAttrs (lib.const (attrs: makeTest (attrs // { + name = "${attrs.name}-Networking-NetworkManager"; + meta = { + maintainers = with lib.maintainers; [ janik ]; + }; + +}))) testCases From 8612ed1ee9be2ee0240e51672e2aae1880f3427f Mon Sep 17 00:00:00 2001 From: "Janik H." Date: Sat, 2 Mar 2024 17:38:25 +0100 Subject: [PATCH 2/5] nixos/networkmanager: change config generation to use the ini generator --- .../manual/release-notes/rl-2405.section.md | 2 + .../services/networking/networkmanager.nix | 84 ++++++++++--------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index 58ceb99b9d7b..f568b5e62002 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -581,6 +581,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m - The `hardware.pulseaudio` module now sets permission of pulse user home directory to 755 when running in "systemWide" mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399). +- The `services.networkmanager.extraConfig` was renamed to `services.networkmanager.settings` and was changed to use the ini type instead of using a multiline string. + - The module `services.github-runner` has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration. - The `services.slskd` has been refactored to include more configuation options in diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index a021798e0e4b..e33bbb2af178 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -10,49 +10,31 @@ let enableIwd = cfg.wifi.backend == "iwd"; - mkValue = v: - if v == true then "yes" - else if v == false then "no" - else if lib.isInt v then toString v - else v; - - mkSection = name: attrs: '' - [${name}] - ${ - lib.concatStringsSep "\n" - (lib.mapAttrsToList - (k: v: "${k}=${mkValue v}") - (lib.filterAttrs - (k: v: v != null) - attrs)) - } - ''; - - configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [ - (mkSection "main" { + configAttrs = lib.recursiveUpdate { + main = { plugins = "keyfile"; inherit (cfg) dhcp dns; # If resolvconf is disabled that means that resolv.conf is managed by some other module. rc-manager = if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"; - }) - (mkSection "keyfile" { + }; + keyfile = { unmanaged-devices = - if cfg.unmanaged == [ ] then null - else lib.concatStringsSep ";" cfg.unmanaged; - }) - (mkSection "logging" { + if cfg.unmanaged == [ ] then null + else lib.concatStringsSep ";" cfg.unmanaged; + }; + logging = { audit = config.security.audit.enable; level = cfg.logLevel; - }) - (mkSection "connection" cfg.connectionConfig) - (mkSection "device" { - "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress; - "wifi.backend" = cfg.wifi.backend; - }) - cfg.extraConfig - ]); + }; + connection = cfg.connectionConfig; + device = { + "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress; + "wifi.backend" = cfg.wifi.backend; + }; + } cfg.settings; + configFile = ini.generate "NetworkManager.conf" configAttrs; /* [network-manager] @@ -145,7 +127,7 @@ in { meta = { - maintainers = teams.freedesktop.members; + maintainers = teams.freedesktop.members ++ [ lib.maintainers.janik ]; }; ###### interface @@ -185,11 +167,11 @@ in ''; }; - extraConfig = mkOption { - type = types.lines; - default = ""; + settings = mkOption { + type = ini.type; + default = {}; description = '' - Configuration appended to the generated NetworkManager.conf. + Configuration added to the generated NetworkManager.conf, note that you can overwrite settings with this. Refer to [ https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html @@ -471,8 +453,28 @@ in imports = [ (mkRenamedOptionModule [ "networking" "networkmanager" "packages" ] - [ "networking" "networkmanager" "plugins" ]) - (mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ]) + [ "networking" "networkmanager" "plugins" ] + ) + (mkRenamedOptionModule + [ "networking" "networkmanager" "useDnsmasq" ] + [ "networking" "networkmanager" "dns" ] + ) + (mkRemovedOptionModule [ "networking" "networkmanager" "extraConfig" ] '' + This option was removed in favour of `networking.networkmanager.settings`, + which accepts structured nix-code equivalent to the ini + and allows for overriding settings. + Example patch: + ```patch + networking.networkmanager = { + - extraConfig = ''' + - [main] + - no-auto-default=* + - ''' + + extraConfig.main.no-auto-default = "*"; + }; + ``` + '' + ) (mkRemovedOptionModule [ "networking" "networkmanager" "enableFccUnlock" ] '' This option was removed, because using bundled FCC unlock scripts is risky, might conflict with vendor-provided unlock scripts, and should From 51cccffa951d0bf44ad690eb9f58b11089586a20 Mon Sep 17 00:00:00 2001 From: "Janik H." Date: Sat, 2 Mar 2024 17:51:18 +0100 Subject: [PATCH 3/5] networkmanager: add passthru.tests --- pkgs/tools/networking/networkmanager/default.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/tools/networking/networkmanager/default.nix b/pkgs/tools/networking/networkmanager/default.nix index c1e14dcfecb1..67de0c877d0d 100644 --- a/pkgs/tools/networking/networkmanager/default.nix +++ b/pkgs/tools/networking/networkmanager/default.nix @@ -50,6 +50,7 @@ , mobile-broadband-provider-info , runtimeShell , buildPackages +, nixosTests }: let @@ -203,6 +204,9 @@ stdenv.mkDerivation rec { attrPath = "networkmanager"; versionPolicy = "odd-unstable"; }; + tests = { + inherit (nixosTests.networking) networkmanager; + }; }; meta = with lib; { From 8b31909a795c61afadb4441f88c8409965d44b72 Mon Sep 17 00:00:00 2001 From: "Janik H." Date: Mon, 4 Mar 2024 23:42:37 +0100 Subject: [PATCH 4/5] codeowners: add Janik to NetworkManager module and tests --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2c3532ae9c9e..aff7aee0f797 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -232,10 +232,12 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt /nixos/modules/services/networking/babeld.nix @mweinelt /nixos/modules/services/networking/kea.nix @mweinelt /nixos/modules/services/networking/knot.nix @mweinelt +nixos/modules/services/networking/networkmanager.nix @Janik-Haag /nixos/modules/services/monitoring/prometheus/exporters/kea.nix @mweinelt /nixos/tests/babeld.nix @mweinelt /nixos/tests/kea.nix @mweinelt /nixos/tests/knot.nix @mweinelt +/nixos/tests/networking/* @Janik-Haag # Web servers /doc/packages/nginx.section.md @raitobezarius From b1fd84f6ec16aec5fa8cd65201b3fc856188f7d6 Mon Sep 17 00:00:00 2001 From: "Janik H." Date: Thu, 18 Apr 2024 00:21:04 +0200 Subject: [PATCH 5/5] nixosTests.networking: start network-online.target manually this is done to avoid future problems for when `network-online.target` doesn't depend on `multi-user.target` --- nixos/tests/networking/networkmanager.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nixos/tests/networking/networkmanager.nix b/nixos/tests/networking/networkmanager.nix index 14dbafc6305d..e654e37d7efb 100644 --- a/nixos/tests/networking/networkmanager.nix +++ b/nixos/tests/networking/networkmanager.nix @@ -50,6 +50,7 @@ let }; testScript = '' start_all() + router.systemctl("start network-online.target") router.wait_for_unit("network-online.target") client.wait_for_unit("NetworkManager.service") @@ -77,6 +78,7 @@ let }; testScript = '' start_all() + router.systemctl("start network-online.target") router.wait_for_unit("network-online.target") client.wait_for_unit("NetworkManager.service") @@ -113,6 +115,7 @@ let }; testScript = '' start_all() + router.systemctl("start network-online.target") router.wait_for_unit("network-online.target") dynamic.wait_for_unit("NetworkManager.service") static.wait_for_unit("NetworkManager.service")