From 5f79952ff1003ab9bbcbc8c0ead99f8e17e838bc Mon Sep 17 00:00:00 2001 From: r-vdp Date: Fri, 27 Mar 2026 17:13:22 +0100 Subject: [PATCH] nixos/mosquitto: write ACL files to StateDirectory instead of /etc The ACL file was placed in /etc via environment.etc with owner=mosquitto and mode=0400. This breaks when system.etc.overlay.enable is set, because the file ownership doesn't map into the service's PrivateUsers namespace and mosquitto can't read it. Install the ACL file into StateDirectory during preStart instead, alongside the password files. The file ends up owned by the mosquitto user with mode 0700, satisfying mosquitto's permission checks. Fixes #474135 --- .../modules/services/networking/mosquitto.nix | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index 9ac643706d40..fd16442ad185 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -378,11 +378,22 @@ let ++ userAsserts prefix listener.users ++ lib.imap0 (i: v: authAsserts "${prefix}.authPlugins.${toString i}" v) listener.authPlugins; + makeACLFile = + idx: listener: + pkgs.writeText "mosquitto-acl-${toString idx}.conf" ( + lib.concatStringsSep "\n" ( + lib.flatten [ + listener.acl + (lib.mapAttrsToList (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl) listener.users) + ] + ) + ); + formatListener = idx: listener: [ "listener ${toString listener.port} ${toString listener.address}" - "acl_file /etc/mosquitto/acl-${toString idx}.conf" + "acl_file ${cfg.dataDir}/acl-${toString idx}.conf" ] ++ lib.optional (!listener.omitPasswordAuth) "password_file ${cfg.dataDir}/passwd-${toString idx}" ++ formatFreeform { } listener.settings @@ -762,32 +773,13 @@ in UMask = "0077"; }; preStart = lib.concatStringsSep "\n" ( - lib.imap0 ( - idx: listener: - makePasswordFile (listenerScope idx) listener.users "${cfg.dataDir}/passwd-${toString idx}" - ) cfg.listeners + lib.imap0 (idx: listener: '' + ${makePasswordFile (listenerScope idx) listener.users "${cfg.dataDir}/passwd-${toString idx}"} + install -m 0700 ${makeACLFile idx listener} ${cfg.dataDir}/acl-${toString idx}.conf + '') cfg.listeners ); }; - environment.etc = lib.listToAttrs ( - lib.imap0 (idx: listener: { - name = "mosquitto/acl-${toString idx}.conf"; - value = { - user = config.users.users.mosquitto.name; - group = config.users.users.mosquitto.group; - mode = "0400"; - text = ( - lib.concatStringsSep "\n" ( - lib.flatten [ - listener.acl - (lib.mapAttrsToList (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl) listener.users) - ] - ) - ); - }; - }) cfg.listeners - ); - users.users.mosquitto = { description = "Mosquitto MQTT Broker Daemon owner"; group = "mosquitto";