From 7d07116532f622d56def733f81fe829bd0aba6a5 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Mon, 11 Nov 2024 23:23:46 +0100 Subject: [PATCH 1/4] nixos/postgresql: rename extraPlugins to extensions This is the upstream lingo, and it makes everything slightly less confusing. (cherry picked from commit 223a6c6ed0726be1fcc56bb3ecf25c1ae6e99813) --- nixos/modules/services/databases/postgresql.nix | 10 +++++----- nixos/modules/services/web-apps/immich.nix | 2 +- nixos/modules/services/web-apps/mobilizon.nix | 2 +- nixos/tests/postgresql/anonymizer.nix | 2 +- nixos/tests/postgresql/pgjwt.nix | 2 +- nixos/tests/postgresql/pgvecto-rs.nix | 2 +- nixos/tests/postgresql/timescaledb.nix | 2 +- nixos/tests/postgresql/tsja.nix | 2 +- nixos/tests/postgresql/wal2json.nix | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 22ff29fd95be6..7511c5245b645 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -41,9 +41,9 @@ let # works. base = if cfg.enableJIT then cfg.package.withJIT else cfg.package.withoutJIT; in - if cfg.extraPlugins == [] + if cfg.extensions == [] then base - else base.withPackages cfg.extraPlugins; + else base.withPackages cfg.extensions; toStr = value: if true == value then "yes" @@ -60,7 +60,6 @@ let ''; groupAccessAvailable = versionAtLeast postgresql.version "11.0"; - in { @@ -69,6 +68,7 @@ in (mkRenamedOptionModule [ "services" "postgresql" "logLinePrefix" ] [ "services" "postgresql" "settings" "log_line_prefix" ]) (mkRenamedOptionModule [ "services" "postgresql" "port" ] [ "services" "postgresql" "settings" "port" ]) + (mkRenamedOptionModule [ "services" "postgresql" "extraPlugins" ] [ "services" "postgresql" "extensions" ]) ]; ###### interface @@ -372,12 +372,12 @@ in ''; }; - extraPlugins = mkOption { + extensions = mkOption { type = with types; coercedTo (listOf path) (path: _ignorePg: path) (functionTo (listOf path)); default = _: []; example = literalExpression "ps: with ps; [ postgis pg_repack ]"; description = '' - List of PostgreSQL plugins. + List of PostgreSQL extensions to install. ''; }; diff --git a/nixos/modules/services/web-apps/immich.nix b/nixos/modules/services/web-apps/immich.nix index 6d9c909c4568d..b81e27d245f5d 100644 --- a/nixos/modules/services/web-apps/immich.nix +++ b/nixos/modules/services/web-apps/immich.nix @@ -227,7 +227,7 @@ in ensureClauses.login = true; } ]; - extraPlugins = ps: with ps; [ pgvecto-rs ]; + extensions = ps: with ps; [ pgvecto-rs ]; settings = { shared_preload_libraries = [ "vectors.so" ]; search_path = "\"$user\", public, vectors"; diff --git a/nixos/modules/services/web-apps/mobilizon.nix b/nixos/modules/services/web-apps/mobilizon.nix index 922d385f8d3bd..122e55ac912af 100644 --- a/nixos/modules/services/web-apps/mobilizon.nix +++ b/nixos/modules/services/web-apps/mobilizon.nix @@ -383,7 +383,7 @@ in ensureDBOwnership = false; } ]; - extraPlugins = ps: with ps; [ postgis ]; + extensions = ps: with ps; [ postgis ]; }; # Nginx config taken from support/nginx/mobilizon-release.conf diff --git a/nixos/tests/postgresql/anonymizer.nix b/nixos/tests/postgresql/anonymizer.nix index 44d79c2213a62..b5bd1479890c3 100644 --- a/nixos/tests/postgresql/anonymizer.nix +++ b/nixos/tests/postgresql/anonymizer.nix @@ -20,7 +20,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extraPlugins = ps: [ ps.anonymizer ]; + extensions = ps: [ ps.anonymizer ]; settings.shared_preload_libraries = [ "anon" ]; }; }; diff --git a/nixos/tests/postgresql/pgjwt.nix b/nixos/tests/postgresql/pgjwt.nix index 87622d54fcacd..2feb41d3366e4 100644 --- a/nixos/tests/postgresql/pgjwt.nix +++ b/nixos/tests/postgresql/pgjwt.nix @@ -24,7 +24,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extraPlugins = + extensions = ps: with ps; [ pgjwt pgtap diff --git a/nixos/tests/postgresql/pgvecto-rs.nix b/nixos/tests/postgresql/pgvecto-rs.nix index 702c55c38dacc..93072a566dcf2 100644 --- a/nixos/tests/postgresql/pgvecto-rs.nix +++ b/nixos/tests/postgresql/pgvecto-rs.nix @@ -38,7 +38,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extraPlugins = + extensions = ps: with ps; [ pgvecto-rs ]; diff --git a/nixos/tests/postgresql/timescaledb.nix b/nixos/tests/postgresql/timescaledb.nix index 8586a0db1bbd3..864efd2b57185 100644 --- a/nixos/tests/postgresql/timescaledb.nix +++ b/nixos/tests/postgresql/timescaledb.nix @@ -54,7 +54,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extraPlugins = + extensions = ps: with ps; [ timescaledb timescaledb_toolkit diff --git a/nixos/tests/postgresql/tsja.nix b/nixos/tests/postgresql/tsja.nix index 26d83f052a668..d2e5e37e6ce0b 100644 --- a/nixos/tests/postgresql/tsja.nix +++ b/nixos/tests/postgresql/tsja.nix @@ -21,7 +21,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extraPlugins = + extensions = ps: with ps; [ tsja ]; diff --git a/nixos/tests/postgresql/wal2json.nix b/nixos/tests/postgresql/wal2json.nix index aca3d5aa89540..174c8a8e50e89 100644 --- a/nixos/tests/postgresql/wal2json.nix +++ b/nixos/tests/postgresql/wal2json.nix @@ -17,7 +17,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extraPlugins = with package.pkgs; [ wal2json ]; + extensions = with package.pkgs; [ wal2json ]; settings = { wal_level = "logical"; max_replication_slots = "10"; From 210f9b1546504961b8fe6c3c04bcf6cbe23d3013 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Sun, 10 Nov 2024 17:08:59 +0100 Subject: [PATCH 2/4] nixos/postgresql: create infrastructure for relaxing systemd hardening By matching on the package names of the plugins passed into the package we can relax the systemd unit hardening as needed. (cherry picked from commit d370af0785e6f71bc79b3a9037046c6579a7dc32) --- .../modules/services/databases/postgresql.nix | 43 +++++++++++-------- pkgs/servers/sql/postgresql/generic.nix | 32 ++++++++------ 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 7511c5245b645..b4df05d2fe626 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -2,6 +2,7 @@ let inherit (lib) + any attrValues concatMapStrings concatStringsSep @@ -9,6 +10,7 @@ let elem escapeShellArgs filterAttrs + getName isString literalExpression mapAttrs @@ -31,19 +33,19 @@ let cfg = config.services.postgresql; - postgresql = - let - # ensure that - # services.postgresql = { - # enableJIT = true; - # package = pkgs.postgresql_; - # }; - # works. - base = if cfg.enableJIT then cfg.package.withJIT else cfg.package.withoutJIT; - in - if cfg.extensions == [] - then base - else base.withPackages cfg.extensions; + # ensure that + # services.postgresql = { + # enableJIT = true; + # package = pkgs.postgresql_; + # }; + # works. + basePackage = if cfg.enableJIT + then cfg.package.withJIT + else cfg.package.withoutJIT; + + postgresql = if cfg.extensions == [] + then basePackage + else basePackage.withPackages cfg.extensions; toStr = value: if true == value then "yes" @@ -60,6 +62,9 @@ let ''; groupAccessAvailable = versionAtLeast postgresql.version "11.0"; + + extensionNames = map getName postgresql.installedExtensions; + extensionInstalled = extension: elem extension extensionNames; in { @@ -639,7 +644,7 @@ in PrivateTmp = true; ProtectHome = true; ProtectSystem = "strict"; - MemoryDenyWriteExecute = lib.mkDefault (cfg.settings.jit == "off"); + MemoryDenyWriteExecute = lib.mkDefault (cfg.settings.jit == "off" && (!any extensionInstalled [ "plv8" ])); NoNewPrivileges = true; LockPersonality = true; PrivateDevices = true; @@ -663,10 +668,12 @@ in RestrictRealtime = true; RestrictSUIDSGID = true; SystemCallArchitectures = "native"; - SystemCallFilter = [ - "@system-service" - "~@privileged @resources" - ]; + SystemCallFilter = + [ + "@system-service" + "~@privileged @resources" + ] + ++ lib.optionals (any extensionInstalled [ "plv8" ]) [ "@pkey" ]; UMask = if groupAccessAvailable then "0027" else "0077"; } (mkIf (cfg.dataDir != "/var/lib/postgresql") { diff --git a/pkgs/servers/sql/postgresql/generic.nix b/pkgs/servers/sql/postgresql/generic.nix index 6ed9b843cf5ae..f93a0c56cb856 100644 --- a/pkgs/servers/sql/postgresql/generic.nix +++ b/pkgs/servers/sql/postgresql/generic.nix @@ -315,25 +315,33 @@ let }; }); - postgresqlWithPackages = { postgresql, buildEnv }: f: buildEnv { + postgresqlWithPackages = { postgresql, buildEnv }: f: let + installedExtensions = f postgresql.pkgs; + in buildEnv { name = "${postgresql.pname}-and-plugins-${postgresql.version}"; - paths = f postgresql.pkgs ++ [ + paths = installedExtensions ++ [ postgresql postgresql.man # in case user installs this into environment ]; pathsToLink = ["/"]; - passthru.version = postgresql.version; - passthru.psqlSchema = postgresql.psqlSchema; - passthru.withJIT = postgresqlWithPackages { - inherit buildEnv; - postgresql = postgresql.withJIT; - } f; - passthru.withoutJIT = postgresqlWithPackages { - inherit buildEnv; - postgresql = postgresql.withoutJIT; - } f; + passthru = { + inherit installedExtensions; + inherit (postgresql) + psqlSchema + version + ; + + withJIT = postgresqlWithPackages { + inherit buildEnv; + postgresql = postgresql.withJIT; + } f; + withoutJIT = postgresqlWithPackages { + inherit buildEnv; + postgresql = postgresql.withoutJIT; + } f; + }; }; in From 830116313f2b2c1c0442b421524d3f78c194fa3b Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Sun, 10 Nov 2024 22:46:47 +0100 Subject: [PATCH 3/4] nixosTests.postgresql: test hardening gets relaxed The plv8 plugin requires access to pkey syscalls. The execution will crash hard when it is not allowed by the syscall filter. Co-Authored-By: Jan Tojnar (cherry picked from commit e198536d265ad4d2822293315fc6eb47c8fe69cb) --- nixos/tests/postgresql/postgresql.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nixos/tests/postgresql/postgresql.nix b/nixos/tests/postgresql/postgresql.nix index 55f61cc972272..bc782b7158f9f 100644 --- a/nixos/tests/postgresql/postgresql.nix +++ b/nixos/tests/postgresql/postgresql.nix @@ -26,6 +26,16 @@ let INSERT INTO sth (id) VALUES (1); CREATE TABLE xmltest ( doc xml ); INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled + -- check if hardening gets relaxed + CREATE EXTENSION plv8; + -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute + DO $$ + let xs = []; + for (let i = 0, n = 400000; i < n; i++) { + xs.push(Math.round(Math.random() * n)) + } + console.log(xs.reduce((acc, x) => acc + x, 0)); + $$ LANGUAGE plv8; ''; makeTestForWithBackupAll = @@ -43,6 +53,7 @@ let inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; + extensions = ps: with ps; [ plv8 ]; }; services.postgresqlBackup = { From 0bd7e8585ffe58b983792eb3e5f6853aef164f23 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sat, 16 Nov 2024 17:09:27 +0100 Subject: [PATCH 4/4] nixos/tests/postgresql: test plv8 hardening on non-JIT variants only PostgreSQL with JIT support enabled doesn't work with plv8. Hence, we'd get an evaluation failure for each `nixosTests.postgresql.postgresql.postgresql_jit_X`. This should be restructured in the future (less VM tests for custom extensions, but a single VM test for this case to cover). For now, we should get this fix out and this is a good-enough approach. (cherry picked from commit 68d9643388957feee8c140ca0abd240b9761670b) --- nixos/tests/postgresql/postgresql.nix | 63 ++++++++++++++++----------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/nixos/tests/postgresql/postgresql.nix b/nixos/tests/postgresql/postgresql.nix index bc782b7158f9f..40c8e1146dae5 100644 --- a/nixos/tests/postgresql/postgresql.nix +++ b/nixos/tests/postgresql/postgresql.nix @@ -14,32 +14,41 @@ let postgresql-clauses = makeEnsureTestFor package; }; - test-sql = pkgs.writeText "postgresql-test" '' - CREATE EXTENSION pgcrypto; -- just to check if lib loading works - CREATE TABLE sth ( - id int + test-sql = + enablePLv8Test: + pkgs.writeText "postgresql-test" ( + '' + CREATE EXTENSION pgcrypto; -- just to check if lib loading works + CREATE TABLE sth ( + id int + ); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + CREATE TABLE xmltest ( doc xml ); + INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled + '' + + lib.optionalString enablePLv8Test '' + -- check if hardening gets relaxed + CREATE EXTENSION plv8; + -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute + DO $$ + let xs = []; + for (let i = 0, n = 400000; i < n; i++) { + xs.push(Math.round(Math.random() * n)) + } + console.log(xs.reduce((acc, x) => acc + x, 0)); + $$ LANGUAGE plv8; + '' ); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - CREATE TABLE xmltest ( doc xml ); - INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled - -- check if hardening gets relaxed - CREATE EXTENSION plv8; - -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute - DO $$ - let xs = []; - for (let i = 0, n = 400000; i < n; i++) { - xs.push(Math.round(Math.random() * n)) - } - console.log(xs.reduce((acc, x) => acc + x, 0)); - $$ LANGUAGE plv8; - ''; makeTestForWithBackupAll = package: backupAll: + let + enablePLv8Check = !package.pkgs.plv8.meta.broken; + in makeTest { name = "postgresql${lib.optionalString backupAll "-backup-all"}-${package.name}"; meta = with lib.maintainers; { @@ -47,13 +56,17 @@ let }; nodes.machine = - { ... }: + { config, ... }: { services.postgresql = { inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; - extensions = ps: with ps; [ plv8 ]; + # plv8 doesn't support postgresql with JIT, so we only run the test + # for the non-jit variant. + # TODO(@Ma27) split this off into its own VM test and move a few other + # extension tests to use postgresqlTestExtension. + extensions = lib.mkIf enablePLv8Check (ps: with ps; [ plv8 ]); }; services.postgresqlBackup = { @@ -80,7 +93,7 @@ let with subtest("Postgresql is available just after unit start"): machine.succeed( - "cat ${test-sql} | sudo -u postgres psql" + "cat ${test-sql enablePLv8Check} | sudo -u postgres psql" ) with subtest("Postgresql survives restart (bug #1735)"):