From e687b19a55ea8a11c058ec0bdfd9db82c319787b Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Tue, 9 Dec 2025 09:17:54 +0000 Subject: [PATCH 01/17] frei0r: 2.5.0 -> 2.5.1 --- pkgs/by-name/fr/frei0r/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/fr/frei0r/package.nix b/pkgs/by-name/fr/frei0r/package.nix index 1f1a1e862ef2..039a7c0e6687 100644 --- a/pkgs/by-name/fr/frei0r/package.nix +++ b/pkgs/by-name/fr/frei0r/package.nix @@ -13,13 +13,13 @@ stdenv.mkDerivation rec { pname = "frei0r-plugins"; - version = "2.5.0"; + version = "2.5.1"; src = fetchFromGitHub { owner = "dyne"; repo = "frei0r"; rev = "v${version}"; - hash = "sha256-JEQndfQOcSARGIPtMwteUqWqTLPEMcpF2F/xD1PsDEU="; + hash = "sha256-3gUWvO5izOrJt+XwcNBNiLfu+iMqo4nuPbx++TYzao0="; }; nativeBuildInputs = [ From abb06beb97f14e592131f2396f56d84226aad4b5 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Tue, 22 Jul 2025 11:47:12 +0200 Subject: [PATCH 02/17] nixos/wpa_supplicant: harden and run as unprivileged user --- .../services/networking/wpa_supplicant.nix | 209 ++++++++++++------ nixos/tests/wpa_supplicant.nix | 55 +++-- .../linux/wpa_supplicant/default.nix | 8 +- .../wpa_supplicant/unprivileged-daemon.patch | 96 ++++++++ 4 files changed, 283 insertions(+), 85 deletions(-) create mode 100644 pkgs/os-specific/linux/wpa_supplicant/unprivileged-daemon.patch diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index 5bcc5e5c38d8..8fa11f9ef87e 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -48,31 +48,6 @@ let else networkList; - # Content of wpa_supplicant.conf - generatedConfig = concatStringsSep "\n" ( - (map mkNetwork allNetworks) - ++ optional cfg.userControlled.enable ( - concatStringsSep "\n" [ - "ctrl_interface=/run/wpa_supplicant" - "ctrl_interface_group=${cfg.userControlled.group}" - "update_config=1" - ] - ) - ++ [ "pmf=1" ] - ++ optional (cfg.secretsFile != null) "ext_password_backend=file:${cfg.secretsFile}" - ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"'' - ++ optional (cfg.extraConfig != "") cfg.extraConfig - ); - - configIsGenerated = with cfg; networks != { } || extraConfig != "" || userControlled.enable; - - # the original configuration file - configFile = - if configIsGenerated then - pkgs.writeText "wpa_supplicant.conf" generatedConfig - else - "/etc/wpa_supplicant.conf"; - # Creates a network block for wpa_supplicant.conf mkNetwork = opts: @@ -104,6 +79,12 @@ let } ''; + hasDeclarative = lib.any id [ + (cfg.networks != { }) + (cfg.extraConfig != "") + cfg.userControlled + ]; + # Creates a systemd unit for wpa_supplicant bound to a given (or any) interface mkUnit = iface: @@ -114,9 +95,11 @@ let configStr = ( if cfg.allowAuxiliaryImperativeNetworks then - "-c /etc/wpa_supplicant.conf -I ${configFile}" + "-c /etc/wpa_supplicant/imperative.conf -I /etc/wpa_supplicant/nixos.conf" + else if hasDeclarative then + "-c /etc/wpa_supplicant/nixos.conf" else - "-c ${configFile}" + "-c /etc/wpa_supplicant/imperative.conf" ) + lib.concatMapStrings (p: " -I " + p) cfg.extraConfigFiles; in @@ -128,30 +111,93 @@ let wants = [ "network.target" ]; requires = deviceUnit; wantedBy = [ "multi-user.target" ]; + stopIfChanged = false; + restartTriggers = [ config.environment.etc."wpa_supplicant/nixos.conf".source ]; path = [ pkgs.wpa_supplicant ]; - # if `userControl.enable`, the supplicant automatically changes the permissions - # and owning group of the runtime dir; setting `umask` ensures the generated - # config file isn't readable (except to root); see nixpkgs#267693 - serviceConfig.UMask = "066"; - serviceConfig.RuntimeDirectory = "wpa_supplicant"; - serviceConfig.RuntimeDirectoryMode = "700"; + serviceConfig = { + User = "wpa_supplicant"; + Group = "wpa_supplicant"; + RuntimeDirectory = "wpa_supplicant"; + AmbientCapabilities = [ + "CAP_NET_ADMIN" + "CAP_NET_RAW" + ]; + CapabilityBoundingSet = [ + "CAP_NET_ADMIN" + "CAP_NET_RAW" + ]; + RootDirectory = "/run/wpa_supplicant"; + RootDirectoryStartOnly = true; + BindPaths = [ + "/etc/wpa_supplicant" # to write wpa_supplicant.conf{,.tmp} + "/run/wpa_supplicant" # to make control sockets + # to set up interfaces + "/proc/sys/net" + "/dev/rfkill" + ] + ++ lib.optional cfg.dbusControlled "/run/dbus" + ++ lib.optional cfg.allowAuxiliaryImperativeNetworks "/etc/wpa_supplicant"; + BindReadOnlyPaths = [ + builtins.storeDir + "/etc/" + ] + ++ lib.optional (cfg.secretsFile != null) cfg.secretsFile; + DeviceAllow = "/dev/rfkill rw"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = false; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + IPAddressDeny = "any"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_PACKET" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallFilter = [ + "@system-service" + "~@keyring" + "~@resources" + ]; + SystemCallArchitectures = "native"; + UMask = "0077"; + + ExecStartPre = + lib.optionals (cfg.allowAuxiliaryImperativeNetworks || !hasDeclarative) [ + # set up imperative config file + "+${pkgs.coreutils}/bin/touch /etc/wpa_supplicant/imperative.conf" + "+${pkgs.coreutils}/bin/chmod 664 /etc/wpa_supplicant/imperative.conf" + "+${pkgs.coreutils}/bin/chown -R wpa_supplicant:wpa_supplicant /etc/wpa_supplicant" + ] + ++ lib.optionals cfg.userControlled [ + # set up client sockets directory + "+${pkgs.coreutils}/bin/mkdir /run/wpa_supplicant/client" + "+${pkgs.coreutils}/bin/chown wpa_supplicant:wpa_supplicant /run/wpa_supplicant/client" + "+${pkgs.coreutils}/bin/chmod g=u /run/wpa_supplicant/client" + ]; + }; script = '' - ${optionalString (configIsGenerated && !cfg.allowAuxiliaryImperativeNetworks) '' - if [ -f /etc/wpa_supplicant.conf ]; then - echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead." - fi - ''} - - # ensure wpa_supplicant.conf exists, or the daemon will fail to start - ${optionalString cfg.allowAuxiliaryImperativeNetworks '' - touch /etc/wpa_supplicant.conf - ''} - iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}" - ${ if iface == null then '' @@ -503,27 +549,36 @@ in ''; }; - userControlled = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. - This is useful for laptop users that switch networks a lot and don't want - to depend on a large package such as NetworkManager just to pick nearby - access points. + userControlled = mkOption { + type = + with types; + coercedTo attrs ( + val: + if builtins.isAttrs val && val ? enable then + trace "Obsolete option `networking.wireless.userControlled.enable' is used. It was renamed to networking.wireless.userControlled" val.enable + else if builtins.isAttrs val && val ? group then + trace + "The option definition `networking.wireless.userControlled.group' no longer has any effect. The group is now fixed to `wpa_supplicant'." + (val.enable or false) + else if builtins.isBool val then + val + else + false + ) bool; + default = false; + description = '' + Allow users of the `wpa_supplicant` group to control wpa_supplicant + through wpa_gui or wpa_cli. + This is useful for laptop users that switch networks a lot and don't want + to depend on a large package such as NetworkManager just to pick nearby + access points. - When using a declarative network specification you cannot persist any - settings via wpa_gui or wpa_cli. - ''; - }; - - group = mkOption { - type = types.str; - default = "wheel"; - example = "network"; - description = "Members of this group can control wpa_supplicant."; - }; + ::: {.note} + When networks are configured declaratively, you cannot persist any settings + via wpa_gui or wpa_cli, unless {option}`allowAuxiliaryImperativeNetworks` + is used. + ::: + ''; }; dbusControlled = mkOption { @@ -624,9 +679,33 @@ in } ]; + users.groups.wpa_supplicant = { }; + users.users.wpa_supplicant = { + isSystemUser = true; + group = "wpa_supplicant"; + description = "WPA Supplicant user"; + }; + hardware.wirelessRegulatoryDatabase = true; environment.systemPackages = [ pkgs.wpa_supplicant ]; + + # NixOS-generated configuration files + environment.etc."wpa_supplicant/nixos.conf".text = concatStringsSep "\n" ( + (map mkNetwork allNetworks) + ++ optional cfg.userControlled ( + concatStringsSep "\n" [ + "ctrl_interface=/run/wpa_supplicant/control" + "ctrl_interface_group=wpa_supplicant" + "update_config=1" + ] + ) + ++ [ "pmf=1" ] + ++ optional (cfg.secretsFile != null) "ext_password_backend=file:${cfg.secretsFile}" + ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"'' + ++ optional (cfg.extraConfig != "") cfg.extraConfig + ); + services.dbus.packages = optional cfg.dbusControlled pkgs.wpa_supplicant; systemd.services = diff --git a/nixos/tests/wpa_supplicant.nix b/nixos/tests/wpa_supplicant.nix index d7aaaf8a1429..921c111cfc1f 100644 --- a/nixos/tests/wpa_supplicant.nix +++ b/nixos/tests/wpa_supplicant.nix @@ -94,20 +94,30 @@ let }; }; + # Note: secrets are stored outside /etc/ and /nix/store to + # test for accessibility of these paths + system.activationScripts.wpa-secrets = { + deps = [ + "users" + "specialfs" + ]; + text = '' + install -Dm600 -o wpa_supplicant ${pkgs.writeText "wpa" '' + psk_nixos_test=${naughtyPassphrase} + ''} /var/lib/secrets/wpa + ''; + }; + # wireless client networking.wireless = lib.mkMerge [ { # the override is needed because the wifi is # disabled with mkVMOverride in qemu-vm.nix. enable = lib.mkOverride 0 true; - userControlled.enable = true; + userControlled = true; interfaces = [ "wlan1" ]; fallbackToWPA2 = lib.mkDefault true; - - # secrets - secretsFile = pkgs.writeText "wpa-secrets" '' - psk_nixos_test=${naughtyPassphrase} - ''; + secretsFile = "/var/lib/secrets/wpa"; } extraConfig ]; @@ -142,7 +152,8 @@ in # the override is needed because the wifi is # disabled with mkVMOverride in qemu-vm.nix. enable = lib.mkOverride 0 true; - userControlled.enable = true; + userControlled = true; + dbusControlled = true; fallbackToWPA2 = true; networks = { @@ -198,9 +209,14 @@ in assert "Failed to connect" not in status, \ "Failed to connect to the daemon" - # get the configuration file - cmdline = machine.succeed("cat /proc/$(pgrep wpa)/cmdline").split('\x00') - config_file = cmdline[cmdline.index("-c") + 1] + with subtest("D-Bus interface is working"): + dbus_command = "dbus-send --system --print-reply --dest=fi.w1.wpa_supplicant1 " \ + "/fi/w1/wpa_supplicant1 fi.w1.wpa_supplicant1.GetInterface string:wlan0" + machine.succeed(dbus_command) # as root + machine.succeed(f"sudo -g wpa_supplicant {dbus_command}") # as wpa_supplicant group + + # generated configuration file + config_file = "/etc/static/wpa_supplicant/nixos.conf" with subtest("WPA2 fallbacks have been generated"): assert int(machine.succeed(f"grep -c sae-only {config_file}")) == 1 @@ -218,6 +234,9 @@ in # save file for manual inspection machine.copy_from_vm(config_file) + + # check hardening options + machine.succeed("systemd-analyze security wpa_supplicant >&2") ''; }; @@ -233,25 +252,27 @@ in # wireless client networking.wireless = { enable = lib.mkOverride 0 true; - userControlled.enable = true; + userControlled = true; allowAuxiliaryImperativeNetworks = true; interfaces = [ "wlan1" ]; }; }; testScript = '' + wpa_cli = "sudo -u nobody -g wpa_supplicant wpa_cli" + with subtest("Daemon is running and accepting connections"): machine.wait_for_unit("wpa_supplicant-wlan1.service") - status = machine.wait_until_succeeds("wpa_cli -i wlan1 status") + status = machine.wait_until_succeeds(f"{wpa_cli} -i wlan1 status") assert "Failed to connect" not in status, \ "Failed to connect to the daemon" with subtest("Daemon can be configured imperatively"): - machine.succeed("wpa_cli -i wlan1 add_network") - machine.succeed("wpa_cli -i wlan1 set_network 0 ssid '\"nixos-test\"'") - machine.succeed("wpa_cli -i wlan1 set_network 0 psk '\"reproducibility\"'") - machine.succeed("wpa_cli -i wlan1 save_config") - machine.succeed("grep -q nixos-test /etc/wpa_supplicant.conf") + machine.succeed(f"{wpa_cli} -i wlan1 add_network") + machine.succeed(f"{wpa_cli} -i wlan1 set_network 0 ssid '\"nixos-test\"'") + machine.succeed(f"{wpa_cli} -i wlan1 set_network 0 psk '\"reproducibility\"'") + machine.succeed(f"{wpa_cli} -i wlan1 save_config") + machine.succeed("grep -q nixos-test /etc/wpa_supplicant/imperative.conf") ''; }; diff --git a/pkgs/os-specific/linux/wpa_supplicant/default.nix b/pkgs/os-specific/linux/wpa_supplicant/default.nix index 204481e0c096..91377303c229 100644 --- a/pkgs/os-specific/linux/wpa_supplicant/default.nix +++ b/pkgs/os-specific/linux/wpa_supplicant/default.nix @@ -14,6 +14,7 @@ readline, withPcsclite ? !stdenv.hostPlatform.isStatic, pcsclite, + unprivileged ? true, }: stdenv.mkDerivation rec { @@ -33,8 +34,6 @@ stdenv.mkDerivation rec { hash = "sha256-X6mBbj7BkW66aYeSCiI3JKBJv10etLQxaTRfRgwsFmM="; revert = true; }) - ./unsurprising-ext-password.patch - ./multiple-configs.patch (fetchpatch { name = "suppress-ctrl-event-signal-change.patch"; url = "https://w1.fi/cgit/hostap/patch/?id=c330b5820eefa8e703dbce7278c2a62d9c69166a"; @@ -45,7 +44,10 @@ stdenv.mkDerivation rec { url = "https://git.w1.fi/cgit/hostap/patch/?id=1ce37105da371c8b9cf3f349f78f5aac77d40836"; hash = "sha256-leCk0oexNBZyVK5Q5gR4ZcgWxa0/xt/aU+DssTa0UwE="; }) - ]; + ./unsurprising-ext-password.patch + ./multiple-configs.patch + ] + ++ lib.optional unprivileged ./unprivileged-daemon.patch; # TODO: Patch epoll so that the dbus actually responds # TODO: Figure out how to get privsep working, currently getting SIGBUS diff --git a/pkgs/os-specific/linux/wpa_supplicant/unprivileged-daemon.patch b/pkgs/os-specific/linux/wpa_supplicant/unprivileged-daemon.patch new file mode 100644 index 000000000000..93dceff753cd --- /dev/null +++ b/pkgs/os-specific/linux/wpa_supplicant/unprivileged-daemon.patch @@ -0,0 +1,96 @@ +commit 24e932357ee3041763135b931206dfc0bbe0441e +Author: rnhmjoj +Date: Wed Jul 23 10:18:55 2025 +0200 + + Fixes for running wpa_supplicant unprivileged + + 1. Change the dbus service user to "wpa_supplicant" + + 2. Ensure appropriate group ownership and permissions on the client sockets. + Motivation: clients communicate with the daemon by creating "client" + sockets; by default this is owned by the user running the client, + so it may be inaccessible by the daemon. + + 3. Move the "control" sockets under a subdirectory of /run/wpa_supplicant. + Motivation: wpa_supplicant will try to adjust the ownership of the + sockets directory, even if they are fine, and fail. + + 4. Move the "client" under a subdirectory of /run/wpa_supplicant instead + of tmp. Motivation: this allows to unshare /tmp + +diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c +index 7e197f0..6bfb091 100644 +--- a/src/common/wpa_ctrl.c ++++ b/src/common/wpa_ctrl.c +@@ -15,6 +15,8 @@ + #include + #include + #include ++#include ++#include + #include + #endif /* CONFIG_CTRL_IFACE_UNIX */ + #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE +@@ -165,6 +167,14 @@ try_again: + return NULL; + } + ++ /* Set the client socket owner group to "wpa_supplicant" ++ * and ensure group and user permissions are the same */ ++ struct group *grp = getgrnam("wpa_supplicant"); ++ if (grp != NULL) { ++ lchown(ctrl->local.sun_path, -1, grp->gr_gid); ++ chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); ++ } ++ + #ifdef ANDROID + /* Set group even if we do not have privileges to change owner */ + lchown(ctrl->local.sun_path, -1, AID_WIFI); +--- a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf ++++ b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf +@@ -2,9 +2,15 @@ + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> + +- ++ + +- ++ ++ ++ ++ ++ ++ ++ + + + +diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in +index d97ff39..367a7c6 100644 +--- a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in ++++ b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in +@@ -1,5 +1,5 @@ + [D-BUS Service] + Name=fi.w1.wpa_supplicant1 + Exec=@BINDIR@/wpa_supplicant -u +-User=root ++User=wpa_supplicant + SystemdService=wpa_supplicant.service +diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c +index af00e79..840b307 100644 +--- a/wpa_supplicant/wpa_cli.c ++++ b/wpa_supplicant/wpa_cli.c +@@ -44,10 +44,10 @@ static int wpa_cli_attached = 0; + static int wpa_cli_connected = -1; + static int wpa_cli_last_id = 0; + #ifndef CONFIG_CTRL_IFACE_DIR +-#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" ++#define CONFIG_CTRL_IFACE_DIR "/run/wpa_supplicant/control" + #endif /* CONFIG_CTRL_IFACE_DIR */ + static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +-static const char *client_socket_dir = NULL; ++static const char *client_socket_dir = "/run/wpa_supplicant/client"; + static char *ctrl_ifname = NULL; + static const char *global = NULL; + static const char *pid_file = NULL; From bf9788e1c5cd71554c24972e6d9ac65e311764cb Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Wed, 23 Jul 2025 12:40:40 +0200 Subject: [PATCH 03/17] nixos/release-notes: mention wpa_supplicant hardening --- nixos/doc/manual/release-notes/rl-2605.section.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index fc7801aa0c1c..3317a4d8a43b 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -55,6 +55,16 @@ of pulling the upstream container image from Docker Hub. If you want the old beh - The Bash implementation of the `nixos-rebuild` program is removed. All switchable systems now use the Python rewrite. Any prior usage of `system.rebuild.enableNg` must now be removed. If you have any outstanding issues with the new implementation, please open an issue on GitHub. +- The `networking.wireless` module has been security hardened: the `wpa_supplicant` daemon now runs under an unprivileged user with restricted access to the system. + + As part of these changes, `/etc/wpa_supplicant.conf` has been deprecated: the NixOS-generated configuration file is now linked to `/etc/wpa_supplicant/nixos.conf` and `/etc/wpa_supplicant/imperative.conf` has been added for imperatively configuring `wpa_supplicant` or when using [allowAuxiliaryImperativeNetworks](#opt-networking.wireless.allowAuxiliaryImperativeNetworks). + + If client certificates, keys or other files are needed, these should be stored under `/etc/wpa_supplicant` and owned by `wpa_supplicant` to ensure the daemon can read them. + + Also, the {option}`networking.wireless.userControlled.group` option has been removed since there is now a dedicated `wpa_supplicant` group to control the daemon, and {option}`networking.wireless.userControlled.enable` has been renamed to [](#opt-networking.wireless.userControlled). + + No functionality should have been impacted by these changes (including controlling via `wpa_cli`, integration with NetworkManager or connman), but if you find any problems, please open an issue on GitHub. + - `services.angrr` now uses TOML for configuration. Define policies with `services.angrr.settings` (generate TOML file) or point to a file using `services.angrr.configFile`. The legacy options `services.angrr.period`, `services.angrr.ownedOnly`, and `services.angrr.removeRoot` have been removed. See `man 5 angrr` and the description of `services.angrr.settings` options for examples and details. - `services.pingvin-share` has been removed as the `pingvin-share.backend` package was broken and the project was archived upstream. From d7535dd0430584ec9ce113f556f3bc97da48a4b5 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Wed, 20 Aug 2025 18:56:24 +0200 Subject: [PATCH 04/17] nixos/networkmanager: fix for rootless wpa_supplicant --- .../services/networking/networkmanager.nix | 19 ++++++++++++------- .../services/networking/wpa_supplicant.nix | 19 +++++++++++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index 9295157b793b..f76e649d75e9 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -11,7 +11,8 @@ let cfg = config.networking.networkmanager; ini = pkgs.formats.ini { }; - delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [ ]; + # Whether wpa_supplicant is managed independently + delegateWireless = config.networking.wireless.networks != { } && cfg.unmanaged != [ ]; enableIwd = cfg.wifi.backend == "iwd"; @@ -136,10 +137,7 @@ let cfg.package ] ++ cfg.plugins - ++ pluginRuntimeDeps - ++ lib.optionals (!delegateWireless && !enableIwd) [ - pkgs.wpa_supplicant - ]; + ++ pluginRuntimeDeps; in { @@ -541,9 +539,9 @@ in assertions = [ { - assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [ ]; + assertion = config.networking.wireless.networks != { } -> cfg.unmanaged != [ ]; message = '' - You can not use networking.networkmanager with networking.wireless. + You can not use networking.networkmanager with networking.wireless.networks. Except if you mark some interfaces as unmanaged by NetworkManager. ''; } @@ -676,6 +674,13 @@ in useDHCP = false; }) + (mkIf (!delegateWireless && !enableIwd) { + # Enable wpa_supplicant but fully control it over DBus + wireless.enable = true; + wireless.autoDetectInterfaces = false; + wireless.dbusControlled = true; + }) + (mkIf enableIwd { wireless.iwd.enable = true; }) diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index 8fa11f9ef87e..124bd9c9eda0 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -199,7 +199,12 @@ let script = '' iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}" ${ - if iface == null then + if iface != null then + '' + # add known interface to the daemon arguments + args="-i${iface} $iface_args" + '' + else if cfg.autoDetectInterfaces then '' # detect interfaces automatically @@ -222,10 +227,7 @@ let done '' else - '' - # add known interface to the daemon arguments - args="-i${iface} $iface_args" - '' + "args=$iface_args" } # finally start daemon @@ -251,7 +253,8 @@ in "wlan1" ]; description = '' - The interfaces {command}`wpa_supplicant` will use. If empty, it will + The interfaces {command}`wpa_supplicant` will use. If empty and + [](#opt-networking.wireless.autoDetectInterfaces) is true it will automatically use all wireless interfaces. ::: {.note} @@ -260,6 +263,10 @@ in ''; }; + autoDetectInterfaces = mkEnableOption "automatic detection of wireless interfaces" // { + default = true; + }; + driver = mkOption { type = types.str; default = "nl80211,wext"; From 7871403fdcf2f0d9c6de620cd6e4d6ed08e82146 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Wed, 20 Aug 2025 21:06:11 +0200 Subject: [PATCH 05/17] nixos/connman: fix for rootless wpa_supplicant --- nixos/modules/services/networking/connman.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix index d3a8e236358e..45168c643ee9 100644 --- a/nixos/modules/services/networking/connman.nix +++ b/nixos/modules/services/networking/connman.nix @@ -165,6 +165,7 @@ in wireless = { enable = lib.mkIf (!enableIwd) true; dbusControlled = true; + autoDetectInterfaces = false; iwd = lib.mkIf enableIwd { enable = true; }; From bdb19fe2e04cd48845513c8b3931b69718ddd1f7 Mon Sep 17 00:00:00 2001 From: Diego Date: Sat, 20 Dec 2025 19:04:21 +0100 Subject: [PATCH 06/17] prmers: add update-script --- pkgs/by-name/pr/prmers/package.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/by-name/pr/prmers/package.nix b/pkgs/by-name/pr/prmers/package.nix index d412b811d699..f65034217c82 100644 --- a/pkgs/by-name/pr/prmers/package.nix +++ b/pkgs/by-name/pr/prmers/package.nix @@ -7,6 +7,7 @@ opencl-headers, stdenv, versionCheckHook, + nix-update-script, }: stdenv.mkDerivation (finalAttrs: { @@ -44,6 +45,8 @@ stdenv.mkDerivation (finalAttrs: { versionCheckProgramArg = "-v"; + passthru.updateScript = nix-update-script { extraArgs = [ "--version=unstable" ]; }; + meta = { description = "GPU-accelerated Mersenne primality testing"; longDescription = '' From f801a20bb7ea31923af4b976323622e081e3da9f Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Wed, 24 Dec 2025 16:54:37 +0000 Subject: [PATCH 07/17] fcitx5-mcbopomofo: 2.9.4 -> 2.9.5 --- pkgs/by-name/fc/fcitx5-mcbopomofo/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/fc/fcitx5-mcbopomofo/package.nix b/pkgs/by-name/fc/fcitx5-mcbopomofo/package.nix index 944441e9778e..f0c5bdf8a228 100644 --- a/pkgs/by-name/fc/fcitx5-mcbopomofo/package.nix +++ b/pkgs/by-name/fc/fcitx5-mcbopomofo/package.nix @@ -15,13 +15,13 @@ stdenv.mkDerivation rec { pname = "fcitx5-mcbopomofo"; - version = "2.9.4"; + version = "2.9.5"; src = fetchFromGitHub { owner = "openvanilla"; repo = "fcitx5-mcbopomofo"; rev = version; - hash = "sha256-4nJrNAAh7N2hJW/+cKz2pmu004VXAkuw9y0PJ7ktIug="; + hash = "sha256-efpVvWchJywKyGu7I6pNRVKJhIv01iKAXFCJ+7kcMwc="; }; nativeBuildInputs = [ From c6b308f4441aacb1a52d771b3b9f81c46b40d908 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 25 Dec 2025 13:42:32 +0100 Subject: [PATCH 08/17] mpd-notification: 0.9.2 -> 0.9.3 Signed-off-by: Matthias Beyer --- pkgs/by-name/mp/mpd-notification/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/mp/mpd-notification/package.nix b/pkgs/by-name/mp/mpd-notification/package.nix index 993d1952f164..cedeb101719d 100644 --- a/pkgs/by-name/mp/mpd-notification/package.nix +++ b/pkgs/by-name/mp/mpd-notification/package.nix @@ -14,13 +14,13 @@ stdenv.mkDerivation rec { pname = "mpd-notification"; - version = "0.9.2"; + version = "0.9.3"; src = fetchFromGitHub { owner = "eworm-de"; repo = "mpd-notification"; rev = version; - hash = "sha256-2rnZkVKrk8jgZz/EcZGQ34tLZrVttjq3tq8k2xSl00A="; + hash = "sha256-1xTIqJtTz7vfg34JvlwNe6kNZuPfd3KnAT0rI8ZYk2U="; }; nativeBuildInputs = [ From c308e79bc461aa223dffed1d229282dd76fcc7f6 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 25 Dec 2025 13:38:31 +0100 Subject: [PATCH 09/17] git-graph: 0.6.0 -> 0.7.0 Signed-off-by: Matthias Beyer --- pkgs/by-name/gi/git-graph/package.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/by-name/gi/git-graph/package.nix b/pkgs/by-name/gi/git-graph/package.nix index 9b5b281fd59b..2580c39f02f2 100644 --- a/pkgs/by-name/gi/git-graph/package.nix +++ b/pkgs/by-name/gi/git-graph/package.nix @@ -7,16 +7,16 @@ rustPlatform.buildRustPackage rec { pname = "git-graph"; - version = "0.6.0"; + version = "0.7.0"; src = fetchFromGitHub { owner = "mlange-42"; repo = "git-graph"; - tag = version; - hash = "sha256-xYUpLujePO1MS0c25UJX5rRdmPzkaFgF5zJonzQOJqM="; + tag = "v${version}"; + hash = "sha256-9GFwxWYDnH3kKDWpxgh7ciSLB1Zr2zExxIrIrhycmZY="; }; - cargoHash = "sha256-tN70YyhVkLD5OiYNm64vbY5NtKAG2sFp4Ry6vFpXvtE="; + cargoHash = "sha256-hKCEAXZj2ExSamvtl10RnAiuV9w6yOYdnsXm0gplFSU="; meta = { description = "Command line tool to show clear git graphs arranged for your branching model"; From 158f49080e465eade5574b0a2f7f9273c336e83c Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Fri, 26 Dec 2025 09:48:47 +0000 Subject: [PATCH 10/17] rip2: 0.9.5 -> 0.9.6 --- pkgs/by-name/ri/rip2/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/ri/rip2/package.nix b/pkgs/by-name/ri/rip2/package.nix index ab1478d8adfe..4b2a6f5a3f49 100644 --- a/pkgs/by-name/ri/rip2/package.nix +++ b/pkgs/by-name/ri/rip2/package.nix @@ -10,16 +10,16 @@ rustPlatform.buildRustPackage rec { pname = "rip2"; - version = "0.9.5"; + version = "0.9.6"; src = fetchFromGitHub { owner = "MilesCranmer"; repo = "rip2"; rev = "v${version}"; - hash = "sha256-vYkJlmzysUcX+jULGSs4Omu2RjUs4ZO4blN/zlzDcqc="; + hash = "sha256-cqc9oZSs0JEMEJfHTHBAgN5Y5/zLPInPeQcOthj+EzQ="; }; - cargoHash = "sha256-+8o4PFcJ/jMoMzfEA96tJ8wuH1CTqxnDwvFuegmPcEY="; + cargoHash = "sha256-2rlxuxiyPiThOEhwaV3VUGBwKHnPTGKbQ6PPTaP9Rps="; nativeBuildInputs = [ installShellFiles ]; From ea8d6526c45e7a68a4a47974493a03413deaa29a Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Fri, 26 Dec 2025 17:47:44 +0000 Subject: [PATCH 11/17] bikeshed: 7.0.5 -> 7.0.6 --- pkgs/by-name/bi/bikeshed/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/bi/bikeshed/package.nix b/pkgs/by-name/bi/bikeshed/package.nix index 34db60658156..6514a3f47078 100644 --- a/pkgs/by-name/bi/bikeshed/package.nix +++ b/pkgs/by-name/bi/bikeshed/package.nix @@ -6,12 +6,12 @@ python3Packages.buildPythonApplication rec { pname = "bikeshed"; - version = "7.0.5"; + version = "7.0.6"; pyproject = true; src = fetchPypi { inherit pname version; - hash = "sha256-D0PP+NnLREJr0F6M/KG8O9HTz3FOaN0GkWLo96PaU/E="; + hash = "sha256-0lO5TlAnUMtJp81XQEXpxt3yBz3zx6ff+vO/LYJfFZA="; }; patches = [ ./remove-install-check.patch ]; From 7484db2f4599a261b4429bcb6fb60b5cd2c6fce2 Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Sat, 27 Dec 2025 06:09:10 +0000 Subject: [PATCH 12/17] windmill: 1.596.0 -> 1.601.0 --- pkgs/by-name/wi/windmill/package.nix | 8 ++++---- pkgs/by-name/wi/windmill/ui_builder.nix | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/by-name/wi/windmill/package.nix b/pkgs/by-name/wi/windmill/package.nix index a40a75e19fde..faf587652245 100644 --- a/pkgs/by-name/wi/windmill/package.nix +++ b/pkgs/by-name/wi/windmill/package.nix @@ -47,13 +47,13 @@ let pname = "windmill"; - version = "1.596.0"; + version = "1.601.0"; src = fetchFromGitHub { owner = "windmill-labs"; repo = "windmill"; rev = "v${version}"; - hash = "sha256-PnEo8rMTYM4R498YLhTUHcDowjMK+YFI1mNgRXqJx5k="; + hash = "sha256-yaIQFK3ECP8DccfwaaMwAEisnzM5JijznDsY6mRBNHA="; }; in rustPlatform.buildRustPackage (finalAttrs: { @@ -66,7 +66,7 @@ rustPlatform.buildRustPackage (finalAttrs: { RUSTY_V8_ARCHIVE = librusty_v8; }; - cargoHash = "sha256-UpeVoPsAp7Y/ZjTSwjFZ/GrmjTsl2w7gPjaFnj4ymPs="; + cargoHash = "sha256-b+qSk2DgVJkWeSB+qXosVtMOM+kUobmYB5Av68/tr68="; buildFeatures = [ "agent_worker_server" @@ -195,7 +195,7 @@ rustPlatform.buildRustPackage (finalAttrs: { sourceRoot = "${src.name}/frontend"; - npmDepsHash = "sha256-28fXEI27W43WOhbAs+B0KpxqUUwlspFt/pdRTNqGmlI="; + npmDepsHash = "sha256-4Sy7C9zzVjw4HIHFFWKfhMUtn97XWyNBDaWAV4WicK4="; # without these you get a # FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory diff --git a/pkgs/by-name/wi/windmill/ui_builder.nix b/pkgs/by-name/wi/windmill/ui_builder.nix index 58f8e4f72b32..2bd251fa0e36 100644 --- a/pkgs/by-name/wi/windmill/ui_builder.nix +++ b/pkgs/by-name/wi/windmill/ui_builder.nix @@ -2,7 +2,7 @@ { fetchzip }: fetchzip { - url = "https://pub-06154ed168a24e73a86ab84db6bf15d8.r2.dev/ui_builder-6dcfccf.tar.gz"; - sha256 = "1cbz1y1600335jrp5p062hpybcn422w89mvy40i94xi5ps5xp7kb"; + url = "https://pub-06154ed168a24e73a86ab84db6bf15d8.r2.dev/ui_builder-9e76079.tar.gz"; + sha256 = "1lxbfqfx8dhrf585gfh7x7098nbmyarja94cjp43sygvp4c6yw4g"; stripRoot = false; } From 241107e2e4007c8932819ee63bd3cf11cea75df2 Mon Sep 17 00:00:00 2001 From: HigherOrderLogic <73709188+HigherOrderLogic@users.noreply.github.com> Date: Sat, 27 Dec 2025 07:08:31 +0000 Subject: [PATCH 13/17] jj-fzf: add `mainProgram` meta attr --- pkgs/by-name/jj/jj-fzf/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/by-name/jj/jj-fzf/package.nix b/pkgs/by-name/jj/jj-fzf/package.nix index 62c5ff003f1f..e785b400f8d7 100644 --- a/pkgs/by-name/jj/jj-fzf/package.nix +++ b/pkgs/by-name/jj/jj-fzf/package.nix @@ -64,6 +64,7 @@ stdenv.mkDerivation rec { homepage = "https://github.com/tim-janik/jj-fzf"; license = lib.licenses.mpl20; maintainers = with lib.maintainers; [ bbigras ]; + mainProgram = "jj-fzf"; platforms = lib.platforms.all; }; } From 86da6ff62b016235ff4917adfb5d1648d000f7ce Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Sun, 16 Nov 2025 03:22:54 +0000 Subject: [PATCH 14/17] python3Packages.pynmeagps: 1.0.54 -> 1.0.56 --- pkgs/development/python-modules/pynmeagps/default.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/development/python-modules/pynmeagps/default.nix b/pkgs/development/python-modules/pynmeagps/default.nix index 03fa407c0b88..14fb55cf1fbb 100644 --- a/pkgs/development/python-modules/pynmeagps/default.nix +++ b/pkgs/development/python-modules/pynmeagps/default.nix @@ -10,16 +10,16 @@ buildPythonPackage rec { pname = "pynmeagps"; - version = "1.0.54"; + version = "1.0.56"; pyproject = true; - disabled = pythonOlder "3.9"; + disabled = pythonOlder "3.10"; src = fetchFromGitHub { owner = "semuconsulting"; repo = "pynmeagps"; tag = "v${version}"; - hash = "sha256-EpEhojwsbvjZnz+V8mQ1H/g2RIfXR8hZPv3XI3OBVOg="; + hash = "sha256-fncUyahHQ/Uks7B/8rAlpP8JDVbjvhoY4ozoNWVTVDY="; }; build-system = [ setuptools ]; From 18d08832de75f01c07ae5e5289c71a805094f16c Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Sat, 27 Dec 2025 08:23:06 +0000 Subject: [PATCH 15/17] python3Packages.geodatasets: 2025.12.0 -> 2025.12.1 --- pkgs/development/python-modules/geodatasets/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/development/python-modules/geodatasets/default.nix b/pkgs/development/python-modules/geodatasets/default.nix index fc52fa497c09..bc28feb9d265 100644 --- a/pkgs/development/python-modules/geodatasets/default.nix +++ b/pkgs/development/python-modules/geodatasets/default.nix @@ -12,14 +12,14 @@ buildPythonPackage rec { pname = "geodatasets"; - version = "2025.12.0"; + version = "2025.12.1"; pyproject = true; src = fetchFromGitHub { owner = "geopandas"; repo = "geodatasets"; tag = version; - hash = "sha256-F5cGmcUKbIy35Lx50WFV7hVDmWwWCk66EScx8YW2OTE="; + hash = "sha256-r5dHWJ6HH6capBOXg/pgeHXPmzLPvXLD27u7AELdIaU="; }; build-system = [ setuptools-scm ]; From 5111a589ea9b26ddba746a8e61c81a37914ba922 Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Sat, 27 Dec 2025 08:44:26 +0000 Subject: [PATCH 16/17] goflow2: 2.2.3 -> 2.2.6 --- pkgs/by-name/go/goflow2/package.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/by-name/go/goflow2/package.nix b/pkgs/by-name/go/goflow2/package.nix index cd32fd4c7ce6..b720a9f48de6 100644 --- a/pkgs/by-name/go/goflow2/package.nix +++ b/pkgs/by-name/go/goflow2/package.nix @@ -4,7 +4,7 @@ fetchFromGitHub, }: let - version = "2.2.3"; + version = "2.2.6"; in buildGoModule { pname = "goflow2"; @@ -14,7 +14,7 @@ buildGoModule { owner = "netsampler"; repo = "goflow2"; rev = "v${version}"; - hash = "sha256-nLsl3v4pvFa0d4AejjlUY9y92yKCU3jM5ui2Y+qZ3JY="; + hash = "sha256-PGXBsUDooYEq5RuLRwmTMOxYuXCxhfAo9Ef/75TWPc0="; }; ldflags = [ @@ -23,7 +23,7 @@ buildGoModule { "-X=main.version=${version}" ]; - vendorHash = "sha256-E7gWeh8GVFQdxLSZhpl5wAaShooKkC9EJJsulGaoBtE="; + vendorHash = "sha256-fhZ74kSCYd/7P9A9rdQhe8ejNIsFGuSQVO84tIRN+QY="; meta = { description = "High performance sFlow/IPFIX/NetFlow Collector"; From 09550217a700c60de8e042bcc789dab9ec50928b Mon Sep 17 00:00:00 2001 From: "R. Ryantm" Date: Sat, 27 Dec 2025 11:36:12 +0000 Subject: [PATCH 17/17] home-assistant-custom-components.daikin_onecta: 4.4.3 -> 4.4.4 --- .../custom-components/daikin_onecta/package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/servers/home-assistant/custom-components/daikin_onecta/package.nix b/pkgs/servers/home-assistant/custom-components/daikin_onecta/package.nix index cfc54afe6798..5cdbfaa03631 100644 --- a/pkgs/servers/home-assistant/custom-components/daikin_onecta/package.nix +++ b/pkgs/servers/home-assistant/custom-components/daikin_onecta/package.nix @@ -7,13 +7,13 @@ buildHomeAssistantComponent rec { owner = "jwillemsen"; domain = "daikin_onecta"; - version = "4.4.3"; + version = "4.4.4"; src = fetchFromGitHub { owner = "jwillemsen"; repo = "daikin_onecta"; tag = "v${version}"; - hash = "sha256-OhihiBM+MX2jzBjoi73EhTAixKDr2Fp0oR/w8tWrd18="; + hash = "sha256-8a5P+eoa3iEER4b2SlsYi+feyPnc4n4RYzl6Lg+nqmQ="; }; meta = {