at 24.11-pre 6.7 kB view raw
1{ lib, config, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.postfixadmin; 7 fpm = config.services.phpfpm.pools.postfixadmin; 8 localDB = cfg.database.host == "localhost"; 9 user = if localDB then cfg.database.username else "nginx"; 10in 11{ 12 options.services.postfixadmin = { 13 enable = mkOption { 14 type = types.bool; 15 default = false; 16 description = '' 17 Whether to enable postfixadmin. 18 19 Also enables nginx virtual host management. 20 Further nginx configuration can be done by adapting `services.nginx.virtualHosts.<name>`. 21 See [](#opt-services.nginx.virtualHosts) for further information. 22 ''; 23 }; 24 25 hostName = mkOption { 26 type = types.str; 27 example = "postfixadmin.example.com"; 28 description = "Hostname to use for the nginx vhost"; 29 }; 30 31 adminEmail = mkOption { 32 type = types.str; 33 example = "postmaster@example.com"; 34 description = '' 35 Defines the Site Admin's email address. 36 This will be used to send emails from to create mailboxes and 37 from Send Email / Broadcast message pages. 38 ''; 39 }; 40 41 setupPasswordFile = mkOption { 42 type = types.path; 43 description = '' 44 Password file for the admin. 45 Generate with `php -r "echo password_hash('some password here', PASSWORD_DEFAULT);"` 46 ''; 47 }; 48 49 database = { 50 username = mkOption { 51 type = types.str; 52 default = "postfixadmin"; 53 description = '' 54 Username for the postgresql connection. 55 If `database.host` is set to `localhost`, a unix user and group of the same name will be created as well. 56 ''; 57 }; 58 host = mkOption { 59 type = types.str; 60 default = "localhost"; 61 description = '' 62 Host of the postgresql server. If this is not set to 63 `localhost`, you have to create the 64 postgresql user and database yourself, with appropriate 65 permissions. 66 ''; 67 }; 68 passwordFile = mkOption { 69 type = types.path; 70 description = "Password file for the postgresql connection. Must be readable by user `nginx`."; 71 }; 72 dbname = mkOption { 73 type = types.str; 74 default = "postfixadmin"; 75 description = "Name of the postgresql database"; 76 }; 77 }; 78 79 extraConfig = mkOption { 80 type = types.lines; 81 default = ""; 82 description = "Extra configuration for the postfixadmin instance, see postfixadmin's config.inc.php for available options."; 83 }; 84 }; 85 86 config = mkIf cfg.enable { 87 environment.etc."postfixadmin/config.local.php".text = '' 88 <?php 89 90 $CONF['setup_password'] = file_get_contents('${cfg.setupPasswordFile}'); 91 92 $CONF['database_type'] = 'pgsql'; 93 $CONF['database_host'] = ${if localDB then "null" else "'${cfg.database.host}'"}; 94 ${optionalString localDB "$CONF['database_user'] = '${cfg.database.username}';"} 95 $CONF['database_password'] = ${if localDB then "'dummy'" else "file_get_contents('${cfg.database.passwordFile}')"}; 96 $CONF['database_name'] = '${cfg.database.dbname}'; 97 $CONF['configured'] = true; 98 99 ${cfg.extraConfig} 100 ''; 101 102 systemd.tmpfiles.settings."10-postfixadmin"."/var/cache/postfixadmin/templates_c".d = { 103 inherit user; 104 group = user; 105 mode = "700"; 106 }; 107 108 services.nginx = { 109 enable = true; 110 virtualHosts = { 111 ${cfg.hostName} = { 112 forceSSL = mkDefault true; 113 enableACME = mkDefault true; 114 locations."/" = { 115 root = "${pkgs.postfixadmin}/public"; 116 index = "index.php"; 117 extraConfig = '' 118 location ~* \.php$ { 119 fastcgi_split_path_info ^(.+\.php)(/.+)$; 120 fastcgi_pass unix:${fpm.socket}; 121 include ${config.services.nginx.package}/conf/fastcgi_params; 122 include ${pkgs.nginx}/conf/fastcgi.conf; 123 } 124 ''; 125 }; 126 }; 127 }; 128 }; 129 130 services.postgresql = mkIf localDB { 131 enable = true; 132 ensureUsers = [ { 133 name = cfg.database.username; 134 } ]; 135 }; 136 # The postgresql module doesn't currently support concepts like 137 # objects owners and extensions; for now we tack on what's needed 138 # here. 139 systemd.services.postfixadmin-postgres = let pgsql = config.services.postgresql; in mkIf localDB { 140 after = [ "postgresql.service" ]; 141 bindsTo = [ "postgresql.service" ]; 142 wantedBy = [ "multi-user.target" ]; 143 path = [ 144 pgsql.package 145 pkgs.util-linux 146 ]; 147 script = '' 148 set -eu 149 150 PSQL() { 151 psql --port=${toString pgsql.port} "$@" 152 } 153 154 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}"' 155 current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.database.dbname}'") 156 if [[ "$current_owner" != "${cfg.database.username}" ]]; then 157 PSQL -tAc 'ALTER DATABASE "${cfg.database.dbname}" OWNER TO "${cfg.database.username}"' 158 if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" ]]; then 159 echo "Reassigning ownership of database ${cfg.database.dbname} to user ${cfg.database.username} failed on last boot. Failing..." 160 exit 1 161 fi 162 touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" 163 PSQL "${cfg.database.dbname}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.database.username}\"" 164 rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" 165 fi 166 ''; 167 168 serviceConfig = { 169 User = pgsql.superUser; 170 Type = "oneshot"; 171 RemainAfterExit = true; 172 }; 173 }; 174 175 users.users.${user} = mkIf localDB { 176 group = user; 177 isSystemUser = true; 178 createHome = false; 179 }; 180 users.groups.${user} = mkIf localDB {}; 181 182 services.phpfpm.pools.postfixadmin = { 183 user = user; 184 phpPackage = pkgs.php81; 185 phpOptions = '' 186 error_log = 'stderr' 187 log_errors = on 188 ''; 189 settings = mapAttrs (name: mkDefault) { 190 "listen.owner" = "nginx"; 191 "listen.group" = "nginx"; 192 "listen.mode" = "0660"; 193 "pm" = "dynamic"; 194 "pm.max_children" = 75; 195 "pm.start_servers" = 2; 196 "pm.min_spare_servers" = 1; 197 "pm.max_spare_servers" = 20; 198 "pm.max_requests" = 500; 199 "catch_workers_output" = true; 200 }; 201 }; 202 }; 203}