lol

nixos/postgresql: drop ensurePermissions, fix ensureUsers for postgresql15

Closes #216989

First of all, a bit of context: in PostgreSQL, newly created users don't
have the CREATE privilege on the public schema of a database even with
`ALL PRIVILEGES` granted via `ensurePermissions` which is how most of
the DB users are currently set up "declaratively"[1]. This means e.g. a
freshly deployed Nextcloud service will break early because Nextcloud
itself cannot CREATE any tables in the public schema anymore.

The other issue here is that `ensurePermissions` is a mere hack. It's
effectively a mixture of SQL code (e.g. `DATABASE foo` is relying on how
a value is substituted in a query. You'd have to parse a subset of SQL
to actually know which object are permissions granted to for a user).

After analyzing the existing modules I realized that in every case with
a single exception[2] the UNIX system user is equal to the db user is
equal to the db name and I don't see a compelling reason why people
would change that in 99% of the cases. In fact, some modules would even
break if you'd change that because the declarations of the system user &
the db user are mixed up[3].

So I decided to go with something new which restricts the ways to use
`ensure*` options rather than expanding those[4]. Effectively this means
that

* The DB user _must_ be equal to the DB name.
* Permissions are granted via `ensureDBOwnerhip` for an attribute-set in
`ensureUsers`. That way, the user is actually the owner and can
perform `CREATE`.
* For such a postgres user, a database must be declared in
`ensureDatabases`.

For anything else, a custom state management should be implemented. This
can either be `initialScript`, doing it manual, outside of the module or
by implementing proper state management for postgresql[5], but the
current state of `ensure*` isn't even declarative, but a convergent tool
which is what Nix actually claims to _not_ do.

Regarding existing setups: there are effectively two options:

* Leave everything as-is (assuming that system user == db user == db
name): then the DB user will automatically become the DB owner and
everything else stays the same.

* Drop the `createDatabase = true;` declarations: nothing will change
because a removal of `ensure*` statements is ignored, so it doesn't
matter at all whether this option is kept after the first deploy (and
later on you'd usually restore from backups anyways).

The DB user isn't the owner of the DB then, but for an existing setup
this is irrelevant because CREATE on the public schema isn't revoked
from existing users (only not granted for new users).

[1] not really declarative though because removals of these statements
are simply ignored for instance: https://github.com/NixOS/nixpkgs/issues/206467
[2] `services.invidious`: I removed the `ensure*` part temporarily
because it IMHO falls into the category "manage the state on your
own" (see the commit message). See also
https://github.com/NixOS/nixpkgs/pull/265857
[3] e.g. roundcube had `"DATABASE ${cfg.database.username}" = "ALL PRIVILEGES";`
[4] As opposed to other changes that are considered a potential fix, but
also add more things like collation for DBs or passwords that are
_never_ touched again when changing those.
[5] As suggested in e.g. https://github.com/NixOS/nixpkgs/issues/206467

authored by

Maximilian Bosch and committed by
Raito Bezarius
48459567 c42941c5

+176 -153
+30 -35
nixos/modules/services/databases/postgresql.nix
··· 165 165 ''; 166 166 }; 167 167 168 - ensurePermissions = mkOption { 169 - type = types.attrsOf types.str; 170 - default = {}; 171 - description = lib.mdDoc '' 172 - Permissions to ensure for the user, specified as an attribute set. 173 - The attribute names specify the database and tables to grant the permissions for. 174 - The attribute values specify the permissions to grant. You may specify one or 175 - multiple comma-separated SQL privileges here. 176 - 177 - For more information on how to specify the target 178 - and on which privileges exist, see the 179 - [GRANT syntax](https://www.postgresql.org/docs/current/sql-grant.html). 180 - The attributes are used as `GRANT ''${attrValue} ON ''${attrName}`. 181 - ''; 182 - example = literalExpression '' 183 - { 184 - "DATABASE \"nextcloud\"" = "ALL PRIVILEGES"; 185 - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; 186 - } 168 + ensureDBOwnership = mkOption { 169 + type = types.bool; 170 + default = false; 171 + description = mdDoc '' 172 + Grants the user ownership to a database with the same name. 173 + This database must be defined manually in 174 + [](#opt-services.postgresql.ensureDatabases). 187 175 ''; 188 176 }; 189 177 ··· 338 326 }); 339 327 default = []; 340 328 description = lib.mdDoc '' 341 - Ensures that the specified users exist and have at least the ensured permissions. 329 + Ensures that the specified users exist. 342 330 The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the 343 331 same name only, and that without the need for a password. 344 - This option will never delete existing users or remove permissions, especially not when the value of this 345 - option is changed. This means that users created and permissions assigned once through this option or 346 - otherwise have to be removed manually. 332 + This option will never delete existing users or remove DB ownership of databases 333 + once granted with `ensureDBOwnership = true;`. This means that this must be 334 + cleaned up manually when changing after changing the config in here. 347 335 ''; 348 336 example = literalExpression '' 349 337 [ 350 338 { 351 339 name = "nextcloud"; 352 - ensurePermissions = { 353 - "DATABASE nextcloud" = "ALL PRIVILEGES"; 354 - }; 355 340 } 356 341 { 357 342 name = "superuser"; 358 - ensurePermissions = { 359 - "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; 360 - }; 343 + ensureDBOwnership = true; 361 344 } 362 345 ] 363 346 ''; ··· 445 428 446 429 config = mkIf cfg.enable { 447 430 431 + assertions = [ 432 + { 433 + assertion = all 434 + ({ name, ensureDBOwnership, ... }: ensureDBOwnership -> builtins.elem name cfg.ensureDatabases) 435 + cfg.ensureUsers; 436 + message = '' 437 + For each database user defined with `services.postgresql.ensureUsers` and 438 + `ensureDBOwnership = true;`, a database with the same name must be defined 439 + in `services.postgresql.ensureDatabases`. 440 + ''; 441 + } 442 + ]; 443 + 448 444 services.postgresql.settings = 449 445 { 450 446 hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}"; ··· 557 553 concatMapStrings 558 554 (user: 559 555 let 560 - userPermissions = concatStringsSep "\n" 561 - (mapAttrsToList 562 - (database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '') 563 - user.ensurePermissions 564 - ); 556 + dbOwnershipStmt = optionalString 557 + user.ensureDBOwnership 558 + ''$PSQL -tAc 'ALTER DATABASE "${user.name}" OWNER TO "${user.name}";' ''; 565 559 566 560 filteredClauses = filterAttrs (name: value: value != null) user.ensureClauses; 567 561 ··· 570 564 userClauses = ''$PSQL -tAc 'ALTER ROLE "${user.name}" ${concatStringsSep " " clauseSqlStatements}' ''; 571 565 in '' 572 566 $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' 573 - ${userPermissions} 574 567 ${userClauses} 568 + 569 + ${dbOwnershipStmt} 575 570 '' 576 571 ) 577 572 cfg.ensureUsers
+2 -2
nixos/modules/services/development/zammad.nix
··· 204 204 205 205 assertions = [ 206 206 { 207 - assertion = cfg.database.createLocally -> cfg.database.user == "zammad"; 207 + assertion = cfg.database.createLocally -> cfg.database.user == "zammad" && cfg.database.name == "zammad"; 208 208 message = "services.zammad.database.user must be set to \"zammad\" if services.zammad.database.createLocally is set to true"; 209 209 } 210 210 { ··· 231 231 ensureUsers = [ 232 232 { 233 233 name = cfg.database.user; 234 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 234 + ensureDBOwnership = true; 235 235 } 236 236 ]; 237 237 };
+1 -1
nixos/modules/services/finance/odoo.nix
··· 121 121 ensureDatabases = [ "odoo" ]; 122 122 ensureUsers = [{ 123 123 name = "odoo"; 124 - ensurePermissions = { "DATABASE odoo" = "ALL PRIVILEGES"; }; 124 + ensureDBOwnership = true; 125 125 }]; 126 126 }; 127 127 });
+1 -1
nixos/modules/services/mail/listmonk.nix
··· 168 168 169 169 ensureUsers = [{ 170 170 name = "listmonk"; 171 - ensurePermissions = { "DATABASE listmonk" = "ALL PRIVILEGES"; }; 171 + ensureDBOwnership = true; 172 172 }]; 173 173 174 174 ensureDatabases = [ "listmonk" ];
+11 -3
nixos/modules/services/mail/roundcube.nix
··· 179 179 }; 180 180 }; 181 181 182 + assertions = [ 183 + { 184 + assertion = localDB -> cfg.database.username == cfg.database.dbname; 185 + message = '' 186 + When setting up a DB and its owner user, the owner and the DB name must be 187 + equal! 188 + ''; 189 + } 190 + ]; 191 + 182 192 services.postgresql = mkIf localDB { 183 193 enable = true; 184 194 ensureDatabases = [ cfg.database.dbname ]; 185 195 ensureUsers = [ { 186 196 name = cfg.database.username; 187 - ensurePermissions = { 188 - "DATABASE ${cfg.database.username}" = "ALL PRIVILEGES"; 189 - }; 197 + ensureDBOwnership = true; 190 198 } ]; 191 199 }; 192 200
+4 -6
nixos/modules/services/mail/sympa.nix
··· 218 218 default = null; 219 219 example = "/run/keys/sympa-dbpassword"; 220 220 description = lib.mdDoc '' 221 - A file containing the password for {option}`services.sympa.database.user`. 221 + A file containing the password for {option}`services.sympa.database.name`. 222 222 ''; 223 223 }; 224 224 ··· 342 342 343 343 db_type = cfg.database.type; 344 344 db_name = cfg.database.name; 345 + db_user = cfg.database.name; 345 346 } 346 347 // (optionalAttrs (cfg.database.host != null) { 347 348 db_host = cfg.database.host; ··· 354 355 }) 355 356 // (optionalAttrs (cfg.database.port != null) { 356 357 db_port = cfg.database.port; 357 - }) 358 - // (optionalAttrs (cfg.database.user != null) { 359 - db_user = cfg.database.user; 360 358 }) 361 359 // (optionalAttrs (cfg.mta.type == "postfix") { 362 360 sendmail_aliases = "${dataDir}/sympa_transport"; ··· 393 391 users.groups.${group} = {}; 394 392 395 393 assertions = [ 396 - { assertion = cfg.database.createLocally -> cfg.database.user == user; 394 + { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user; 397 395 message = "services.sympa.database.user must be set to ${user} if services.sympa.database.createLocally is set to true"; 398 396 } 399 397 { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; ··· 579 577 ensureDatabases = [ cfg.database.name ]; 580 578 ensureUsers = [ 581 579 { name = cfg.database.user; 582 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 580 + ensureDBOwnership = true; 583 581 } 584 582 ]; 585 583 };
+2 -2
nixos/modules/services/matrix/matrix-sliding-sync.nix
··· 74 74 services.postgresql = lib.optionalAttrs cfg.createDatabase { 75 75 enable = true; 76 76 ensureDatabases = [ "matrix-sliding-sync" ]; 77 - ensureUsers = [ rec { 77 + ensureUsers = [ { 78 78 name = "matrix-sliding-sync"; 79 - ensurePermissions."DATABASE \"${name}\"" = "ALL PRIVILEGES"; 79 + ensureDBOwnership = true; 80 80 } ]; 81 81 }; 82 82
+1 -3
nixos/modules/services/matrix/mautrix-facebook.nix
··· 135 135 ensureDatabases = ["mautrix-facebook"]; 136 136 ensureUsers = [{ 137 137 name = "mautrix-facebook"; 138 - ensurePermissions = { 139 - "DATABASE \"mautrix-facebook\"" = "ALL PRIVILEGES"; 140 - }; 138 + ensureDBOwnership = true; 141 139 }]; 142 140 }; 143 141
+1 -3
nixos/modules/services/misc/atuin.nix
··· 73 73 enable = true; 74 74 ensureUsers = [{ 75 75 name = "atuin"; 76 - ensurePermissions = { 77 - "DATABASE atuin" = "ALL PRIVILEGES"; 78 - }; 76 + ensureDBOwnership = true; 79 77 }]; 80 78 ensureDatabases = [ "atuin" ]; 81 79 };
+9 -1
nixos/modules/services/misc/forgejo.nix
··· 357 357 assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user; 358 358 message = "services.forgejo.database.user must match services.forgejo.user if the database is to be automatically provisioned"; 359 359 } 360 + { assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name; 361 + message = '' 362 + When creating a database via NixOS, the db user and db name must be equal! 363 + If you already have an existing DB+user and this assertion is new, you can safely set 364 + `services.forgejo.createDatabase` to `false` because removal of `ensureUsers` 365 + and `ensureDatabases` doesn't have any effect. 366 + ''; 367 + } 360 368 ]; 361 369 362 370 services.forgejo.settings = { ··· 423 431 ensureUsers = [ 424 432 { 425 433 name = cfg.database.user; 426 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 434 + ensureDBOwnership = true; 427 435 } 428 436 ]; 429 437 };
+9 -1
nixos/modules/services/misc/gitea.nix
··· 394 394 { assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user; 395 395 message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned"; 396 396 } 397 + { assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name; 398 + message = '' 399 + When creating a database via NixOS, the db user and db name must be equal! 400 + If you already have an existing DB+user and this assertion is new, you can safely set 401 + `services.gitea.createDatabase` to `false` because removal of `ensureUsers` 402 + and `ensureDatabases` doesn't have any effect. 403 + ''; 404 + } 397 405 ]; 398 406 399 407 services.gitea.settings = { ··· 461 469 ensureDatabases = [ cfg.database.name ]; 462 470 ensureUsers = [ 463 471 { name = cfg.database.user; 464 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 472 + ensureDBOwnership = true; 465 473 } 466 474 ]; 467 475 };
+2 -2
nixos/modules/services/misc/redmine.nix
··· 267 267 { assertion = cfg.database.passwordFile != null || cfg.database.socket != null; 268 268 message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set"; 269 269 } 270 - { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user; 270 + { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user && cfg.database.user == cfg.database.name; 271 271 message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true"; 272 272 } 273 273 { assertion = cfg.database.createLocally -> cfg.database.socket != null; ··· 315 315 ensureDatabases = [ cfg.database.name ]; 316 316 ensureUsers = [ 317 317 { name = cfg.database.user; 318 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 318 + ensureDBOwnership = true; 319 319 } 320 320 ]; 321 321 };
+10 -1
nixos/modules/services/misc/sourcehut/service.nix
··· 242 242 } cfg.nginx.virtualHost ]; 243 243 }; 244 244 245 + assertions = [ 246 + { 247 + assertion = srvCfg.user == srvCfg.postgresql.database; 248 + message = '' 249 + When creating a database via NixOS, the db user and db name must be equal! 250 + ''; 251 + } 252 + ]; 253 + 245 254 services.postgresql = mkIf cfg.postgresql.enable { 246 255 authentication = '' 247 256 local ${srvCfg.postgresql.database} ${srvCfg.user} trust ··· 249 258 ensureDatabases = [ srvCfg.postgresql.database ]; 250 259 ensureUsers = map (name: { 251 260 inherit name; 252 - ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; }; 261 + ensureDBOwnership = true; 253 262 }) [srvCfg.user]; 254 263 }; 255 264
+2 -2
nixos/modules/services/monitoring/zabbix-proxy.nix
··· 203 203 { assertion = !config.services.zabbixServer.enable; 204 204 message = "Please choose one of services.zabbixServer or services.zabbixProxy."; 205 205 } 206 - { assertion = cfg.database.createLocally -> cfg.database.user == user; 206 + { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user; 207 207 message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true"; 208 208 } 209 209 { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; ··· 252 252 ensureDatabases = [ cfg.database.name ]; 253 253 ensureUsers = [ 254 254 { name = cfg.database.user; 255 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 255 + ensureDBOwnership = true; 256 256 } 257 257 ]; 258 258 };
+2 -2
nixos/modules/services/monitoring/zabbix-server.nix
··· 191 191 config = mkIf cfg.enable { 192 192 193 193 assertions = [ 194 - { assertion = cfg.database.createLocally -> cfg.database.user == user; 194 + { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name; 195 195 message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true"; 196 196 } 197 197 { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; ··· 240 240 ensureDatabases = [ cfg.database.name ]; 241 241 ensureUsers = [ 242 242 { name = cfg.database.user; 243 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 243 + ensureDBOwnership = true; 244 244 } 245 245 ]; 246 246 };
+1 -1
nixos/modules/services/security/hockeypuck.nix
··· 55 55 ensureDatabases = [ "hockeypuck" ]; 56 56 ensureUsers = [{ 57 57 name = "hockeypuck"; 58 - ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES"; 58 + ensureDBOwnership = true; 59 59 }]; 60 60 }; 61 61 ```
+4 -6
nixos/modules/services/web-apps/coder.nix
··· 149 149 150 150 config = mkIf cfg.enable { 151 151 assertions = [ 152 - { assertion = cfg.database.createLocally -> cfg.database.username == name; 153 - message = "services.coder.database.username must be set to ${user} if services.coder.database.createLocally is set true"; 152 + { assertion = cfg.database.createLocally -> cfg.database.username == name && cfg.database.database == cfg.database.username; 153 + message = "services.coder.database.username must be set to ${name} if services.coder.database.createLocally is set true"; 154 154 } 155 155 ]; 156 156 ··· 193 193 cfg.database.database 194 194 ]; 195 195 ensureUsers = [{ 196 - name = cfg.database.username; 197 - ensurePermissions = { 198 - "DATABASE \"${cfg.database.database}\"" = "ALL PRIVILEGES"; 199 - }; 196 + name = cfg.user; 197 + ensureDBOwnership = true; 200 198 } 201 199 ]; 202 200 };
+1 -3
nixos/modules/services/web-apps/gotosocial.nix
··· 128 128 ensureUsers = [ 129 129 { 130 130 name = "gotosocial"; 131 - ensurePermissions = { 132 - "DATABASE gotosocial" = "ALL PRIVILEGES"; 133 - }; 131 + ensureDBOwnership = true; 134 132 } 135 133 ]; 136 134 };
-6
nixos/modules/services/web-apps/invidious.nix
··· 112 112 services.postgresql = { 113 113 enable = true; 114 114 ensureDatabases = lib.singleton cfg.settings.db.dbname; 115 - ensureUsers = lib.singleton { 116 - name = cfg.settings.db.user; 117 - ensurePermissions = { 118 - "DATABASE ${cfg.settings.db.dbname}" = "ALL PRIVILEGES"; 119 - }; 120 - }; 121 115 # This is only needed because the unix user invidious isn't the same as 122 116 # the database user. This tells postgres to map one to the other. 123 117 identMap = ''
+1 -1
nixos/modules/services/web-apps/lemmy.nix
··· 146 146 ensureDatabases = [ cfg.settings.database.database ]; 147 147 ensureUsers = [{ 148 148 name = cfg.settings.database.user; 149 - ensurePermissions."DATABASE ${cfg.settings.database.database}" = "ALL PRIVILEGES"; 149 + ensureDBOwnership = true; 150 150 }]; 151 151 }; 152 152
+3 -3
nixos/modules/services/web-apps/mastodon.nix
··· 557 557 config = lib.mkIf cfg.enable (lib.mkMerge [{ 558 558 assertions = [ 559 559 { 560 - assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user); 560 + assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user && cfg.database.user == cfg.database.name); 561 561 message = '' 562 562 For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer 563 563 authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user ··· 799 799 enable = true; 800 800 ensureUsers = [ 801 801 { 802 - name = cfg.database.user; 803 - ensurePermissions."DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; 802 + name = cfg.database.name; 803 + ensureDBOwnership = true; 804 804 } 805 805 ]; 806 806 ensureDatabases = [ cfg.database.name ];
+2 -2
nixos/modules/services/web-apps/mediawiki.nix
··· 454 454 { assertion = cfg.database.createLocally -> (cfg.database.type == "mysql" || cfg.database.type == "postgres"); 455 455 message = "services.mediawiki.createLocally is currently only supported for database type 'mysql' and 'postgres'"; 456 456 } 457 - { assertion = cfg.database.createLocally -> cfg.database.user == user; 457 + { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user; 458 458 message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true"; 459 459 } 460 460 { assertion = cfg.database.createLocally -> cfg.database.socket != null; ··· 486 486 ensureDatabases = [ cfg.database.name ]; 487 487 ensureUsers = [{ 488 488 name = cfg.database.user; 489 - ensurePermissions = { "DATABASE \"${cfg.database.name}\"" = "ALL PRIVILEGES"; }; 489 + ensureDBOwnership = true; 490 490 }]; 491 491 }; 492 492
+6 -11
nixos/modules/services/web-apps/miniflux.nix
··· 6 6 7 7 defaultAddress = "localhost:8080"; 8 8 9 - dbUser = "miniflux"; 10 - dbName = "miniflux"; 11 - 12 9 pgbin = "${config.services.postgresql.package}/bin"; 13 10 preStart = pkgs.writeScript "miniflux-pre-start" '' 14 11 #!${pkgs.runtimeShell} 15 - ${pgbin}/psql "${dbName}" -c "CREATE EXTENSION IF NOT EXISTS hstore" 12 + ${pgbin}/psql "miniflux" -c "CREATE EXTENSION IF NOT EXISTS hstore" 16 13 ''; 17 14 in 18 15 ··· 62 59 63 60 services.miniflux.config = { 64 61 LISTEN_ADDR = mkDefault defaultAddress; 65 - DATABASE_URL = "user=${dbUser} host=/run/postgresql dbname=${dbName}"; 62 + DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux"; 66 63 RUN_MIGRATIONS = "1"; 67 64 CREATE_ADMIN = "1"; 68 65 }; ··· 70 67 services.postgresql = { 71 68 enable = true; 72 69 ensureUsers = [ { 73 - name = dbUser; 74 - ensurePermissions = { 75 - "DATABASE ${dbName}" = "ALL PRIVILEGES"; 76 - }; 70 + name = "miniflux"; 71 + ensureDBOwnership = true; 77 72 } ]; 78 - ensureDatabases = [ dbName ]; 73 + ensureDatabases = [ "miniflux" ]; 79 74 }; 80 75 81 76 systemd.services.miniflux-dbsetup = { ··· 97 92 98 93 serviceConfig = { 99 94 ExecStart = "${cfg.package}/bin/miniflux"; 100 - User = dbUser; 95 + User = "miniflux"; 101 96 DynamicUser = true; 102 97 RuntimeDirectory = "miniflux"; 103 98 RuntimeDirectoryMode = "0700";
+7 -3
nixos/modules/services/web-apps/mobilizon.nix
··· 212 212 assertion = cfg.nginx.enable -> (cfg.settings.":mobilizon"."Mobilizon.Web.Endpoint".http.ip == settingsFormat.lib.mkTuple [ 0 0 0 0 0 0 0 1 ]); 213 213 message = "Setting the IP mobilizon listens on is only possible when the nginx config is not used, as it is hardcoded there."; 214 214 } 215 + { 216 + assertion = isLocalPostgres -> repoSettings.database == repoSettings.username; 217 + message = '' 218 + When creating a database via NixOS, the db user and db name must be equal! 219 + ''; 220 + } 215 221 ]; 216 222 217 223 services.mobilizon.settings = { ··· 372 378 ensureUsers = [ 373 379 { 374 380 name = dbUser; 375 - ensurePermissions = { 376 - "DATABASE \"${repoSettings.database}\"" = "ALL PRIVILEGES"; 377 - }; 381 + ensureDBOwnership = true; 378 382 } 379 383 ]; 380 384 extraPlugins = with postgresql.pkgs; [ postgis ];
+2 -2
nixos/modules/services/web-apps/moodle.nix
··· 194 194 config = mkIf cfg.enable { 195 195 196 196 assertions = [ 197 - { assertion = cfg.database.createLocally -> cfg.database.user == user; 197 + { assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name; 198 198 message = "services.moodle.database.user must be set to ${user} if services.moodle.database.createLocally is set true"; 199 199 } 200 200 { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; ··· 220 220 ensureDatabases = [ cfg.database.name ]; 221 221 ensureUsers = [ 222 222 { name = cfg.database.user; 223 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 223 + ensureDBOwnership = true; 224 224 } 225 225 ]; 226 226 };
+1 -3
nixos/modules/services/web-apps/netbox.nix
··· 257 257 ensureUsers = [ 258 258 { 259 259 name = "netbox"; 260 - ensurePermissions = { 261 - "DATABASE netbox" = "ALL PRIVILEGES"; 262 - }; 260 + ensureDBOwnership = true; 263 261 } 264 262 ]; 265 263 };
+1 -1
nixos/modules/services/web-apps/nextcloud.nix
··· 1042 1042 ensureDatabases = [ cfg.config.dbname ]; 1043 1043 ensureUsers = [{ 1044 1044 name = cfg.config.dbuser; 1045 - ensurePermissions = { "DATABASE ${cfg.config.dbname}" = "ALL PRIVILEGES"; }; 1045 + ensureDBOwnership = true; 1046 1046 }]; 1047 1047 }; 1048 1048
+1 -1
nixos/modules/services/web-apps/onlyoffice.nix
··· 198 198 ensureDatabases = [ "onlyoffice" ]; 199 199 ensureUsers = [{ 200 200 name = "onlyoffice"; 201 - ensurePermissions = { "DATABASE \"onlyoffice\"" = "ALL PRIVILEGES"; }; 201 + ensureDBOwnership = true; 202 202 }]; 203 203 }; 204 204 };
+1 -1
nixos/modules/services/web-apps/outline.nix
··· 581 581 enable = true; 582 582 ensureUsers = [{ 583 583 name = "outline"; 584 - ensurePermissions."DATABASE outline" = "ALL PRIVILEGES"; 584 + ensureDBOwnership = true; 585 585 }]; 586 586 ensureDatabases = [ "outline" ]; 587 587 };
+1 -3
nixos/modules/services/web-apps/peering-manager.nix
··· 186 186 ensureUsers = [ 187 187 { 188 188 name = "peering-manager"; 189 - ensurePermissions = { 190 - "DATABASE \"peering-manager\"" = "ALL PRIVILEGES"; 191 - }; 189 + ensureDBOwnership = true; 192 190 } 193 191 ]; 194 192 };
-1
nixos/modules/services/web-apps/pixelfed.nix
··· 271 271 ensureDatabases = [ cfg.database.name ]; 272 272 ensureUsers = [{ 273 273 name = user; 274 - ensurePermissions = { }; 275 274 }]; 276 275 }; 277 276
+11 -2
nixos/modules/services/web-apps/tt-rss.nix
··· 529 529 assertion = cfg.database.password != null -> cfg.database.passwordFile == null; 530 530 message = "Cannot set both password and passwordFile"; 531 531 } 532 + { 533 + assertion = cfg.database.createLocally -> cfg.database.name == cfg.user && cfg.database.user == cfg.user; 534 + message = '' 535 + When creating a database via NixOS, the db user and db name must be equal! 536 + If you already have an existing DB+user and this assertion is new, you can safely set 537 + `services.tt-rss.database.createLocally` to `false` because removal of `ensureUsers` 538 + and `ensureDatabases` doesn't have any effect. 539 + ''; 540 + } 532 541 ]; 533 542 534 543 services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") { ··· 632 641 enable = mkDefault true; 633 642 ensureDatabases = [ cfg.database.name ]; 634 643 ensureUsers = [ 635 - { name = cfg.user; 636 - ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 644 + { name = cfg.database.user; 645 + ensureDBOwnership = true; 637 646 } 638 647 ]; 639 648 };
+1 -1
nixos/modules/services/web-servers/hydron.nix
··· 93 93 ensureDatabases = [ "hydron" ]; 94 94 ensureUsers = [ 95 95 { name = "hydron"; 96 - ensurePermissions = { "DATABASE hydron" = "ALL PRIVILEGES"; }; 96 + ensureDBOwnership = true; 97 97 } 98 98 ]; 99 99 };
+1 -1
nixos/tests/dex-oidc.nix
··· 49 49 ensureUsers = [ 50 50 { 51 51 name = "dex"; 52 - ensurePermissions = { "DATABASE dex" = "ALL PRIVILEGES"; }; 52 + ensureDBOwnership = true; 53 53 } 54 54 ]; 55 55 };
+1 -1
nixos/tests/ferretdb.nix
··· 39 39 ensureDatabases = [ "ferretdb" ]; 40 40 ensureUsers = [{ 41 41 name = "ferretdb"; 42 - ensurePermissions."DATABASE ferretdb" = "ALL PRIVILEGES"; 42 + ensureDBOwnership = true; 43 43 }]; 44 44 }; 45 45
+1 -3
nixos/tests/freshrss-pgsql.nix
··· 22 22 ensureUsers = [ 23 23 { 24 24 name = "freshrss"; 25 - ensurePermissions = { 26 - "DATABASE freshrss" = "ALL PRIVILEGES"; 27 - }; 25 + ensureDBOwnership = true; 28 26 } 29 27 ]; 30 28 initialScript = pkgs.writeText "postgresql-password" ''
+1 -1
nixos/tests/grafana/basic.nix
··· 55 55 ensureDatabases = [ "grafana" ]; 56 56 ensureUsers = [{ 57 57 name = "grafana"; 58 - ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES"; 58 + ensureDBOwnership = true; 59 59 }]; 60 60 }; 61 61 systemd.services.grafana.after = [ "postgresql.service" ];
+1 -1
nixos/tests/hockeypuck.nix
··· 35 35 ensureDatabases = [ "hockeypuck" ]; 36 36 ensureUsers = [{ 37 37 name = "hockeypuck"; 38 - ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES"; 38 + ensureDBOwnership = true; 39 39 }]; 40 40 }; 41 41 };
+5 -7
nixos/tests/home-assistant.nix
··· 9 9 nodes.hass = { pkgs, ... }: { 10 10 services.postgresql = { 11 11 enable = true; 12 - 13 - # FIXME: hack for https://github.com/NixOS/nixpkgs/issues/216989 14 - # Should be replaced with ensureUsers again when a solution for that is found 15 - initialScript = pkgs.writeText "hass-setup-db.sql" '' 16 - CREATE ROLE hass WITH LOGIN; 17 - CREATE DATABASE hass WITH OWNER hass; 18 - ''; 12 + ensureDatabases = [ "hass" ]; 13 + ensureUsers = [{ 14 + name = "hass"; 15 + ensureDBOwnership = true; 16 + }]; 19 17 }; 20 18 21 19 services.home-assistant = {
+1 -1
nixos/tests/paperless.nix
··· 17 17 ensureDatabases = [ "paperless" ]; 18 18 ensureUsers = [ 19 19 { name = config.services.paperless.user; 20 - ensurePermissions = { "DATABASE \"paperless\"" = "ALL PRIVILEGES"; }; 20 + ensureDBOwnership = true; 21 21 } 22 22 ]; 23 23 };
-8
nixos/tests/pgadmin4.nix
··· 19 19 authentication = '' 20 20 host all all localhost trust 21 21 ''; 22 - ensureUsers = [ 23 - { 24 - name = "postgres"; 25 - ensurePermissions = { 26 - "DATABASE \"postgres\"" = "ALL PRIVILEGES"; 27 - }; 28 - } 29 - ]; 30 22 }; 31 23 32 24 services.pgadmin = {
+5 -7
nixos/tests/pgbouncer.nix
··· 24 24 services = { 25 25 postgresql = { 26 26 enable = true; 27 - ensureDatabases = [ "testdb" ]; 27 + ensureDatabases = [ "test" ]; 28 28 ensureUsers = [ 29 29 { 30 - name = "testuser"; 31 - ensurePermissions = { 32 - "DATABASE testdb" = "ALL PRIVILEGES"; 33 - }; 30 + name = "test"; 31 + ensureDBOwnership = true; 34 32 }]; 35 33 authentication = '' 36 34 local testdb testuser scram-sha-256 ··· 40 38 pgbouncer = { 41 39 enable = true; 42 40 listenAddress = "localhost"; 43 - databases = { testdb = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; }; 41 + databases = { test = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=test"; }; 44 42 authType = "scram-sha-256"; 45 43 authFile = testAuthFile; 46 44 }; ··· 55 53 56 54 # Test if we can make a query through PgBouncer 57 55 one.wait_until_succeeds( 58 - "psql 'postgres://testuser:testpass@localhost:6432/testdb' -c 'SELECT 1;'" 56 + "psql 'postgres://testuser:testpass@localhost:6432/test' -c 'SELECT 1;'" 59 57 ) 60 58 ''; 61 59 })
+1 -3
nixos/tests/powerdns-admin.nix
··· 87 87 ensureUsers = [ 88 88 { 89 89 name = "powerdnsadmin"; 90 - ensurePermissions = { 91 - "DATABASE powerdnsadmin" = "ALL PRIVILEGES"; 92 - }; 90 + ensureDBOwnership = true; 93 91 } 94 92 ]; 95 93 };
+1 -1
nixos/tests/sftpgo.nix
··· 156 156 ensureDatabases = [ "sftpgo" ]; 157 157 ensureUsers = [{ 158 158 name = "sftpgo"; 159 - ensurePermissions."DATABASE sftpgo" = "ALL PRIVILEGES"; 159 + ensureDBOwnership = true; 160 160 }]; 161 161 }; 162 162
+23
nixos/tests/tandoor-recipes.nix
··· 5 5 nodes.machine = { pkgs, ... }: { 6 6 services.tandoor-recipes = { 7 7 enable = true; 8 + extraConfig = { 9 + DB_ENGINE = "django.db.backends.postgresql"; 10 + POSTGRES_HOST = "/run/postgresql"; 11 + POSTGRES_USER = "tandoor_recipes"; 12 + POSTGRES_DB = "tandoor_recipes"; 13 + }; 14 + }; 15 + 16 + services.postgresql = { 17 + enable = true; 18 + ensureDatabases = [ "tandoor_recipes" ]; 19 + ensureUsers = [ 20 + { 21 + name = "tandoor_recipes"; 22 + ensureDBOwnership = true; 23 + } 24 + ]; 25 + }; 26 + 27 + systemd.services = { 28 + tandoor-recipes = { 29 + after = [ "postgresql.service" ]; 30 + }; 8 31 }; 9 32 }; 10 33
+1 -1
nixos/tests/vikunja.nix
··· 33 33 ensureDatabases = [ "vikunja-api" ]; 34 34 ensureUsers = [ 35 35 { name = "vikunja-api"; 36 - ensurePermissions = { "DATABASE \"vikunja-api\"" = "ALL PRIVILEGES"; }; 36 + ensureDBOwnership = true; 37 37 } 38 38 ]; 39 39 };
+3 -2
nixos/tests/wiki-js.nix
··· 10 10 enable = true; 11 11 settings.db.host = "/run/postgresql"; 12 12 settings.db.user = "wiki-js"; 13 + settings.db.db = "wiki-js"; 13 14 settings.logLevel = "debug"; 14 15 }; 15 16 services.postgresql = { 16 17 enable = true; 17 - ensureDatabases = [ "wiki" ]; 18 + ensureDatabases = [ "wiki-js" ]; 18 19 ensureUsers = [ 19 20 { name = "wiki-js"; 20 - ensurePermissions."DATABASE wiki" = "ALL PRIVILEGES"; 21 + ensureDBOwnership = true; 21 22 } 22 23 ]; 23 24 };