diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 876969ef9bb57..af19676d37db8 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 @@ -30,19 +32,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.extraPlugins == [] - then base - else base.withPackages cfg.extraPlugins; + # 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,8 @@ let groupAccessAvailable = versionAtLeast postgresql.version "11.0"; + extensionNames = map getName postgresql.installedExtensions; + extensionInstalled = extension: elem extension extensionNames; in { @@ -68,6 +72,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 @@ -371,12 +376,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. ''; }; @@ -630,7 +635,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; @@ -654,10 +659,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/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/postgresql.nix b/nixos/tests/postgresql/postgresql.nix index 55f61cc972272..40c8e1146dae5 100644 --- a/nixos/tests/postgresql/postgresql.nix +++ b/nixos/tests/postgresql/postgresql.nix @@ -14,22 +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 - ''; 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; { @@ -37,12 +56,17 @@ let }; nodes.machine = - { ... }: + { config, ... }: { services.postgresql = { inherit package; enable = true; enableJIT = lib.hasInfix "-jit-" package.name; + # 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 = { @@ -69,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)"): 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"; diff --git a/pkgs/servers/sql/postgresql/generic.nix b/pkgs/servers/sql/postgresql/generic.nix index 546dbf1e284c8..eaa4ef8444263 100644 --- a/pkgs/servers/sql/postgresql/generic.nix +++ b/pkgs/servers/sql/postgresql/generic.nix @@ -323,25 +323,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