mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-30 08:37:31 +09:00
shared_preload_libraries is blocked in postgresql.parameters, and rightly so: the parameter is free text and accepts a path, so it can point at a writable location such as the PGDATA volume. What the block leaves is an indirect route. CloudNativePG preloads pg_stat_statements, auto_explain and pgaudit by itself as soon as a parameter under their prefix is set, such as pg_stat_statements.track, so enabling one of them today means setting one of its tuning parameters, even to its default value. pg_prewarm has no route at all, and its autoprewarm worker only runs when the module is preloaded. postgresql.sharedPreloadLibraries is the explicit switch. It is a closed enum of four module names that already ship in the CloudNativePG PostgreSQL image, rendered onto the CNPG Cluster field of the same meaning, which the operator merges with the libraries it preloads on its own. A bare name can only resolve inside $libdir, which sits on the read-only image filesystem, and dynamic_library_path stays blocked, so no path can be introduced. The blocklist on postgresql.parameters is left untouched. The template repeats the enum as a backstop for renders that skip schema validation. helm-unittest always validates the schema and never reaches it, so a bats suite renders with --skip-schema-validation to keep the backstop, and its agreement with the enum, under test. Verified on a live CloudNativePG 1.27.3 cluster: with the field set, the operator writes shared_preload_libraries into postgresql.conf, SHOW reports the modules, and pg_stat_statements returns rows. Changing the list restarts PostgreSQL on every instance, the primary included, through the instance manager and without restarting the pod. Assisted-by: LLM Signed-off-by: Mattia Eleuteri <mattia@hidora.io>
126 lines
4.2 KiB
YAML
126 lines
4.2 KiB
YAML
suite: shared preload libraries allowlist
|
|
|
|
# postgresql.sharedPreloadLibraries maps onto the CloudNativePG Cluster field of the
|
|
# same meaning, which the operator renders into postgresql.conf and which is additive
|
|
# to the operator's own defaults.
|
|
#
|
|
# It exists because shared_preload_libraries stays blocked in postgresql.parameters:
|
|
# that parameter is free text and accepts a path, so it can point at a writable
|
|
# location such as the PGDATA volume. A bare library name from this allowlist can only
|
|
# resolve inside $libdir, which sits on the read-only image filesystem, so the code
|
|
# execution vector the block defends against does not apply. Every name in the list
|
|
# ships with the CloudNativePG PostgreSQL image.
|
|
#
|
|
# CloudNativePG already preloads pg_stat_statements, auto_explain and pgaudit on its
|
|
# own when a parameter under their prefix is set (ManagedExtensions in its
|
|
# pkg/postgres/configuration.go). This list is the explicit switch that needs no
|
|
# tuning parameter, and the only route for pg_prewarm.
|
|
#
|
|
# The enum in values.schema.json is the guard the negative cases below exercise: it
|
|
# rejects a value before rendering starts. The template carries the same allowlist as
|
|
# a backstop for renders that bypass schema validation, which is why those two checks
|
|
# assert the schema error rather than the template one; the backstop itself is pinned
|
|
# by hack/check-postgres-preload-allowlist.bats.
|
|
|
|
templates:
|
|
- templates/db.yaml
|
|
|
|
release:
|
|
name: pg-test
|
|
namespace: tenant-test
|
|
|
|
set:
|
|
_cluster:
|
|
scheduling: {}
|
|
cluster-domain: cozy.local
|
|
|
|
tests:
|
|
- it: "the field is absent by default, leaving the operator defaults untouched"
|
|
documentSelector:
|
|
path: kind
|
|
value: Cluster
|
|
asserts:
|
|
- notExists:
|
|
path: spec.postgresql.shared_preload_libraries
|
|
|
|
- it: "an allowed library is rendered onto the CNPG field"
|
|
set:
|
|
postgresql:
|
|
sharedPreloadLibraries:
|
|
- pg_stat_statements
|
|
documentSelector:
|
|
path: kind
|
|
value: Cluster
|
|
asserts:
|
|
- equal:
|
|
path: spec.postgresql.shared_preload_libraries
|
|
value:
|
|
- pg_stat_statements
|
|
|
|
- it: "order is preserved for several allowed libraries"
|
|
set:
|
|
postgresql:
|
|
sharedPreloadLibraries:
|
|
- pg_stat_statements
|
|
- auto_explain
|
|
- pgaudit
|
|
- pg_prewarm
|
|
documentSelector:
|
|
path: kind
|
|
value: Cluster
|
|
asserts:
|
|
- equal:
|
|
path: spec.postgresql.shared_preload_libraries
|
|
value:
|
|
- pg_stat_statements
|
|
- auto_explain
|
|
- pgaudit
|
|
- pg_prewarm
|
|
|
|
- it: "preloading does not disturb postgresql.parameters"
|
|
set:
|
|
postgresql:
|
|
sharedPreloadLibraries:
|
|
- pg_stat_statements
|
|
parameters:
|
|
max_connections: 200
|
|
documentSelector:
|
|
path: kind
|
|
value: Cluster
|
|
asserts:
|
|
- equal:
|
|
path: spec.postgresql.parameters.max_connections
|
|
value: "200"
|
|
- equal:
|
|
path: spec.postgresql.parameters.max_wal_senders
|
|
value: "30"
|
|
|
|
- it: "a library outside the allowlist is rejected by the schema"
|
|
set:
|
|
postgresql:
|
|
sharedPreloadLibraries:
|
|
- pg_cron
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "postgresql/sharedPreloadLibraries/0.*value must be one of 'pg_stat_statements', 'auto_explain', 'pgaudit', 'pg_prewarm'"
|
|
|
|
- it: "an absolute path is rejected by the schema, which is the vector the parameter block defends against"
|
|
set:
|
|
postgresql:
|
|
sharedPreloadLibraries:
|
|
- /var/lib/postgresql/data/pgdata/evil.so
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "postgresql/sharedPreloadLibraries/0.*value must be one of 'pg_stat_statements', 'auto_explain', 'pgaudit', 'pg_prewarm'"
|
|
|
|
- it: "shared_preload_libraries stays blocked as a parameter even when the allowlist is used"
|
|
set:
|
|
postgresql:
|
|
sharedPreloadLibraries:
|
|
- pg_stat_statements
|
|
parameters:
|
|
shared_preload_libraries: pg_stat_statements
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "SECURITY: Parameter 'shared_preload_libraries' is blocked. This parameter can enable arbitrary code execution and must not be set by tenants."
|