at 24.11-pre 12 kB view raw
1{ config, pkgs, lib, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.mattermost; 8 9 database = "postgres://${cfg.localDatabaseUser}:${cfg.localDatabasePassword}@localhost:5432/${cfg.localDatabaseName}?sslmode=disable&connect_timeout=10"; 10 11 postgresPackage = config.services.postgresql.package; 12 13 createDb = { 14 statePath ? cfg.statePath, 15 localDatabaseUser ? cfg.localDatabaseUser, 16 localDatabasePassword ? cfg.localDatabasePassword, 17 localDatabaseName ? cfg.localDatabaseName, 18 useSudo ? true 19 }: '' 20 if ! test -e ${escapeShellArg "${statePath}/.db-created"}; then 21 ${lib.optionalString useSudo "${pkgs.sudo}/bin/sudo -u ${escapeShellArg config.services.postgresql.superUser} \\"} 22 ${postgresPackage}/bin/psql postgres -c \ 23 "CREATE ROLE ${localDatabaseUser} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${localDatabasePassword}'" 24 ${lib.optionalString useSudo "${pkgs.sudo}/bin/sudo -u ${escapeShellArg config.services.postgresql.superUser} \\"} 25 ${postgresPackage}/bin/createdb \ 26 --owner ${escapeShellArg localDatabaseUser} ${escapeShellArg localDatabaseName} 27 touch ${escapeShellArg "${statePath}/.db-created"} 28 fi 29 ''; 30 31 mattermostPluginDerivations = with pkgs; 32 map (plugin: stdenv.mkDerivation { 33 name = "mattermost-plugin"; 34 installPhase = '' 35 mkdir -p $out/share 36 cp ${plugin} $out/share/plugin.tar.gz 37 ''; 38 dontUnpack = true; 39 dontPatch = true; 40 dontConfigure = true; 41 dontBuild = true; 42 preferLocalBuild = true; 43 }) cfg.plugins; 44 45 mattermostPlugins = with pkgs; 46 if mattermostPluginDerivations == [] then null 47 else stdenv.mkDerivation { 48 name = "${cfg.package.name}-plugins"; 49 nativeBuildInputs = [ 50 autoPatchelfHook 51 ] ++ mattermostPluginDerivations; 52 buildInputs = [ 53 cfg.package 54 ]; 55 installPhase = '' 56 mkdir -p $out/data/plugins 57 plugins=(${escapeShellArgs (map (plugin: "${plugin}/share/plugin.tar.gz") mattermostPluginDerivations)}) 58 for plugin in "''${plugins[@]}"; do 59 hash="$(sha256sum "$plugin" | cut -d' ' -f1)" 60 mkdir -p "$hash" 61 tar -C "$hash" -xzf "$plugin" 62 autoPatchelf "$hash" 63 GZIP_OPT=-9 tar -C "$hash" -cvzf "$out/data/plugins/$hash.tar.gz" . 64 rm -rf "$hash" 65 done 66 ''; 67 68 dontUnpack = true; 69 dontPatch = true; 70 dontConfigure = true; 71 dontBuild = true; 72 preferLocalBuild = true; 73 }; 74 75 mattermostConfWithoutPlugins = recursiveUpdate 76 { ServiceSettings.SiteURL = cfg.siteUrl; 77 ServiceSettings.ListenAddress = cfg.listenAddress; 78 TeamSettings.SiteName = cfg.siteName; 79 SqlSettings.DriverName = "postgres"; 80 SqlSettings.DataSource = database; 81 PluginSettings.Directory = "${cfg.statePath}/plugins/server"; 82 PluginSettings.ClientDirectory = "${cfg.statePath}/plugins/client"; 83 } 84 cfg.extraConfig; 85 86 mattermostConf = recursiveUpdate 87 mattermostConfWithoutPlugins 88 ( 89 lib.optionalAttrs (mattermostPlugins != null) { 90 PluginSettings = { 91 Enable = true; 92 }; 93 } 94 ); 95 96 mattermostConfJSON = pkgs.writeText "mattermost-config.json" (builtins.toJSON mattermostConf); 97 98in 99 100{ 101 options = { 102 services.mattermost = { 103 enable = mkEnableOption "Mattermost chat server"; 104 105 package = mkPackageOption pkgs "mattermost" { }; 106 107 statePath = mkOption { 108 type = types.str; 109 default = "/var/lib/mattermost"; 110 description = "Mattermost working directory"; 111 }; 112 113 siteUrl = mkOption { 114 type = types.str; 115 example = "https://chat.example.com"; 116 description = '' 117 URL this Mattermost instance is reachable under, without trailing slash. 118 ''; 119 }; 120 121 siteName = mkOption { 122 type = types.str; 123 default = "Mattermost"; 124 description = "Name of this Mattermost site."; 125 }; 126 127 listenAddress = mkOption { 128 type = types.str; 129 default = ":8065"; 130 example = "[::1]:8065"; 131 description = '' 132 Address and port this Mattermost instance listens to. 133 ''; 134 }; 135 136 mutableConfig = mkOption { 137 type = types.bool; 138 default = false; 139 description = '' 140 Whether the Mattermost config.json is writeable by Mattermost. 141 142 Most of the settings can be edited in the system console of 143 Mattermost if this option is enabled. A template config using 144 the options specified in services.mattermost will be generated 145 but won't be overwritten on changes or rebuilds. 146 147 If this option is disabled, changes in the system console won't 148 be possible (default). If an config.json is present, it will be 149 overwritten! 150 ''; 151 }; 152 153 preferNixConfig = mkOption { 154 type = types.bool; 155 default = false; 156 description = '' 157 If both mutableConfig and this option are set, the Nix configuration 158 will take precedence over any settings configured in the server 159 console. 160 ''; 161 }; 162 163 extraConfig = mkOption { 164 type = types.attrs; 165 default = { }; 166 description = '' 167 Additional configuration options as Nix attribute set in config.json schema. 168 ''; 169 }; 170 171 plugins = mkOption { 172 type = types.listOf (types.oneOf [types.path types.package]); 173 default = []; 174 example = "[ ./com.github.moussetc.mattermost.plugin.giphy-2.0.0.tar.gz ]"; 175 description = '' 176 Plugins to add to the configuration. Overrides any installed if non-null. 177 This is a list of paths to .tar.gz files or derivations evaluating to 178 .tar.gz files. 179 ''; 180 }; 181 environmentFile = mkOption { 182 type = types.nullOr types.path; 183 default = null; 184 description = '' 185 Environment file (see {manpage}`systemd.exec(5)` 186 "EnvironmentFile=" section for the syntax) which sets config options 187 for mattermost (see [the mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)). 188 189 Settings defined in the environment file will overwrite settings 190 set via nix or via the {option}`services.mattermost.extraConfig` 191 option. 192 193 Useful for setting config options without their value ending up in the 194 (world-readable) nix store, e.g. for a database password. 195 ''; 196 }; 197 198 localDatabaseCreate = mkOption { 199 type = types.bool; 200 default = true; 201 description = '' 202 Create a local PostgreSQL database for Mattermost automatically. 203 ''; 204 }; 205 206 localDatabaseName = mkOption { 207 type = types.str; 208 default = "mattermost"; 209 description = '' 210 Local Mattermost database name. 211 ''; 212 }; 213 214 localDatabaseUser = mkOption { 215 type = types.str; 216 default = "mattermost"; 217 description = '' 218 Local Mattermost database username. 219 ''; 220 }; 221 222 localDatabasePassword = mkOption { 223 type = types.str; 224 default = "mmpgsecret"; 225 description = '' 226 Password for local Mattermost database user. 227 ''; 228 }; 229 230 user = mkOption { 231 type = types.str; 232 default = "mattermost"; 233 description = '' 234 User which runs the Mattermost service. 235 ''; 236 }; 237 238 group = mkOption { 239 type = types.str; 240 default = "mattermost"; 241 description = '' 242 Group which runs the Mattermost service. 243 ''; 244 }; 245 246 matterircd = { 247 enable = mkEnableOption "Mattermost IRC bridge"; 248 package = mkPackageOption pkgs "matterircd" { }; 249 parameters = mkOption { 250 type = types.listOf types.str; 251 default = [ ]; 252 example = [ "-mmserver chat.example.com" "-bind [::]:6667" ]; 253 description = '' 254 Set commandline parameters to pass to matterircd. See 255 https://github.com/42wim/matterircd#usage for more information. 256 ''; 257 }; 258 }; 259 }; 260 }; 261 262 config = mkMerge [ 263 (mkIf cfg.enable { 264 users.users = optionalAttrs (cfg.user == "mattermost") { 265 mattermost = { 266 group = cfg.group; 267 uid = config.ids.uids.mattermost; 268 home = cfg.statePath; 269 }; 270 }; 271 272 users.groups = optionalAttrs (cfg.group == "mattermost") { 273 mattermost.gid = config.ids.gids.mattermost; 274 }; 275 276 services.postgresql.enable = cfg.localDatabaseCreate; 277 278 # The systemd service will fail to execute the preStart hook 279 # if the WorkingDirectory does not exist 280 systemd.tmpfiles.settings."10-mattermost".${cfg.statePath}.d = { }; 281 282 systemd.services.mattermost = { 283 description = "Mattermost chat service"; 284 wantedBy = [ "multi-user.target" ]; 285 after = [ "network.target" "postgresql.service" ]; 286 287 preStart = '' 288 mkdir -p "${cfg.statePath}"/{data,config,logs,plugins} 289 mkdir -p "${cfg.statePath}/plugins"/{client,server} 290 ln -sf ${cfg.package}/{bin,fonts,i18n,templates,client} "${cfg.statePath}" 291 '' + lib.optionalString (mattermostPlugins != null) '' 292 rm -rf "${cfg.statePath}/data/plugins" 293 ln -sf ${mattermostPlugins}/data/plugins "${cfg.statePath}/data" 294 '' + lib.optionalString (!cfg.mutableConfig) '' 295 rm -f "${cfg.statePath}/config/config.json" 296 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${cfg.package}/config/config.json ${mattermostConfJSON} > "${cfg.statePath}/config/config.json" 297 '' + lib.optionalString cfg.mutableConfig '' 298 if ! test -e "${cfg.statePath}/config/.initial-created"; then 299 rm -f ${cfg.statePath}/config/config.json 300 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${cfg.package}/config/config.json ${mattermostConfJSON} > "${cfg.statePath}/config/config.json" 301 touch "${cfg.statePath}/config/.initial-created" 302 fi 303 '' + lib.optionalString (cfg.mutableConfig && cfg.preferNixConfig) '' 304 new_config="$(${pkgs.jq}/bin/jq -s '.[0] * .[1]' "${cfg.statePath}/config/config.json" ${mattermostConfJSON})" 305 306 rm -f "${cfg.statePath}/config/config.json" 307 echo "$new_config" > "${cfg.statePath}/config/config.json" 308 '' + lib.optionalString cfg.localDatabaseCreate (createDb {}) + '' 309 # Don't change permissions recursively on the data, current, and symlinked directories (see ln -sf command above). 310 # This dramatically decreases startup times for installations with a lot of files. 311 find . -maxdepth 1 -not -name data -not -name client -not -name templates -not -name i18n -not -name fonts -not -name bin -not -name . \ 312 -exec chown "${cfg.user}:${cfg.group}" -R {} \; -exec chmod u+rw,g+r,o-rwx -R {} \; 313 314 chown "${cfg.user}:${cfg.group}" "${cfg.statePath}/data" . 315 chmod u+rw,g+r,o-rwx "${cfg.statePath}/data" . 316 ''; 317 318 serviceConfig = { 319 PermissionsStartOnly = true; 320 User = cfg.user; 321 Group = cfg.group; 322 ExecStart = "${cfg.package}/bin/mattermost"; 323 WorkingDirectory = "${cfg.statePath}"; 324 Restart = "always"; 325 RestartSec = "10"; 326 LimitNOFILE = "49152"; 327 EnvironmentFile = cfg.environmentFile; 328 }; 329 unitConfig.JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service"; 330 }; 331 }) 332 (mkIf cfg.matterircd.enable { 333 systemd.services.matterircd = { 334 description = "Mattermost IRC bridge service"; 335 wantedBy = [ "multi-user.target" ]; 336 serviceConfig = { 337 User = "nobody"; 338 Group = "nogroup"; 339 ExecStart = "${cfg.matterircd.package}/bin/matterircd ${escapeShellArgs cfg.matterircd.parameters}"; 340 WorkingDirectory = "/tmp"; 341 PrivateTmp = true; 342 Restart = "always"; 343 RestartSec = "5"; 344 }; 345 }; 346 }) 347 ]; 348}