From 1b76e5a9ce840f9880ce7d5d1a6076f1ca71713e Mon Sep 17 00:00:00 2001 From: 2tefan Date: Sat, 18 Apr 2026 20:35:43 +0200 Subject: [PATCH 1/4] mosquitto: 2.0.22 -> 2.1.2 --- pkgs/by-name/mo/mosquitto/package.nix | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pkgs/by-name/mo/mosquitto/package.nix b/pkgs/by-name/mo/mosquitto/package.nix index 967eade3b7dd..ae3e932a0aa6 100644 --- a/pkgs/by-name/mo/mosquitto/package.nix +++ b/pkgs/by-name/mo/mosquitto/package.nix @@ -7,11 +7,13 @@ libxslt, c-ares, cjson, + libargon2, libuuid, libuv, libwebsockets, openssl, withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd, + sqlite, systemd, uthash, nixosTests, @@ -32,13 +34,17 @@ let in stdenv.mkDerivation (finalAttrs: { pname = "mosquitto"; - version = "2.0.22"; + version = "2.1.2"; + # Tests disabled: upstream test suite requires additional Python deps, + # uses chown() to fixed UIDs, and relies on signal handling that breaks + # in sandboxed builds. Re-enable once upstream tests are sandbox-friendly. + doCheck = false; src = fetchFromGitHub { - owner = "eclipse"; + owner = "eclipse-mosquitto"; repo = "mosquitto"; rev = "v${finalAttrs.version}"; - hash = "sha256-PCiNxRG2AqVlS2t/u7Cqn8NbTrrYGO1OXl8zvPQRrJM="; + hash = "sha256-Zl55yjuzQY2fyaKs/zLaJ7a3OONKTDQPaT+DpPURdZI="; }; postPatch = '' @@ -63,22 +69,27 @@ stdenv.mkDerivation (finalAttrs: { buildInputs = [ c-ares cjson + libargon2 libuuid libuv libwebsockets' openssl + sqlite uthash ] ++ lib.optional withSystemd systemd; + propagatedBuildInputs = [ cjson ]; + cmakeFlags = [ (lib.cmakeBool "WITH_BUNDLED_DEPS" false) (lib.cmakeBool "WITH_WEBSOCKETS" true) (lib.cmakeBool "WITH_SYSTEMD" withSystemd) + (lib.cmakeBool "WITH_TESTS" finalAttrs.doCheck) ]; postFixup = '' - sed -i "s|^prefix=.*|prefix=$lib|g" $dev/lib/pkgconfig/*.pc + sed -i "s|^libdir=.*|libdir=$lib/lib|g" $dev/lib/pkgconfig/*.pc ''; passthru.tests = { From 0101675dad73660b4f037da356ccc5b00217b512 Mon Sep 17 00:00:00 2001 From: r-vdp Date: Tue, 21 Apr 2026 13:09:07 +0200 Subject: [PATCH 2/4] nixos/mosquitto: allow 2.1 config keys in freeform settings Mosquitto 2.1 introduced a number of new listener, bridge and global config keys. The module rejects unknown keys via assertKeysValid, so these need to be added to the allowlists for users to be able to set them. --- nixos/modules/services/networking/mosquitto.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index fd16442ad185..36fcc5f752ea 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -263,6 +263,7 @@ let ++ formatFreeform { prefix = "auth_opt_"; } plugin.options; freeformListenerKeys = { + accept_protocol_versions = 1; allow_anonymous = 1; allow_zero_length_clientid = 1; auto_id_prefix = 1; @@ -274,13 +275,18 @@ let "ciphers_tls1.3" = 1; crlfile = 1; dhparamfile = 1; + disable_client_cert_date_checks = 1; + enable_proxy_protocol = 1; http_dir = 1; keyfile = 1; + listener_allow_anonymous = 1; + listener_auto_id_prefix = 1; max_connections = 1; max_qos = 1; max_topic_alias = 1; mount_point = 1; protocol = 1; + proxy_protocol_v2_require_tls = 1; psk_file = 1; psk_hint = 1; require_certificate = 1; @@ -292,6 +298,7 @@ let use_identity_as_username = 1; use_subject_as_username = 1; use_username_as_clientid = 1; + websockets_origin = 1; }; listenerOptions = @@ -413,7 +420,11 @@ let bridge_outgoing_retain = 1; bridge_protocol_version = 1; bridge_psk = 1; + bridge_receive_maximum = 1; + bridge_reload_type = 1; bridge_require_ocsp = 1; + bridge_session_expiry_interval = 1; + bridge_tls_use_os_certs = 1; bridge_tls_version = 1; cleansession = 1; idle_timeout = 1; @@ -512,6 +523,9 @@ let autosave_on_changes = 1; check_retain_source = 1; connection_messages = 1; + enable_control_api = 1; + global_max_clients = 1; + global_max_connections = 1; log_facility = 1; log_timestamp = 1; log_timestamp_format = 1; From 068672d9af8c54206629ac685b6a3ca02404b11e Mon Sep 17 00:00:00 2001 From: r-vdp Date: Tue, 21 Apr 2026 13:09:31 +0200 Subject: [PATCH 3/4] nixos/mosquitto: emit plugin/plugin_opt_* instead of deprecated aliases The auth_plugin and auth_opt_* config keys have been deprecated upstream in favour of plugin and plugin_opt_*. Both spellings are accepted since mosquitto 2.0, so this is safe regardless of the package version. auth_plugin_deny_special_chars has no new-style alias and is kept as-is. --- nixos/modules/services/networking/mosquitto.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index 36fcc5f752ea..1d3bf8d671df 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -239,8 +239,8 @@ let options = lib.mkOption { type = attrsOf optionType; description = '' - Options for the auth plugin. Each key turns into a `auth_opt_*` - line in the config. + Options for the plugin. Each key turns into a `plugin_opt_*` + line in the config. ''; default = { }; }; @@ -257,10 +257,10 @@ let formatAuthPlugin = plugin: [ - "auth_plugin ${plugin.plugin}" + "plugin ${plugin.plugin}" "auth_plugin_deny_special_chars ${optionToString plugin.denySpecialChars}" ] - ++ formatFreeform { prefix = "auth_opt_"; } plugin.options; + ++ formatFreeform { prefix = "plugin_opt_"; } plugin.options; freeformListenerKeys = { accept_protocol_versions = 1; From 386a2346d7c43582bee112c3241343ce477d0019 Mon Sep 17 00:00:00 2001 From: r-vdp Date: Tue, 21 Apr 2026 13:13:15 +0200 Subject: [PATCH 4/4] nixos/mosquitto: use acl-file/password-file plugins Mosquitto 2.1 deprecated the acl_file and password_file config options in favour of plugins shipping the same code, with removal scheduled for 3.0. Switch the per-listener config generation over to load the plugins instead. per_listener_settings is also deprecated but kept for now since the module design is built around per-listener auth and the 3.0 replacement semantics are not yet finalised upstream. A version assertion is added since the plugins only ship with 2.1+. --- .../manual/release-notes/rl-2605.section.md | 2 ++ .../modules/services/networking/mosquitto.md | 11 ++++++----- .../modules/services/networking/mosquitto.nix | 19 +++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index f883372fd450..b07a270be3da 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -147,6 +147,8 @@ - `services.crabfit` was removed because its upstream packages are unmaintained and insecure. +- `services.mosquitto` now generates per-listener authentication and access control via the upstream `password-file` and `acl-file` plugins instead of the deprecated `password_file` and `acl_file` options. The plugins contain the same code, so behaviour is unchanged, but [](#opt-services.mosquitto.package) must now be at least version 2.1. + - `sing-box` has been updated to 1.13.0, which has removed some deprecated options. See [upstream documentation](https://sing-box.sagernet.org/configuration/) for details and migration options. - `services.statsd` has been removed because the packages it relies on do not exist anymore in nixpkgs. diff --git a/nixos/modules/services/networking/mosquitto.md b/nixos/modules/services/networking/mosquitto.md index 53b5beb16b89..b1a6fabb1f2a 100644 --- a/nixos/modules/services/networking/mosquitto.md +++ b/nixos/modules/services/networking/mosquitto.md @@ -83,9 +83,10 @@ localhost). Almost all options of Mosquitto are available for configuration at their appropriate levels, some as NixOS options written in camel case, the remainders under `settings` with their exact names in -the Mosquitto config file. The exceptions are `acl_file` (which is always set according to the -`acl` attributes of a listener and its users) and `per_listener_settings` (which is always set to -`true`). +the Mosquitto config file. The exceptions are `per_listener_settings` (which is always set to +`true`) and the per-listener access control, which is always configured via instances of the +`acl-file` and `password-file` plugins generated from the `acl` and `users` attributes of each +listener. ### Password authentication {#module-services-mosquitto-config-passwords} @@ -101,8 +102,8 @@ will not be able to use the broker. ### ACL format {#module-services-mosquitto-config-acl} -Every listener has a Mosquitto `acl_file` attached to it. This ACL is configured via two -attributes of the config: +Every listener has an instance of the Mosquitto `acl-file` plugin attached to it. This ACL is +configured via two attributes of the config: * the `acl` attribute of the listener configures pattern ACL entries and topic ACL entries for anonymous users. Each entry must be prefixed with `pattern` or `topic` to distinguish diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index 1d3bf8d671df..21b531533513 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -7,6 +7,8 @@ let cfg = config.services.mosquitto; + pluginLibDir = "${lib.getLib cfg.package}/lib"; + # note that mosquitto config parsing is very simplistic as of may 2021. # often times they'll e.g. strtok() a line, check the first two tokens, and ignore the rest. # there's no escaping available either, so we have to prevent any being necessary. @@ -400,10 +402,16 @@ let idx: listener: [ "listener ${toString listener.port} ${toString listener.address}" - "acl_file ${cfg.dataDir}/acl-${toString idx}.conf" ] - ++ lib.optional (!listener.omitPasswordAuth) "password_file ${cfg.dataDir}/passwd-${toString idx}" ++ formatFreeform { } listener.settings + ++ [ + "plugin ${pluginLibDir}/mosquitto_acl_file.so" + "plugin_opt_acl_file ${cfg.dataDir}/acl-${toString idx}.conf" + ] + ++ lib.optionals (!listener.omitPasswordAuth) [ + "plugin ${pluginLibDir}/mosquitto_password_file.so" + "plugin_opt_password_file ${cfg.dataDir}/passwd-${toString idx}" + ] ++ lib.concatMap formatAuthPlugin listener.authPlugins; freeformBridgeKeys = { @@ -648,6 +656,13 @@ let globalAsserts = prefix: cfg: lib.flatten [ + { + assertion = lib.versionAtLeast cfg.package.version "2.1"; + message = '' + ${prefix}.package must be at least version 2.1, since the generated + configuration relies on the acl-file and password-file plugins. + ''; + } (assertKeysValid "${prefix}.settings" freeformGlobalKeys cfg.settings) (lib.imap0 (n: l: listenerAsserts "${prefix}.listener.${toString n}" l) cfg.listeners) (lib.mapAttrsToList (n: b: bridgeAsserts "${prefix}.bridge.${n}" b) cfg.bridges)