nixos/postgresql: improve local peer authentication with default map

This allows to easily map allowed database roles to system users.
This commit is contained in:
Wolfgang Walther 2025-05-05 10:31:21 +02:00
parent f934044282
commit 3d29b7d3a2
No known key found for this signature in database
GPG key ID: B39893FA5F65CAE1
4 changed files with 58 additions and 1 deletions

View file

@ -1286,6 +1286,12 @@
"module-services-postgres-initializing-extra-permissions-service-user-oneshot": [
"index.html#module-services-postgres-initializing-extra-permissions-service-user-oneshot"
],
"module-services-postgres-authentication": [
"index.html#module-services-postgres-authentication"
],
"module-services-postgres-authentication-user-mapping": [
"index.html#module-services-postgres-authentication-user-mapping"
],
"module-services-postgres-upgrading": [
"index.html#module-services-postgres-upgrading"
],

View file

@ -170,6 +170,38 @@ are already created.
}
```
## Authentication {#module-services-postgres-authentication}
Local connections are made through unix sockets by default and support [peer authentication](https://www.postgresql.org/docs/current/auth-peer.html).
This allows system users to login with database roles of the same name.
For example, the `postgres` system user is allowed to login with the database role `postgres`.
System users and database roles might not always match.
In this case, to allow access for a service, you can create a [user name map](https://www.postgresql.org/docs/current/auth-username-maps.html) between system roles and an existing database role.
### User Mapping {#module-services-postgres-authentication-user-mapping}
Assume that your app creates a role `admin` and you want the `root` user to be able to login with it.
You can then use [](#opt-services.postgresql.identMap) to define the map and [](#opt-services.postgresql.authentication) to enable it:
```nix
services.postgresql = {
identMap = ''
admin root admin
'';
authentication = ''
local all admin peer map=admin
'';
}
```
::: {.warning}
To avoid conflicts with other modules, you should never apply a map to `all` roles.
Because PostgreSQL will stop on the first matching line in `pg_hba.conf`, a line matching all roles would lock out other services.
Each module should only manage user maps for the database roles that belong to this module.
Best practice is to name the map after the database role it manages to avoid name conflicts.
:::
## Upgrading {#module-services-postgres-upgrading}
::: {.note}

View file

@ -274,6 +274,14 @@ in
Defines the mapping from system users to database users.
See the [auth doc](https://postgresql.org/docs/current/auth-username-maps.html).
There is a default map "postgres" which is used for local peer authentication
as the postgres superuser role.
For example, to allow the root user to login as the postgres superuser, add:
```
postgres root postgres
```
'';
};
@ -674,12 +682,20 @@ in
(mkBefore "# Generated file; do not edit!")
(mkAfter ''
# default value of services.postgresql.authentication
local all postgres peer map=postgres
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
'')
];
# The default allows to login with the same database username as the current system user.
# This is the default for peer authentication without a map, but needs to be made explicit
# once a map is used.
services.postgresql.identMap = mkAfter ''
postgres postgres postgres
'';
services.postgresql.systemCallFilter = mkMerge [
(mapAttrs (const mkDefault) {
"@system-service" = true;

View file

@ -54,6 +54,9 @@ let
services.postgresql = {
inherit package;
enable = true;
identMap = ''
postgres root postgres
'';
# TODO(@Ma27) split this off into its own VM test and move a few other
# extension tests to use postgresqlTestExtension.
extensions = ps: with ps; [ plv8 ];
@ -73,7 +76,7 @@ let
in
''
def check_count(statement, lines):
return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
return 'test $(psql -U postgres postgres -tAc "{}"|wc -l) -eq {}'.format(
statement, lines
)