@@ -36,10 +36,17 @@ more `ConfigMap` or `Secret` resources (see the
3636
3737All monitoring queries that are performed on PostgreSQL are:
3838
39- - atomic (one transaction per query)
40- - executed with the ` pg_monitor ` role
39+ - atomic (one read-only transaction per query)
40+ - executed as the ` cnpg_metrics_exporter ` role (a member of ` pg_monitor ` )
4141- executed with ` application_name ` set to ` cnpg_metrics_exporter `
42- - executed as user ` postgres `
42+
43+ The connection uses peer authentication on the pod-local Unix socket;
44+ because ` session_user ` is never a superuser, the monitoring session
45+ cannot escalate via ` RESET ROLE ` or ` RESET SESSION AUTHORIZATION ` . Do
46+ not grant additional privileges or role memberships to
47+ ` cnpg_metrics_exporter ` beyond ` pg_monitor ` and the table-level grants
48+ required by your custom queries: any extra membership flows into the
49+ scrape session via inheritance and weakens this property.
4350
4451Please refer to the "Predefined Roles" section in PostgreSQL
4552[ documentation] ( https://www.postgresql.org/docs/current/predefined-roles.html )
@@ -494,6 +501,42 @@ Take care that the referred resources have to be created **in the same namespace
494501 and a key `queryName` containing the overwritten query name.
495502:: :
496503
504+ # ### Custom query privileges and safety
505+
506+ :::warning
507+ Custom queries run as the `cnpg_metrics_exporter` role, which inherits
508+ ` pg_monitor` . Queries within `pg_monitor`'s scope (catalog reads,
509+ ` pg_stat_*` views, configuration parameters) work without modification.
510+ Queries that read user-owned tables or superuser-only catalogs (e.g.
511+ ` pg_authid` , `pg_subscription`) need explicit grants. Reading a table
512+ also requires USAGE on its schema :
513+
514+ ` ` ` sql
515+ GRANT USAGE ON SCHEMA myschema TO cnpg_metrics_exporter;
516+ GRANT SELECT ON TABLE myschema.mytable TO cnpg_metrics_exporter;
517+ ` ` `
518+
519+ Every database in `target_databases` must allow
520+ ` cnpg_metrics_exporter` to `CONNECT`. On clusters that have
521+ revoked `CONNECT` from `PUBLIC` for a database, grant it
522+ explicitly to that role :
523+
524+ ` ` ` sql
525+ GRANT CONNECT ON DATABASE domainapp TO cnpg_metrics_exporter;
526+ ` ` `
527+
528+ Prefer an explicit list of trusted databases (e.g.
529+ `target_databases : ["domainapp"]`) over the `"*"` wildcard. The
530+ wildcard scrapes every database the role can connect to and
531+ silently skips the rest, so an explicit list makes a missing grant
532+ easier to notice. Use `"*"` only when the query is meant to
533+ collect per-database metrics across the whole cluster.
534+
535+ Schema-qualify catalog references (`pg_catalog.now()`,
536+ ` pg_catalog.current_database()` ) to prevent `search_path` shadowing
537+ by user-owned objects.
538+ :: :
539+
497540# ### Example of a user defined metric
498541
499542Here you can see an example of a `ConfigMap` containing a single custom query,
@@ -570,17 +613,22 @@ Database auto-discovery can be enabled for a specific query by specifying a
570613*shell-like pattern* (i.e., containing `*`, `?` or `[]`) in the list of
571614` target_databases` . If provided, the operator will expand the list of target
572615databases by adding all the databases returned by the execution of `SELECT
573- datname FROM pg_database WHERE datallowconn AND NOT datistemplate` and matching
574- the pattern according to [path.Match()](https://pkg.go.dev/path#Match) rules.
616+ datname FROM pg_catalog.pg_database WHERE datallowconn AND NOT datistemplate
617+ AND pg_catalog.has_database_privilege(datname, 'CONNECT')` and matching the
618+ pattern according to [path.Match()](https://pkg.go.dev/path#Match) rules.
619+ Databases on which `cnpg_metrics_exporter` lacks the `CONNECT` privilege are
620+ silently skipped; if you want a database with revoked `PUBLIC` access to be
621+ scraped, grant `CONNECT` explicitly (see "Custom query privileges and safety"
622+ above).
575623
576624:::note
577625 The `*` character has a [special meaning](https://yaml.org/spec/1.2/spec.html#id2786448) in yaml,
578626 so you need to quote (`"*"`) the `target_databases` value when it includes such a pattern.
579627:: :
580628
581629It is recommended that you always include the name of the database
582- in the returned labels, for example using the `current_database()` function
583- as in the following example :
630+ in the returned labels, for example using the `pg_catalog. current_database()`
631+ function as in the following example :
584632
585633` ` ` yaml
586634some_query: |
@@ -757,6 +805,33 @@ CloudNativePG is inspired by the PostgreSQL Prometheus Exporter, but
757805presents some differences. In particular, the `cache_seconds` field is not implemented
758806in CloudNativePG's exporter.
759807
808+ # ## Manually creating the metrics exporter role
809+
810+ The operator creates the `cnpg_metrics_exporter` PostgreSQL role on the
811+ primary during reconciliation; it then propagates to standbys and
812+ replica clusters via streaming replication.
813+
814+ If the role is missing (replica cluster upgraded before its primary,
815+ restore from a backup that predates the role, accidental removal),
816+ recreate it as a superuser on the writable primary of the replication
817+ chain (the source primary, not a designated primary of a replica
818+ cluster) :
819+
820+ ` ` ` sql
821+ CREATE ROLE cnpg_metrics_exporter WITH LOGIN NOSUPERUSER NOCREATEDB
822+ NOCREATEROLE NOREPLICATION NOBYPASSRLS INHERIT;
823+ GRANT pg_monitor TO cnpg_metrics_exporter;
824+ ` ` `
825+
826+ If your custom monitoring queries need access to objects outside
827+ ` pg_monitor` ' s scope, grant the necessary privileges explicitly. SELECT
828+ on a table also requires USAGE on its schema:
829+
830+ ```sql
831+ GRANT USAGE ON SCHEMA myschema TO cnpg_metrics_exporter;
832+ GRANT SELECT ON TABLE myschema.mytable TO cnpg_metrics_exporter;
833+ ```
834+
760835## Monitoring the CloudNativePG operator
761836
762837The operator internally exposes [Prometheus](https://prometheus.io/) metrics
0 commit comments