From 3d29b7d3a2082c0b4d7b1ef0c5b4ae436eb0e48c Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Mon, 5 May 2025 10:31:21 +0200 Subject: [PATCH] nixos/postgresql: improve local peer authentication with default map This allows to easily map allowed database roles to system users. --- nixos/doc/manual/redirects.json | 6 ++++ .../modules/services/databases/postgresql.md | 32 +++++++++++++++++++ .../modules/services/databases/postgresql.nix | 16 ++++++++++ nixos/tests/postgresql/postgresql.nix | 5 ++- 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 2c9b0242e9e8..7abcc36257f5 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -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" ], diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md index daff317e9e2b..e1256c9672f2 100644 --- a/nixos/modules/services/databases/postgresql.md +++ b/nixos/modules/services/databases/postgresql.md @@ -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} diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index abcb704b887a..48ff4c2a48ef 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -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; diff --git a/nixos/tests/postgresql/postgresql.nix b/nixos/tests/postgresql/postgresql.nix index a88143c6421a..ecf1b5552804 100644 --- a/nixos/tests/postgresql/postgresql.nix +++ b/nixos/tests/postgresql/postgresql.nix @@ -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 )