diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index f04b0e03cf91..95ce2f481d0b 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -162,6 +162,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 fd16442ad185..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. @@ -239,8 +241,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,12 +259,13 @@ 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; allow_anonymous = 1; allow_zero_length_clientid = 1; auto_id_prefix = 1; @@ -274,13 +277,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 +300,7 @@ let use_identity_as_username = 1; use_subject_as_username = 1; use_username_as_clientid = 1; + websockets_origin = 1; }; listenerOptions = @@ -393,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 = { @@ -413,7 +428,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 +531,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; @@ -634,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) 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 = {