@@ -179,23 +179,74 @@ stringData:
179179 password: SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
180180` ` `
181181
182+ :::warning
183+ The example above uses `stringData:`, where Kubernetes encodes the value
184+ for you, which is the safest path for pre-hashed passwords. If you must
185+ use `data:`, encode the bytes exactly with `printf '%s' "$hash" | base64`
186+ (or `echo -n "$hash" | base64`). A trailing newline from a naive
187+ ` echo "$hash" | base64` makes the value miss the SCRAM/MD5 shadow-format
188+ check, so the operator falls back to treating it as cleartext and
189+ re-hashes it, and login stops working.
190+ :: :
191+
182192# ## Safety when transmitting cleartext passwords
183193
184- While role passwords are safely managed in Kubernetes using Secrets,
185- there is still a risk on the PostgreSQL side. If creating/altering a role with
186- password, PostgreSQL may print the password as part of the query statement
187- in some `postgres` logs, as mentioned in the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-createrole.html) :
194+ Role passwords are safely managed in Kubernetes using Secrets, but the
195+ SQL path between the operator and PostgreSQL is also a concern. As noted
196+ in the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-createrole.html) :
188197
189198> The password will be transmitted to the server in cleartext, and it might
190199> also be logged in the client's command history or the server log
191200
192- CloudNativePG adds a safety layer by temporarily suppressing both statement
193- logging (`log_statement`) and error statement logging
194- (`log_min_error_statement`) for any CREATE or ALTER operation on a role with
195- password, thus preventing leakage in both success and failure scenarios.
201+ CloudNativePG protects this path in two complementary ways :
202+
203+ 1. Before emitting `CREATE`/`ALTER ROLE ... PASSWORD '...'`, the operator
204+ SCRAM-SHA-256 encodes any cleartext password operator-side (client-side
205+ from PostgreSQL's point of view). This is the standard PostgreSQL
206+ practice for keeping cleartext out of server logs and extensions like
207+ ` pg_stat_statements` or `pgaudit`, and is the same encoding that
208+ ` psql \p assword` and libpq's `PQencryptPasswordConn` perform. The
209+ literal PostgreSQL receives is the SCRAM-SHA-256 verifier stored in
210+ ` pg_authid.rolpassword` . Passwords already provided in MD5 or
211+ SCRAM-SHA-256 shadow form are forwarded unchanged.
212+ 2. The same `CREATE`/`ALTER ROLE` statements are executed inside a
213+ transaction that temporarily suppresses both statement logging
214+ (`log_statement`) and error statement logging
215+ (`log_min_error_statement`), preventing leakage to the PostgreSQL log
216+ in both success and failure scenarios.
217+
196218The Status section of the cluster does not print the query statement for any
197219managed role operation.
198220
221+ # ### Opting out of operator-side encoding
222+
223+ If you need PostgreSQL (not the operator) to decide how the password is
224+ hashed (for example, on a cluster running `password_encryption = md5`),
225+ set the annotation `cnpg.io/passwordPassthrough : " enabled" ` on the
226+ basic-auth Secret. The operator will then forward the password value
227+ verbatim.
228+
229+ :::warning
230+ The ` cnpg.io/passwordPassthrough` annotation must be set on the
231+ **basic-auth Secret** itself, not on the `Cluster` resource. Placing it
232+ on the `Cluster` has no effect, and the operator will continue to apply
233+ SCRAM-SHA-256 encoding to the password before sending it to PostgreSQL.
234+ :: :
235+
236+ The opt-in is per-Secret and applies to every basic-auth Secret the
237+ operator consumes (managed-role secrets, but also the superuser and
238+ application-user secrets), so a single cluster can mix passthrough
239+ secrets and operator-encoded secrets freely. The statement-logging
240+ suppression layer described above still applies in both modes.
241+
242+ :::warning
243+ With `cnpg.io/passwordPassthrough : " enabled" ` , the operator forwards
244+ the Secret's ` password` value verbatim. If that value is cleartext (the
245+ common case on a `password_encryption = md5` cluster), extensions such
246+ as `pg_stat_statements` or `pgaudit` will observe it. This is the
247+ expected trade-off for letting PostgreSQL choose the hash format.
248+ :: :
249+
199250# # Unrealizable role configurations
200251
201252In PostgreSQL, in some cases, commands cannot be honored by the database and
0 commit comments