diff options
Diffstat (limited to 'nixos/modules/services/mail/postfixadmin.nix')
-rw-r--r-- | nixos/modules/services/mail/postfixadmin.nix | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/nixos/modules/services/mail/postfixadmin.nix b/nixos/modules/services/mail/postfixadmin.nix new file mode 100644 index 00000000000..a0846ad5290 --- /dev/null +++ b/nixos/modules/services/mail/postfixadmin.nix @@ -0,0 +1,199 @@ +{ lib, config, pkgs, ... }: + +with lib; + +let + cfg = config.services.postfixadmin; + fpm = config.services.phpfpm.pools.postfixadmin; + localDB = cfg.database.host == "localhost"; + user = if localDB then cfg.database.username else "nginx"; +in +{ + options.services.postfixadmin = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable postfixadmin. + + Also enables nginx virtual host management. + Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.<name></literal>. + See <xref linkend="opt-services.nginx.virtualHosts"/> for further information. + ''; + }; + + hostName = mkOption { + type = types.str; + example = "postfixadmin.example.com"; + description = "Hostname to use for the nginx vhost"; + }; + + adminEmail = mkOption { + type = types.str; + example = "postmaster@example.com"; + description = '' + Defines the Site Admin's email address. + This will be used to send emails from to create mailboxes and + from Send Email / Broadcast message pages. + ''; + }; + + setupPasswordFile = mkOption { + type = types.path; + description = '' + Password file for the admin. + Generate with <literal>php -r "echo password_hash('some password here', PASSWORD_DEFAULT);"</literal> + ''; + }; + + database = { + username = mkOption { + type = types.str; + default = "postfixadmin"; + description = '' + Username for the postgresql connection. + If <literal>database.host</literal> is set to <literal>localhost</literal>, a unix user and group of the same name will be created as well. + ''; + }; + host = mkOption { + type = types.str; + default = "localhost"; + description = '' + Host of the postgresql server. If this is not set to + <literal>localhost</literal>, you have to create the + postgresql user and database yourself, with appropriate + permissions. + ''; + }; + passwordFile = mkOption { + type = types.path; + description = "Password file for the postgresql connection. Must be readable by user <literal>nginx</literal>."; + }; + dbname = mkOption { + type = types.str; + default = "postfixadmin"; + description = "Name of the postgresql database"; + }; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra configuration for the postfixadmin instance, see postfixadmin's config.inc.php for available options."; + }; + }; + + config = mkIf cfg.enable { + environment.etc."postfixadmin/config.local.php".text = '' + <?php + + $CONF['setup_password'] = file_get_contents('${cfg.setupPasswordFile}'); + + $CONF['database_type'] = 'pgsql'; + $CONF['database_host'] = ${if localDB then "null" else "'${cfg.database.host}'"}; + ${optionalString localDB "$CONF['database_user'] = '${cfg.database.username}';"} + $CONF['database_password'] = ${if localDB then "'dummy'" else "file_get_contents('${cfg.database.passwordFile}')"}; + $CONF['database_name'] = '${cfg.database.dbname}'; + $CONF['configured'] = true; + + ${cfg.extraConfig} + ''; + + systemd.tmpfiles.rules = [ "d /var/cache/postfixadmin/templates_c 700 ${user} ${user}" ]; + + services.nginx = { + enable = true; + virtualHosts = { + ${cfg.hostName} = { + forceSSL = mkDefault true; + enableACME = mkDefault true; + locations."/" = { + root = "${pkgs.postfixadmin}/public"; + index = "index.php"; + extraConfig = '' + location ~* \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${fpm.socket}; + include ${config.services.nginx.package}/conf/fastcgi_params; + include ${pkgs.nginx}/conf/fastcgi.conf; + } + ''; + }; + }; + }; + }; + + services.postgresql = mkIf localDB { + enable = true; + ensureUsers = [ { + name = cfg.database.username; + } ]; + }; + # The postgresql module doesn't currently support concepts like + # objects owners and extensions; for now we tack on what's needed + # here. + systemd.services.postfixadmin-postgres = let pgsql = config.services.postgresql; in mkIf localDB { + after = [ "postgresql.service" ]; + bindsTo = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ + pgsql.package + pkgs.util-linux + ]; + script = '' + set -eu + + PSQL() { + psql --port=${toString pgsql.port} "$@" + } + + PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.database.dbname}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.database.dbname}" OWNER "${cfg.database.username}"' + current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.database.dbname}'") + if [[ "$current_owner" != "${cfg.database.username}" ]]; then + PSQL -tAc 'ALTER DATABASE "${cfg.database.dbname}" OWNER TO "${cfg.database.username}"' + if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" ]]; then + echo "Reassigning ownership of database ${cfg.database.dbname} to user ${cfg.database.username} failed on last boot. Failing..." + exit 1 + fi + touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" + PSQL "${cfg.database.dbname}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.database.username}\"" + rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" + fi + ''; + + serviceConfig = { + User = pgsql.superUser; + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + users.users.${user} = mkIf localDB { + group = user; + isSystemUser = true; + createHome = false; + }; + users.groups.${user} = mkIf localDB {}; + + services.phpfpm.pools.postfixadmin = { + user = user; + phpPackage = pkgs.php74; + phpOptions = '' + error_log = 'stderr' + log_errors = on + ''; + settings = mapAttrs (name: mkDefault) { + "listen.owner" = "nginx"; + "listen.group" = "nginx"; + "listen.mode" = "0660"; + "pm" = "dynamic"; + "pm.max_children" = 75; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 1; + "pm.max_spare_servers" = 20; + "pm.max_requests" = 500; + "catch_workers_output" = true; + }; + }; + }; +} |