Database Role Management¶
From its inception, CloudNativePG has managed the creation of specific roles required in PostgreSQL instances:
some reserved users, such as the
postgressuperuser,streaming_replicaandcnpg_pooler_pgbouncer(when the PgBouncerPooleris used)The application user, set as the low-privilege owner of the application database
This process is described in the BootstrapConfiguration section.
With the managed stanza in the cluster spec, CloudNativePG now
provides full lifecycle management for roles specified in
.spec.managed.roles .
This feature enables declarative management of existing roles, as well as the creation of new roles if they are not already present in the database. Role creation will occur after the database bootstrapping is complete.
An example manifest for a cluster with declarative role management can be found in the file
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example-with-roles
spec:
instances: 3
storage:
size: 1Gi
managed:
roles:
- name: app
createdb: true
login: true
- name: dante
ensure: present
comment: my database-side comment
login: true
superuser: false
createdb: true
createrole: false
inherit: false
replication: false
bypassrls: false
connectionLimit: 4
validUntil: "2053-04-12T15:04:05Z"
inRoles:
- pg_monitor
- pg_signal_backend
passwordSecret:
name: cluster-example-dante
- --
apiVersion: v1
data:
username: ZGFudGU=
password: ZGFudGU=
kind: Secret
metadata:
name: cluster-example-dante
type: kubernetes.io/basic-auth
.
Here is an excerpt from that file:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
spec:
managed:
roles:
- name: dante
ensure: present
comment: Dante Alighieri
login: true
superuser: false
inRoles:
- pg_monitor
- pg_signal_backend
- The role specification in
spec.managed.rolesadheres to the PostgreSQL structure and naming conventions . Please refer to the API Reference for the full list of
attributes you can define for each role.
A few points are worth noting:
The
ensureattribute is not part of PostgreSQL. It enables declarative role management to create and remove roles. The two possible values arepresent(the default) andabsent.The
inheritattribute is true by default, following PostgreSQL conventions.The
connectionLimitattribute defaults to -1, in line with PostgreSQL conventions.Role membership with
inRolesdefaults to no memberships.
Declarative role management ensures that PostgreSQL instances align with the spec. If a user modifies role attributes directly in the database, the CloudNativePG operator will revert those changes during the next reconciliation cycle.
Password management¶
The declarative role management feature includes reconciling of role passwords. Passwords are managed in fundamentally different ways in the Kubernetes world and in PostgreSQL, and as a result there are a few things to note.
Managed role configurations may optionally specify the name of a Secret where the username and password are stored (encoded in Base64 as is usual in Kubernetes). For example:
managed:
roles:
- name: dante
ensure: present
[… snipped …]
passwordSecret:
name: cluster-example-dante
This would assume the existence of a Secret called
cluster-example-dante , containing a username and password. The
username should match the role we are setting the password for. For
example, :
apiVersion: v1
data:
username: ZGFudGU=
password: ZGFudGU=
kind: Secret
metadata:
name: cluster-example-dante
labels:
cnpg.io/reload: "true"
type: kubernetes.io/basic-auth
If there is no passwordSecret specified for a role, the instance
manager will not try to CREATE / ALTER the role with a password. This
keeps with PostgreSQL conventions, where ALTER will not update passwords
unless directed to with WITH PASSWORD .
If a role was initially created with a password, and we would like to
set the password to NULL in PostgreSQL, this necessitates being explicit
on the part of the user of CloudNativePG. To distinguish “no password
provided in spec” from “set the password to NULL”, the field
DisablePassword should be used.
Imagine we decided we would like to have no password on the dante
role in the database. In such case we would specify the following:
managed:
roles:
- name: dante
ensure: present
[… snipped …]
disablePassword: true
NOTE: it is considered an error to set both passwordSecret and
disablePassword on a given role. This configuration will be rejected
by the validation webhook.
Password expiry, VALID UNTIL¶
The VALID UNTIL role attribute in PostgreSQL controls password
expiry. Roles created without VALID UNTIL specified get NULL by
default in PostgreSQL, meaning that their password will never expire.
PostgreSQL uses a timestamp type for VALID UNTIL , which includes
support for the value 'infinity' indicating that the password never
expires. Please see the PostgreSQL documentation
for reference.
With declarative role management, the validUntil attribute for
managed roles controls password expiry. validUntil can only take:
a Kubernetes timestamp, or
be omitted (defaulting to
null)
In the first case, the given validUntil timestamp will be set in the
database as the VALID UNTIL attribute of the role.
In the second case (omitted validUntil ) the operator will ensure
password never expires, mirroring the behavior of PostgreSQL.
Specifically:
in case of new role, it will omit the
VALID UNTILclause in the role creation statementin case of existing role, it will set
VALID UNTILtoinfinityifVALID UNTILwas not set toNULLin the database (this is due to PostgreSQL not allowingVALID UNTIL NULLin theALTER ROLESQL statement)
Warning
The declarative role management feature has changed behavior since its initial version (1.20.0). In 1.20.0, a role without a passwordSecret would lead to setting the password to NULL in PostgreSQL. In practice there is little difference from 1.20.0. New roles created without passwordSecret will have a NULL password. The relevant change is when using the managed roles to manage roles that had been previously created. In 1.20.0, doing this might inadvertently result in setting existing passwords to NULL .
Unrealizable role configurations¶
In PostgreSQL, in some cases, commands cannot be honored by the database and will be rejected. Please refer to the PostgreSQL documentation on error codes
for details.
Role operations can produce such fundamental errors. Two examples:
We ask PostgreSQL to create the role
petrarcaas a member of the role (group)poets, butpoetsdoes not exist.We ask PostgreSQL to drop the role
dante, but the roledanteis the owner of the databaseinferno.
These fundamental errors cannot be fixed by the database, nor the
CloudNativePG operator, without clarification from the human
administrator. The two examples above could be fixed by creating the
role poets or dropping the database inferno respectively, but
they might have originated due to human error, and in such case, the
“fix” proposed might be the wrong thing to do.
CloudNativePG will record when such fundamental errors occur, and will display them in the cluster Status. Which segues into…
Status of managed roles¶
The CRD status includes a section for the managed roles’ status, as shown below:
status:
[…snipped…]
managedRolesStatus:
byStatus:
not-managed:
- app
pending-reconciliation:
- dante
- petrarca
reconciled:
- ariosto
reserved:
- postgres
- streaming_replica
cannotReconcile:
dante:
- could not perform DELETE on role dante: owner of database inferno
petrarca:
- could not perform UPDATE_MEMBERSHIPS on role petrarca: role "poets" does not exist
Note the special sub-section cannotReconcile for operations the
database (and CloudNativePG) cannot honor, and which require human
intervention.
This section covers roles reserved for operator use and those that are not under declarative management, providing a comprehensive view of the roles in the database instances.
Important
In terms of backward compatibility, declarative role management is designed to ignore roles that exist in the database but are not included in the spec. The lifecycle of these roles will continue to be managed within PostgreSQL, allowing CloudNativePG users to adopt this feature at their convenience.