Merge pull request #266270 from Ma27/postgresql-ownership-15

+3
nixos/doc/manual/release-notes/rl-2311.section.md
···
## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
- `network-online.target` has been fixed to no longer time out for systems with `networking.useDHCP = true` and `networking.useNetworkd = true`.
Workarounds for this can be removed.
···
## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
+
- `services.postgresql.ensurePermissions` has been deprecated in favor of `services.postgresql.ensureUsers.*.ensureDBOwnership` which simplifies the setup of database owned by a certain system user
+
in local database contexts (which make use of peer authentication via UNIX sockets), migration guidelines were provided in the NixOS manual, please refer to them if you are affected by a PostgreSQL 15 changing the way `GRANT ALL PRIVILEGES` is working. `services.postgresql.ensurePermissions` will be removed in 24.05. All NixOS modules were migrated using one of the strategy, e.g. `ensureDBOwnership` or `postStart`. More about this situation can be learnt in https://github.com/NixOS/nixpkgs/pull/266270.
+
- `network-online.target` has been fixed to no longer time out for systems with `networking.useDHCP = true` and `networking.useNetworkd = true`.
Workarounds for this can be removed.
+47 -11
nixos/modules/services/databases/postgresql.nix
···
ensurePermissions = mkOption {
type = types.attrsOf types.str;
default = {};
description = lib.mdDoc ''
Permissions to ensure for the user, specified as an attribute set.
The attribute names specify the database and tables to grant the permissions for.
The attribute values specify the permissions to grant. You may specify one or
···
"DATABASE \"nextcloud\"" = "ALL PRIVILEGES";
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
}
'';
};
···
});
default = [];
description = lib.mdDoc ''
-
Ensures that the specified users exist and have at least the ensured permissions.
The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the
same name only, and that without the need for a password.
-
This option will never delete existing users or remove permissions, especially not when the value of this
-
option is changed. This means that users created and permissions assigned once through this option or
-
otherwise have to be removed manually.
'';
example = literalExpression ''
[
{
name = "nextcloud";
-
ensurePermissions = {
-
"DATABASE nextcloud" = "ALL PRIVILEGES";
-
};
}
{
name = "superuser";
-
ensurePermissions = {
-
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
-
};
}
]
'';
···
config = mkIf cfg.enable {
services.postgresql.settings =
{
hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}";
···
${
concatMapStrings
(user:
-
let
userPermissions = concatStringsSep "\n"
(mapAttrsToList
(database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '')
user.ensurePermissions
);
filteredClauses = filterAttrs (name: value: value != null) user.ensureClauses;
···
$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
${userPermissions}
${userClauses}
''
)
cfg.ensureUsers
···
ensurePermissions = mkOption {
type = types.attrsOf types.str;
default = {};
+
visible = false; # This option has been deprecated.
description = lib.mdDoc ''
+
This option is DEPRECATED and should not be used in nixpkgs anymore,
+
use `ensureDBOwnership` instead. It can also break with newer
+
versions of PostgreSQL (≥ 15).
+
Permissions to ensure for the user, specified as an attribute set.
The attribute names specify the database and tables to grant the permissions for.
The attribute values specify the permissions to grant. You may specify one or
···
"DATABASE \"nextcloud\"" = "ALL PRIVILEGES";
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
}
+
'';
+
};
+
+
ensureDBOwnership = mkOption {
+
type = types.bool;
+
default = false;
+
description = mdDoc ''
+
Grants the user ownership to a database with the same name.
+
This database must be defined manually in
+
[](#opt-services.postgresql.ensureDatabases).
'';
};
···
});
default = [];
description = lib.mdDoc ''
+
Ensures that the specified users exist.
The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the
same name only, and that without the need for a password.
+
This option will never delete existing users or remove DB ownership of databases
+
once granted with `ensureDBOwnership = true;`. This means that this must be
+
cleaned up manually when changing after changing the config in here.
'';
example = literalExpression ''
[
{
name = "nextcloud";
}
{
name = "superuser";
+
ensureDBOwnership = true;
}
]
'';
···
config = mkIf cfg.enable {
+
assertions = map ({ name, ensureDBOwnership, ... }: {
+
assertion = ensureDBOwnership -> builtins.elem name cfg.ensureDatabases;
+
message = ''
+
For each database user defined with `services.postgresql.ensureUsers` and
+
`ensureDBOwnership = true;`, a database with the same name must be defined
+
in `services.postgresql.ensureDatabases`.
+
+
Offender: ${name} has not been found among databases.
+
'';
+
}) cfg.ensureUsers;
+
# `ensurePermissions` is now deprecated, let's avoid it.
+
warnings = lib.optional (any ({ ensurePermissions, ... }: ensurePermissions != {}) cfg.ensureUsers) "
+
`services.postgresql.*.ensurePermissions` is used in your expressions,
+
this option is known to be broken with newer PostgreSQL versions,
+
consider migrating to `services.postgresql.*.ensureDBOwnership` or
+
consult the release notes or manual for more migration guidelines.
+
+
This option will be removed in NixOS 24.05 unless it sees significant
+
maintenance improvements.
+
";
+
services.postgresql.settings =
{
hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}";
···
${
concatMapStrings
(user:
+
let
userPermissions = concatStringsSep "\n"
(mapAttrsToList
(database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '')
user.ensurePermissions
);
+
dbOwnershipStmt = optionalString
+
user.ensureDBOwnership
+
''$PSQL -tAc 'ALTER DATABASE "${user.name}" OWNER TO "${user.name}";' '';
filteredClauses = filterAttrs (name: value: value != null) user.ensureClauses;
···
$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
${userPermissions}
${userClauses}
+
+
${dbOwnershipStmt}
''
)
cfg.ensureUsers
+2 -2
nixos/modules/services/development/zammad.nix
···
assertions = [
{
-
assertion = cfg.database.createLocally -> cfg.database.user == "zammad";
message = "services.zammad.database.user must be set to \"zammad\" if services.zammad.database.createLocally is set to true";
}
{
···
ensureUsers = [
{
name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
assertions = [
{
+
assertion = cfg.database.createLocally -> cfg.database.user == "zammad" && cfg.database.name == "zammad";
message = "services.zammad.database.user must be set to \"zammad\" if services.zammad.database.createLocally is set to true";
}
{
···
ensureUsers = [
{
name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/modules/services/finance/odoo.nix
···
ensureDatabases = [ "odoo" ];
ensureUsers = [{
name = "odoo";
-
ensurePermissions = { "DATABASE odoo" = "ALL PRIVILEGES"; };
}];
};
});
···
ensureDatabases = [ "odoo" ];
ensureUsers = [{
name = "odoo";
+
ensureDBOwnership = true;
}];
};
});
+1 -1
nixos/modules/services/mail/listmonk.nix
···
ensureUsers = [{
name = "listmonk";
-
ensurePermissions = { "DATABASE listmonk" = "ALL PRIVILEGES"; };
}];
ensureDatabases = [ "listmonk" ];
···
ensureUsers = [{
name = "listmonk";
+
ensureDBOwnership = true;
}];
ensureDatabases = [ "listmonk" ];
+11 -3
nixos/modules/services/mail/roundcube.nix
···
};
};
services.postgresql = mkIf localDB {
enable = true;
ensureDatabases = [ cfg.database.dbname ];
ensureUsers = [ {
name = cfg.database.username;
-
ensurePermissions = {
-
"DATABASE ${cfg.database.username}" = "ALL PRIVILEGES";
-
};
} ];
};
···
};
};
+
assertions = [
+
{
+
assertion = localDB -> cfg.database.username == cfg.database.dbname;
+
message = ''
+
When setting up a DB and its owner user, the owner and the DB name must be
+
equal!
+
'';
+
}
+
];
+
services.postgresql = mkIf localDB {
enable = true;
ensureDatabases = [ cfg.database.dbname ];
ensureUsers = [ {
name = cfg.database.username;
+
ensureDBOwnership = true;
} ];
};
+4 -6
nixos/modules/services/mail/sympa.nix
···
default = null;
example = "/run/keys/sympa-dbpassword";
description = lib.mdDoc ''
-
A file containing the password for {option}`services.sympa.database.user`.
'';
};
···
db_type = cfg.database.type;
db_name = cfg.database.name;
}
// (optionalAttrs (cfg.database.host != null) {
db_host = cfg.database.host;
···
})
// (optionalAttrs (cfg.database.port != null) {
db_port = cfg.database.port;
-
})
-
// (optionalAttrs (cfg.database.user != null) {
-
db_user = cfg.database.user;
})
// (optionalAttrs (cfg.mta.type == "postfix") {
sendmail_aliases = "${dataDir}/sympa_transport";
···
users.groups.${group} = {};
assertions = [
-
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.sympa.database.user must be set to ${user} if services.sympa.database.createLocally is set to true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
default = null;
example = "/run/keys/sympa-dbpassword";
description = lib.mdDoc ''
+
A file containing the password for {option}`services.sympa.database.name`.
'';
};
···
db_type = cfg.database.type;
db_name = cfg.database.name;
+
db_user = cfg.database.name;
}
// (optionalAttrs (cfg.database.host != null) {
db_host = cfg.database.host;
···
})
// (optionalAttrs (cfg.database.port != null) {
db_port = cfg.database.port;
})
// (optionalAttrs (cfg.mta.type == "postfix") {
sendmail_aliases = "${dataDir}/sympa_transport";
···
users.groups.${group} = {};
assertions = [
+
{ assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
message = "services.sympa.database.user must be set to ${user} if services.sympa.database.createLocally is set to true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+2 -2
nixos/modules/services/matrix/matrix-sliding-sync.nix
···
services.postgresql = lib.optionalAttrs cfg.createDatabase {
enable = true;
ensureDatabases = [ "matrix-sliding-sync" ];
-
ensureUsers = [ rec {
name = "matrix-sliding-sync";
-
ensurePermissions."DATABASE \"${name}\"" = "ALL PRIVILEGES";
} ];
};
···
services.postgresql = lib.optionalAttrs cfg.createDatabase {
enable = true;
ensureDatabases = [ "matrix-sliding-sync" ];
+
ensureUsers = [ {
name = "matrix-sliding-sync";
+
ensureDBOwnership = true;
} ];
};
+1 -3
nixos/modules/services/matrix/mautrix-facebook.nix
···
ensureDatabases = ["mautrix-facebook"];
ensureUsers = [{
name = "mautrix-facebook";
-
ensurePermissions = {
-
"DATABASE \"mautrix-facebook\"" = "ALL PRIVILEGES";
-
};
}];
};
···
ensureDatabases = ["mautrix-facebook"];
ensureUsers = [{
name = "mautrix-facebook";
+
ensureDBOwnership = true;
}];
};
+1 -3
nixos/modules/services/misc/atuin.nix
···
enable = true;
ensureUsers = [{
name = "atuin";
-
ensurePermissions = {
-
"DATABASE atuin" = "ALL PRIVILEGES";
-
};
}];
ensureDatabases = [ "atuin" ];
};
···
enable = true;
ensureUsers = [{
name = "atuin";
+
ensureDBOwnership = true;
}];
ensureDatabases = [ "atuin" ];
};
+9 -1
nixos/modules/services/misc/forgejo.nix
···
assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
message = "services.forgejo.database.user must match services.forgejo.user if the database is to be automatically provisioned";
}
];
services.forgejo.settings = {
···
ensureUsers = [
{
name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
message = "services.forgejo.database.user must match services.forgejo.user if the database is to be automatically provisioned";
}
+
{ assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
+
message = ''
+
When creating a database via NixOS, the db user and db name must be equal!
+
If you already have an existing DB+user and this assertion is new, you can safely set
+
`services.forgejo.createDatabase` to `false` because removal of `ensureUsers`
+
and `ensureDatabases` doesn't have any effect.
+
'';
+
}
];
services.forgejo.settings = {
···
ensureUsers = [
{
name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+9 -1
nixos/modules/services/misc/gitea.nix
···
{ assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned";
}
];
services.gitea.settings = {
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
{ assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned";
}
+
{ assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
+
message = ''
+
When creating a database via NixOS, the db user and db name must be equal!
+
If you already have an existing DB+user and this assertion is new, you can safely set
+
`services.gitea.createDatabase` to `false` because removal of `ensureUsers`
+
and `ensureDatabases` doesn't have any effect.
+
'';
+
}
];
services.gitea.settings = {
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+2 -2
nixos/modules/services/misc/redmine.nix
···
{ assertion = cfg.database.passwordFile != null || cfg.database.socket != null;
message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set";
}
-
{ assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
{ assertion = cfg.database.passwordFile != null || cfg.database.socket != null;
message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set";
}
+
{ assertion = cfg.database.createLocally -> cfg.database.user == cfg.user && cfg.database.user == cfg.database.name;
message = "services.redmine.database.user must be set to ${cfg.user} if services.redmine.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+7 -3
nixos/modules/services/misc/sourcehut/service.nix
···
ensureDatabases = [ srvCfg.postgresql.database ];
ensureUsers = map (name: {
inherit name;
-
ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
}) [srvCfg.user];
};
services.sourcehut.settings = mkMerge [
{
···
extraService
])) extraServices)
-
# Work around 'pq: permission denied for schema public' with postgres v15, until a
-
# solution for `services.postgresql.ensureUsers` is found.
# See https://github.com/NixOS/nixpkgs/issues/216989
# Workaround taken from nixos/forgejo: https://github.com/NixOS/nixpkgs/pull/262741
(lib.mkIf (
cfg.postgresql.enable
&& lib.strings.versionAtLeast config.services.postgresql.package.version "15.0"
···
ensureDatabases = [ srvCfg.postgresql.database ];
ensureUsers = map (name: {
inherit name;
+
# We don't use it because we have a special default database name with dots.
+
# TODO(for maintainers of sourcehut): migrate away from custom preStart script.
+
ensureDBOwnership = false;
}) [srvCfg.user];
};
+
services.sourcehut.settings = mkMerge [
{
···
extraService
])) extraServices)
+
# Work around 'pq: permission denied for schema public' with postgres v15.
# See https://github.com/NixOS/nixpkgs/issues/216989
# Workaround taken from nixos/forgejo: https://github.com/NixOS/nixpkgs/pull/262741
+
# TODO(to maintainers of sourcehut): please migrate away from this workaround
+
# by migrating away from database name defaults with dots.
(lib.mkIf (
cfg.postgresql.enable
&& lib.strings.versionAtLeast config.services.postgresql.package.version "15.0"
+2 -2
nixos/modules/services/monitoring/zabbix-proxy.nix
···
{ assertion = !config.services.zabbixServer.enable;
message = "Please choose one of services.zabbixServer or services.zabbixProxy.";
}
-
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
{ assertion = !config.services.zabbixServer.enable;
message = "Please choose one of services.zabbixServer or services.zabbixProxy.";
}
+
{ assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+2 -2
nixos/modules/services/monitoring/zabbix-server.nix
···
config = mkIf cfg.enable {
assertions = [
-
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
config = mkIf cfg.enable {
assertions = [
+
{ assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name;
message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/modules/services/security/hockeypuck.nix
···
ensureDatabases = [ "hockeypuck" ];
ensureUsers = [{
name = "hockeypuck";
-
ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES";
}];
};
```
···
ensureDatabases = [ "hockeypuck" ];
ensureUsers = [{
name = "hockeypuck";
+
ensureDBOwnership = true;
}];
};
```
+4 -6
nixos/modules/services/web-apps/coder.nix
···
config = mkIf cfg.enable {
assertions = [
-
{ assertion = cfg.database.createLocally -> cfg.database.username == name;
-
message = "services.coder.database.username must be set to ${user} if services.coder.database.createLocally is set true";
}
];
···
cfg.database.database
];
ensureUsers = [{
-
name = cfg.database.username;
-
ensurePermissions = {
-
"DATABASE \"${cfg.database.database}\"" = "ALL PRIVILEGES";
-
};
}
];
};
···
config = mkIf cfg.enable {
assertions = [
+
{ assertion = cfg.database.createLocally -> cfg.database.username == name && cfg.database.database == cfg.database.username;
+
message = "services.coder.database.username must be set to ${name} if services.coder.database.createLocally is set true";
}
];
···
cfg.database.database
];
ensureUsers = [{
+
name = cfg.user;
+
ensureDBOwnership = true;
}
];
};
+1 -3
nixos/modules/services/web-apps/gotosocial.nix
···
ensureUsers = [
{
name = "gotosocial";
-
ensurePermissions = {
-
"DATABASE gotosocial" = "ALL PRIVILEGES";
-
};
}
];
};
···
ensureUsers = [
{
name = "gotosocial";
+
ensureDBOwnership = true;
}
];
};
+9 -6
nixos/modules/services/web-apps/invidious.nix
···
# Default to using the local database if we create it
services.invidious.database.host = lib.mkDefault null;
services.postgresql = {
enable = true;
ensureDatabases = lib.singleton cfg.settings.db.dbname;
-
ensureUsers = lib.singleton {
-
name = cfg.settings.db.user;
-
ensurePermissions = {
-
"DATABASE ${cfg.settings.db.dbname}" = "ALL PRIVILEGES";
-
};
-
};
# This is only needed because the unix user invidious isn't the same as
# the database user. This tells postgres to map one to the other.
identMap = ''
···
documentation = [ "https://docs.invidious.io/Database-Information-and-Maintenance.md" ];
startAt = lib.mkDefault "weekly";
path = [ config.services.postgresql.package ];
script = ''
psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "DELETE FROM nonces * WHERE expire < current_timestamp"
psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "TRUNCATE TABLE videos"
···
# Default to using the local database if we create it
services.invidious.database.host = lib.mkDefault null;
+
+
# TODO(raitobezarius to maintainers of invidious): I strongly advise to clean up the kemal specific
+
# thing for 24.05 and use `ensureDBOwnership`.
+
# See https://github.com/NixOS/nixpkgs/issues/216989
+
systemd.services.postgresql.postStart = lib.mkAfter ''
+
$PSQL -tAc 'ALTER DATABASE "${cfg.settings.db.dbname}" OWNER TO "${cfg.settings.db.user}";'
+
'';
services.postgresql = {
enable = true;
+
ensureUsers = lib.singleton { name = cfg.settings.db.user; ensureDBOwnership = false; };
ensureDatabases = lib.singleton cfg.settings.db.dbname;
# This is only needed because the unix user invidious isn't the same as
# the database user. This tells postgres to map one to the other.
identMap = ''
···
documentation = [ "https://docs.invidious.io/Database-Information-and-Maintenance.md" ];
startAt = lib.mkDefault "weekly";
path = [ config.services.postgresql.package ];
+
after = [ "postgresql.service" ];
script = ''
psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "DELETE FROM nonces * WHERE expire < current_timestamp"
psql ${cfg.settings.db.dbname} ${cfg.settings.db.user} -c "TRUNCATE TABLE videos"
+1 -1
nixos/modules/services/web-apps/lemmy.nix
···
ensureDatabases = [ cfg.settings.database.database ];
ensureUsers = [{
name = cfg.settings.database.user;
-
ensurePermissions."DATABASE ${cfg.settings.database.database}" = "ALL PRIVILEGES";
}];
};
···
ensureDatabases = [ cfg.settings.database.database ];
ensureUsers = [{
name = cfg.settings.database.user;
+
ensureDBOwnership = true;
}];
};
+3 -3
nixos/modules/services/web-apps/mastodon.nix
···
config = lib.mkIf cfg.enable (lib.mkMerge [{
assertions = [
{
-
assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user);
message = ''
For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer
authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user
···
enable = true;
ensureUsers = [
{
-
name = cfg.database.user;
-
ensurePermissions."DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";
}
];
ensureDatabases = [ cfg.database.name ];
···
config = lib.mkIf cfg.enable (lib.mkMerge [{
assertions = [
{
+
assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user && cfg.database.user == cfg.database.name);
message = ''
For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer
authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user
···
enable = true;
ensureUsers = [
{
+
name = cfg.database.name;
+
ensureDBOwnership = true;
}
];
ensureDatabases = [ cfg.database.name ];
+2 -2
nixos/modules/services/web-apps/mediawiki.nix
···
{ assertion = cfg.database.createLocally -> (cfg.database.type == "mysql" || cfg.database.type == "postgres");
message = "services.mediawiki.createLocally is currently only supported for database type 'mysql' and 'postgres'";
}
-
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [{
name = cfg.database.user;
-
ensurePermissions = { "DATABASE \"${cfg.database.name}\"" = "ALL PRIVILEGES"; };
}];
};
···
{ assertion = cfg.database.createLocally -> (cfg.database.type == "mysql" || cfg.database.type == "postgres");
message = "services.mediawiki.createLocally is currently only supported for database type 'mysql' and 'postgres'";
}
+
{ assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.name == cfg.database.user;
message = "services.mediawiki.database.user must be set to ${user} if services.mediawiki.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.socket != null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [{
name = cfg.database.user;
+
ensureDBOwnership = true;
}];
};
+6 -11
nixos/modules/services/web-apps/miniflux.nix
···
defaultAddress = "localhost:8080";
-
dbUser = "miniflux";
-
dbName = "miniflux";
-
pgbin = "${config.services.postgresql.package}/bin";
preStart = pkgs.writeScript "miniflux-pre-start" ''
#!${pkgs.runtimeShell}
-
${pgbin}/psql "${dbName}" -c "CREATE EXTENSION IF NOT EXISTS hstore"
'';
in
···
services.miniflux.config = {
LISTEN_ADDR = mkDefault defaultAddress;
-
DATABASE_URL = "user=${dbUser} host=/run/postgresql dbname=${dbName}";
RUN_MIGRATIONS = "1";
CREATE_ADMIN = "1";
};
···
services.postgresql = {
enable = true;
ensureUsers = [ {
-
name = dbUser;
-
ensurePermissions = {
-
"DATABASE ${dbName}" = "ALL PRIVILEGES";
-
};
} ];
-
ensureDatabases = [ dbName ];
};
systemd.services.miniflux-dbsetup = {
···
serviceConfig = {
ExecStart = "${cfg.package}/bin/miniflux";
-
User = dbUser;
DynamicUser = true;
RuntimeDirectory = "miniflux";
RuntimeDirectoryMode = "0700";
···
defaultAddress = "localhost:8080";
pgbin = "${config.services.postgresql.package}/bin";
preStart = pkgs.writeScript "miniflux-pre-start" ''
#!${pkgs.runtimeShell}
+
${pgbin}/psql "miniflux" -c "CREATE EXTENSION IF NOT EXISTS hstore"
'';
in
···
services.miniflux.config = {
LISTEN_ADDR = mkDefault defaultAddress;
+
DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux";
RUN_MIGRATIONS = "1";
CREATE_ADMIN = "1";
};
···
services.postgresql = {
enable = true;
ensureUsers = [ {
+
name = "miniflux";
+
ensureDBOwnership = true;
} ];
+
ensureDatabases = [ "miniflux" ];
};
systemd.services.miniflux-dbsetup = {
···
serviceConfig = {
ExecStart = "${cfg.package}/bin/miniflux";
+
User = "miniflux";
DynamicUser = true;
RuntimeDirectory = "miniflux";
RuntimeDirectoryMode = "0700";
+10 -3
nixos/modules/services/web-apps/mobilizon.nix
···
# Taken from here:
# https://framagit.org/framasoft/mobilizon/-/blob/1.1.0/priv/templates/setup_db.eex
script =
''
psql "${repoSettings.database}" -c "\
CREATE EXTENSION IF NOT EXISTS postgis; \
CREATE EXTENSION IF NOT EXISTS pg_trgm; \
CREATE EXTENSION IF NOT EXISTS unaccent;"
'';
serviceConfig = {
···
ensureUsers = [
{
name = dbUser;
-
ensurePermissions = {
-
"DATABASE \"${repoSettings.database}\"" = "ALL PRIVILEGES";
-
};
}
];
extraPlugins = with postgresql.pkgs; [ postgis ];
···
# Taken from here:
# https://framagit.org/framasoft/mobilizon/-/blob/1.1.0/priv/templates/setup_db.eex
+
# TODO(to maintainers of mobilizon): the owner database alteration is necessary
+
# as PostgreSQL 15 changed their behaviors w.r.t. to privileges.
+
# See https://github.com/NixOS/nixpkgs/issues/216989 to get rid
+
# of that workaround.
script =
''
psql "${repoSettings.database}" -c "\
CREATE EXTENSION IF NOT EXISTS postgis; \
CREATE EXTENSION IF NOT EXISTS pg_trgm; \
CREATE EXTENSION IF NOT EXISTS unaccent;"
+
psql -tAc 'ALTER DATABASE "${repoSettings.database}" OWNER TO "${dbUser}";'
+
'';
serviceConfig = {
···
ensureUsers = [
{
name = dbUser;
+
# Given that `dbUser` is potentially arbitrarily custom, we will perform
+
# manual fixups in mobilizon-postgres.
+
# TODO(to maintainers of mobilizon): Feel free to simplify your setup by using `ensureDBOwnership`.
+
ensureDBOwnership = false;
}
];
extraPlugins = with postgresql.pkgs; [ postgis ];
+2 -2
nixos/modules/services/web-apps/moodle.nix
···
config = mkIf cfg.enable {
assertions = [
-
{ assertion = cfg.database.createLocally -> cfg.database.user == user;
message = "services.moodle.database.user must be set to ${user} if services.moodle.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
config = mkIf cfg.enable {
assertions = [
+
{ assertion = cfg.database.createLocally -> cfg.database.user == user && cfg.database.user == cfg.database.name;
message = "services.moodle.database.user must be set to ${user} if services.moodle.database.createLocally is set true";
}
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+1 -3
nixos/modules/services/web-apps/netbox.nix
···
ensureUsers = [
{
name = "netbox";
-
ensurePermissions = {
-
"DATABASE netbox" = "ALL PRIVILEGES";
-
};
}
];
};
···
ensureUsers = [
{
name = "netbox";
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/modules/services/web-apps/nextcloud.nix
···
ensureDatabases = [ cfg.config.dbname ];
ensureUsers = [{
name = cfg.config.dbuser;
-
ensurePermissions = { "DATABASE ${cfg.config.dbname}" = "ALL PRIVILEGES"; };
}];
};
···
ensureDatabases = [ cfg.config.dbname ];
ensureUsers = [{
name = cfg.config.dbuser;
+
ensureDBOwnership = true;
}];
};
+1 -1
nixos/modules/services/web-apps/onlyoffice.nix
···
ensureDatabases = [ "onlyoffice" ];
ensureUsers = [{
name = "onlyoffice";
-
ensurePermissions = { "DATABASE \"onlyoffice\"" = "ALL PRIVILEGES"; };
}];
};
};
···
ensureDatabases = [ "onlyoffice" ];
ensureUsers = [{
name = "onlyoffice";
+
ensureDBOwnership = true;
}];
};
};
+1 -1
nixos/modules/services/web-apps/outline.nix
···
enable = true;
ensureUsers = [{
name = "outline";
-
ensurePermissions."DATABASE outline" = "ALL PRIVILEGES";
}];
ensureDatabases = [ "outline" ];
};
···
enable = true;
ensureUsers = [{
name = "outline";
+
ensureDBOwnership = true;
}];
ensureDatabases = [ "outline" ];
};
+1 -3
nixos/modules/services/web-apps/peering-manager.nix
···
ensureUsers = [
{
name = "peering-manager";
-
ensurePermissions = {
-
"DATABASE \"peering-manager\"" = "ALL PRIVILEGES";
-
};
}
];
};
···
ensureUsers = [
{
name = "peering-manager";
+
ensureDBOwnership = true;
}
];
};
-1
nixos/modules/services/web-apps/pixelfed.nix
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [{
name = user;
-
ensurePermissions = { };
}];
};
···
ensureDatabases = [ cfg.database.name ];
ensureUsers = [{
name = user;
}];
};
+11 -2
nixos/modules/services/web-apps/tt-rss.nix
···
assertion = cfg.database.password != null -> cfg.database.passwordFile == null;
message = "Cannot set both password and passwordFile";
}
];
services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
···
enable = mkDefault true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
-
{ name = cfg.user;
-
ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
}
];
};
···
assertion = cfg.database.password != null -> cfg.database.passwordFile == null;
message = "Cannot set both password and passwordFile";
}
+
{
+
assertion = cfg.database.createLocally -> cfg.database.name == cfg.user && cfg.database.user == cfg.user;
+
message = ''
+
When creating a database via NixOS, the db user and db name must be equal!
+
If you already have an existing DB+user and this assertion is new, you can safely set
+
`services.tt-rss.database.createLocally` to `false` because removal of `ensureUsers`
+
and `ensureDatabases` doesn't have any effect.
+
'';
+
}
];
services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
···
enable = mkDefault true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
+
{ name = cfg.database.user;
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/modules/services/web-servers/hydron.nix
···
ensureDatabases = [ "hydron" ];
ensureUsers = [
{ name = "hydron";
-
ensurePermissions = { "DATABASE hydron" = "ALL PRIVILEGES"; };
}
];
};
···
ensureDatabases = [ "hydron" ];
ensureUsers = [
{ name = "hydron";
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/tests/dex-oidc.nix
···
ensureUsers = [
{
name = "dex";
-
ensurePermissions = { "DATABASE dex" = "ALL PRIVILEGES"; };
}
];
};
···
ensureUsers = [
{
name = "dex";
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/tests/ferretdb.nix
···
ensureDatabases = [ "ferretdb" ];
ensureUsers = [{
name = "ferretdb";
-
ensurePermissions."DATABASE ferretdb" = "ALL PRIVILEGES";
}];
};
···
ensureDatabases = [ "ferretdb" ];
ensureUsers = [{
name = "ferretdb";
+
ensureDBOwnership = true;
}];
};
+1 -3
nixos/tests/freshrss-pgsql.nix
···
ensureUsers = [
{
name = "freshrss";
-
ensurePermissions = {
-
"DATABASE freshrss" = "ALL PRIVILEGES";
-
};
}
];
initialScript = pkgs.writeText "postgresql-password" ''
···
ensureUsers = [
{
name = "freshrss";
+
ensureDBOwnership = true;
}
];
initialScript = pkgs.writeText "postgresql-password" ''
+1 -1
nixos/tests/grafana/basic.nix
···
ensureDatabases = [ "grafana" ];
ensureUsers = [{
name = "grafana";
-
ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES";
}];
};
systemd.services.grafana.after = [ "postgresql.service" ];
···
ensureDatabases = [ "grafana" ];
ensureUsers = [{
name = "grafana";
+
ensureDBOwnership = true;
}];
};
systemd.services.grafana.after = [ "postgresql.service" ];
+1 -1
nixos/tests/hockeypuck.nix
···
ensureDatabases = [ "hockeypuck" ];
ensureUsers = [{
name = "hockeypuck";
-
ensurePermissions."DATABASE hockeypuck" = "ALL PRIVILEGES";
}];
};
};
···
ensureDatabases = [ "hockeypuck" ];
ensureUsers = [{
name = "hockeypuck";
+
ensureDBOwnership = true;
}];
};
};
+5 -7
nixos/tests/home-assistant.nix
···
nodes.hass = { pkgs, ... }: {
services.postgresql = {
enable = true;
-
-
# FIXME: hack for https://github.com/NixOS/nixpkgs/issues/216989
-
# Should be replaced with ensureUsers again when a solution for that is found
-
initialScript = pkgs.writeText "hass-setup-db.sql" ''
-
CREATE ROLE hass WITH LOGIN;
-
CREATE DATABASE hass WITH OWNER hass;
-
'';
};
services.home-assistant = {
···
nodes.hass = { pkgs, ... }: {
services.postgresql = {
enable = true;
+
ensureDatabases = [ "hass" ];
+
ensureUsers = [{
+
name = "hass";
+
ensureDBOwnership = true;
+
}];
};
services.home-assistant = {
+1 -2
nixos/tests/invidious.nix
···
enable = true;
initialScript = pkgs.writeText "init-postgres-with-password" ''
CREATE USER kemal WITH PASSWORD 'correct horse battery staple';
-
CREATE DATABASE invidious;
-
GRANT ALL PRIVILEGES ON DATABASE invidious TO kemal;
'';
};
};
···
enable = true;
initialScript = pkgs.writeText "init-postgres-with-password" ''
CREATE USER kemal WITH PASSWORD 'correct horse battery staple';
+
CREATE DATABASE invidious OWNER kemal;
'';
};
};
+1 -1
nixos/tests/paperless.nix
···
ensureDatabases = [ "paperless" ];
ensureUsers = [
{ name = config.services.paperless.user;
-
ensurePermissions = { "DATABASE \"paperless\"" = "ALL PRIVILEGES"; };
}
];
};
···
ensureDatabases = [ "paperless" ];
ensureUsers = [
{ name = config.services.paperless.user;
+
ensureDBOwnership = true;
}
];
};
-8
nixos/tests/pgadmin4.nix
···
authentication = ''
host all all localhost trust
'';
-
ensureUsers = [
-
{
-
name = "postgres";
-
ensurePermissions = {
-
"DATABASE \"postgres\"" = "ALL PRIVILEGES";
-
};
-
}
-
];
};
services.pgadmin = {
···
authentication = ''
host all all localhost trust
'';
};
services.pgadmin = {
+4 -6
nixos/tests/pgbouncer.nix
···
systemd.services.postgresql = {
postStart = ''
-
${pkgs.postgresql}/bin/psql -U postgres -c "ALTER ROLE testuser WITH LOGIN PASSWORD 'testpass'";
'';
};
···
ensureUsers = [
{
name = "testuser";
-
ensurePermissions = {
-
"DATABASE testdb" = "ALL PRIVILEGES";
-
};
}];
authentication = ''
local testdb testuser scram-sha-256
···
pgbouncer = {
enable = true;
listenAddress = "localhost";
-
databases = { testdb = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; };
authType = "scram-sha-256";
authFile = testAuthFile;
};
···
# Test if we can make a query through PgBouncer
one.wait_until_succeeds(
-
"psql 'postgres://testuser:testpass@localhost:6432/testdb' -c 'SELECT 1;'"
)
'';
})
···
systemd.services.postgresql = {
postStart = ''
+
${pkgs.postgresql}/bin/psql -U postgres -c "ALTER ROLE testuser WITH LOGIN PASSWORD 'testpass'";
+
${pkgs.postgresql}/bin/psql -U postgres -c "ALTER DATABASE testdb OWNER TO testuser;";
'';
};
···
ensureUsers = [
{
name = "testuser";
}];
authentication = ''
local testdb testuser scram-sha-256
···
pgbouncer = {
enable = true;
listenAddress = "localhost";
+
databases = { test = "host=/run/postgresql/ port=5432 auth_user=testuser dbname=testdb"; };
authType = "scram-sha-256";
authFile = testAuthFile;
};
···
# Test if we can make a query through PgBouncer
one.wait_until_succeeds(
+
"psql 'postgres://testuser:testpass@localhost:6432/test' -c 'SELECT 1;'"
)
'';
})
+1 -3
nixos/tests/powerdns-admin.nix
···
ensureUsers = [
{
name = "powerdnsadmin";
-
ensurePermissions = {
-
"DATABASE powerdnsadmin" = "ALL PRIVILEGES";
-
};
}
];
};
···
ensureUsers = [
{
name = "powerdnsadmin";
+
ensureDBOwnership = true;
}
];
};
+1 -1
nixos/tests/sftpgo.nix
···
ensureDatabases = [ "sftpgo" ];
ensureUsers = [{
name = "sftpgo";
-
ensurePermissions."DATABASE sftpgo" = "ALL PRIVILEGES";
}];
};
···
ensureDatabases = [ "sftpgo" ];
ensureUsers = [{
name = "sftpgo";
+
ensureDBOwnership = true;
}];
};
+23
nixos/tests/tandoor-recipes.nix
···
nodes.machine = { pkgs, ... }: {
services.tandoor-recipes = {
enable = true;
};
};
···
nodes.machine = { pkgs, ... }: {
services.tandoor-recipes = {
enable = true;
+
extraConfig = {
+
DB_ENGINE = "django.db.backends.postgresql";
+
POSTGRES_HOST = "/run/postgresql";
+
POSTGRES_USER = "tandoor_recipes";
+
POSTGRES_DB = "tandoor_recipes";
+
};
+
};
+
+
services.postgresql = {
+
enable = true;
+
ensureDatabases = [ "tandoor_recipes" ];
+
ensureUsers = [
+
{
+
name = "tandoor_recipes";
+
ensureDBOwnership = true;
+
}
+
];
+
};
+
+
systemd.services = {
+
tandoor-recipes = {
+
after = [ "postgresql.service" ];
+
};
};
};
+1 -1
nixos/tests/vikunja.nix
···
ensureDatabases = [ "vikunja-api" ];
ensureUsers = [
{ name = "vikunja-api";
-
ensurePermissions = { "DATABASE \"vikunja-api\"" = "ALL PRIVILEGES"; };
}
];
};
···
ensureDatabases = [ "vikunja-api" ];
ensureUsers = [
{ name = "vikunja-api";
+
ensureDBOwnership = true;
}
];
};
+3 -2
nixos/tests/wiki-js.nix
···
enable = true;
settings.db.host = "/run/postgresql";
settings.db.user = "wiki-js";
settings.logLevel = "debug";
};
services.postgresql = {
enable = true;
-
ensureDatabases = [ "wiki" ];
ensureUsers = [
{ name = "wiki-js";
-
ensurePermissions."DATABASE wiki" = "ALL PRIVILEGES";
}
];
};
···
enable = true;
settings.db.host = "/run/postgresql";
settings.db.user = "wiki-js";
+
settings.db.db = "wiki-js";
settings.logLevel = "debug";
};
services.postgresql = {
enable = true;
+
ensureDatabases = [ "wiki-js" ];
ensureUsers = [
{ name = "wiki-js";
+
ensureDBOwnership = true;
}
];
};