diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 2e654752834104..1a88bef76409de 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -790,6 +790,11 @@ - `iproute2` now has libbpf support. +- `postgresql` is now [hardened by default](#module-services-postgres-hardening) using the common `systemd` settings for that. + + If you use extensions that are not packaged in nixpkgs, please review whether it still works + with the current settings and adjust accordingly if needed. + - `nix.channel.enable = false` no longer implies `nix.settings.nix-path = []`. Since Nix 2.13, a `nix-path` set in `nix.conf` cannot be overridden by the `NIX_PATH` configuration variable. diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md index 5108f040e96846..6cd3defb24a332 100644 --- a/nixos/modules/services/databases/postgresql.md +++ b/nixos/modules/services/databases/postgresql.md @@ -364,6 +364,24 @@ postgresql.withJIT.pname evaluates to `"foobar"`. +## Service hardening {#module-services-postgres-hardening} + +The service created by the [`postgresql`-module](#opt-services.postgresql.enable) uses +several common hardening options from `systemd`, most notably: + +* Memory pages must not be both writable and executable (this only applies to non-JIT setups). +* A system call filter (see {manpage}`systemd.exec(5)` for details on `@system-service`). +* A stricter default UMask (`0027`). +* Only sockets of type `AF_INET`/`AF_INET6`/`AF_NETLINK`/`AF_UNIX` allowed. +* Restricted filesystem access (private `/tmp`, most of the file-system hierachy is mounted read-only, only process directories in `/proc` that are owned by the same user). + +The NixOS module also contains necessary adjustments for extensions from `nixpkgs` +if these are enabled. If an extension or a postgresql feature from `nixpkgs` breaks +with hardening, it's considered a bug. + +When using extensions that are not packaged in `nixpkgs`, hardening adjustments may +become necessary. + ## Notable differences to upstream {#module-services-postgres-upstream-deviation} - To avoid circular dependencies between default and -dev outputs, the output of the `pg_config` system view has been removed. diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index ceaccde813a0a1..876969ef9bb579 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -623,7 +623,46 @@ in TimeoutSec = 120; ExecStart = "${postgresql}/bin/postgres"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DevicePolicy = "closed"; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + MemoryDenyWriteExecute = lib.mkDefault (cfg.settings.jit == "off"); + NoNewPrivileges = true; + LockPersonality = true; + PrivateDevices = true; + PrivateMounts = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" # used for network interface enumeration + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged @resources" + ]; + UMask = if groupAccessAvailable then "0027" else "0077"; } + (mkIf (cfg.dataDir != "/var/lib/postgresql") { + ReadWritePaths = [ cfg.dataDir ]; + }) (mkIf (cfg.dataDir == "/var/lib/postgresql/${cfg.package.psqlSchema}") { StateDirectory = "postgresql postgresql/${cfg.package.psqlSchema}"; StateDirectoryMode = if groupAccessAvailable then "0750" else "0700"; diff --git a/nixos/tests/postgresql-wal-receiver.nix b/nixos/tests/postgresql-wal-receiver.nix index ab2ab4ad0d4fa6..a984f73c2be5b5 100644 --- a/nixos/tests/postgresql-wal-receiver.nix +++ b/nixos/tests/postgresql-wal-receiver.nix @@ -22,8 +22,8 @@ let replicationUser = "wal_receiver_user"; replicationSlot = "wal_receiver_slot"; replicationConn = "postgresql://${replicationUser}@localhost"; - baseBackupDir = "/tmp/pg_basebackup"; - walBackupDir = "/tmp/pg_wal"; + baseBackupDir = "/var/cache/wals/pg_basebackup"; + walBackupDir = "/var/cache/wals/pg_wal"; recoveryFile = pkgs.writeTextDir "recovery.signal" ""; @@ -32,6 +32,10 @@ let meta.maintainers = with lib.maintainers; [ pacien ]; nodes.machine = { ... }: { + systemd.tmpfiles.rules = [ + "d /var/cache/wals 0750 postgres postgres - -" + ]; + services.postgresql = { package = pkg; enable = true; @@ -60,6 +64,7 @@ let # This is only to speedup test, it isn't time racing. Service is set to autorestart always, # default 60sec is fine for real system, but is too much for a test systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5; + systemd.services.postgresql.serviceConfig.ReadWritePaths = [ "/var/cache/wals" ]; }; testScript = '' diff --git a/nixos/tests/postgresql.nix b/nixos/tests/postgresql.nix index c0dd24cf6ad2ee..ce16e54edf6629 100644 --- a/nixos/tests/postgresql.nix +++ b/nixos/tests/postgresql.nix @@ -126,6 +126,8 @@ let with subtest("Initdb works"): machine.succeed("sudo -u postgres initdb -D /tmp/testpostgres2") + machine.log(machine.execute("systemd-analyze security postgresql.service | grep -v ✓")[1]) + machine.shutdown() '';