You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3cd5af5
Browse filesBrowse the repository at this point in the historyBrowse files
authored
feat: SCRAM-SHA-256 encode role passwords operator-side (#10724)
Before emitting `CREATE`/`ALTER ROLE ... PASSWORD '...'`, the operator
now SCRAM-SHA-256 encodes any cleartext password operator-side
(client-side from PostgreSQL's point of view). The literal PostgreSQL
parses (and that extensions such as `pg_stat_statements` or `pgaudit`
may capture) is the SCRAM verifier ultimately stored in
`pg_authid.rolpassword`, never the cleartext secret. This matches what
`psql \password` and libpq's `PQencryptPasswordConn` do, and applies to
every basic-auth Secret the operator consumes: the `postgres` superuser,
the application user, and managed-role password secrets. The existing
statement-log suppression for CREATE/ALTER ROLE remains in effect.
Passwords already supplied to PostgreSQL in its MD5 or SCRAM-SHA-256
shadow form are forwarded unchanged, so users who store a pre-hashed
value in the Secret retain full control over the stored hash.
Opt-out: set the annotation `cnpg.io/passwordPassthrough: "enabled"` on
the basic-auth Secret to have the operator forward the password literal
verbatim and let PostgreSQL encode it according to its own
`password_encryption` GUC. The opt-out is per-Secret, so a single
cluster can mix passthrough and operator-encoded secrets freely,
restoring the pre-change behaviour for clusters that explicitly run with
`password_encryption = md5` or otherwise need PostgreSQL to choose the
hash format.
Signed-off-by: Leonardo Cecchi <[email protected]>
Signed-off-by: Armando Ruocco <[email protected]>
Signed-off-by: Gabriele Bartolini <[email protected]>
Signed-off-by: Marco Nenciarini <[email protected]>
Co-authored-by: Armando Ruocco <[email protected]>
Co-authored-by: Jaime Silvela <[email protected]>
Co-authored-by: Gabriele Bartolini <[email protected]>
Co-authored-by: Marco Nenciarini <[email protected]>
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
+
182
192
### Safety when transmitting cleartext passwords
183
193
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):
188
197
189
198
> The password will be transmitted to the server in cleartext, and it might
190
199
> also be logged in the client's command history or the server log
191
200
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 \password`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
+
196
218
The Status section of the cluster does not print the query statement for any
197
219
managed role operation.
198
220
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
+
199
250
## Unrealizable role configurations
200
251
201
252
In PostgreSQL, in some cases, commands cannot be honored by the database and
0 commit comments