内容提要
CloudNativePG 1.31 将为 DatabaseRole 新增密码自动生成与轮换功能。password 字段支持 generate、secret、external、setNull 四种模式;generate 模式由操作符生成密码并存入其管理的 Secret,包含 username、password、pgpass 三个键。可通过 duration 和 renewBefore 设置定期轮换,PostgreSQL 以 VALID UNTIL 强制密码有效期。支持通过注解手动触发轮换,并可自定义密码长度及数字、符号等规则。默认不轮换,因为角色仅有一个密码,切换时无重叠期。
延伸解读
密码轮换的边界与影响
文章指出,PostgreSQL 角色只有一个密码,轮换时没有重叠期,ALTER ROLE 后旧密码立即对新连接失效。但已建立的会话不受影响,应用重连时读取新密码即可。风险在于缓存密码整个进程生命周期的应用,会在下次重连时失败。因此,是否启用 duration 取决于消费者能否在重连时重新读取 Secret。
模式切换的 GitOps 注意事项
从 generate 切换到 external 或 setNull 时,操作符会删除生成的 Secret 并将密码设为 NULL;切换到 secret 时也会删除生成的 Secret,即使 password.secret 指向它。这意味着在 GitOps 工作流中,模式变更的 diff 可能导致凭据丢失。文章建议在切换前先将密码复制到自己的 Secret 中。
生成密码的约束与错误处理
密码生成器默认 24 位,包含数字和大小写字母,不含符号,以避免在连接字符串和 .pgpass 中转义问题。criteria 可自定义长度、数字和符号数量,但字符不重复(除非 allowRepeat)导致某些组合无法满足,如超过 52 个字母或超过 10 个数字。API 会在应用时拒绝明显无效的配置,生成器无法满足的组合则记录在 status.password.message 中,操作符不会无限重试。
与 External Secrets Operator 的分工
文章明确,generate 模式并不取代 External Secrets Operator(ESO)。如果组织已将凭据存放在 vault 并通过 ESO 分发,应使用 mode: secret 指向 ESO 生成的 Secret。generate 适用于凭据随角色生灭、无需外部组件的场景。两者定位不同,选择取决于是否已有外部凭据管理设施。
Q&A
CloudNativePG 1.31 中 DatabaseRole 的 password 字段支持哪几种模式?
password 字段的 mode 有四种:generate(操作符生成密码并存入其管理的 Secret)、secret(从用户提供的 Secret 读取密码,操作符不写入)、external(密码由外部管理,操作符不处理)、setNull(将密码设为 NULL,禁用密码认证)。
如何让 CloudNativePG 自动生成并定期轮换 PostgreSQL 用户密码?
在 DatabaseRole 的 password 节中设置 mode: generate,并可选设置 duration(如 720h)和 renewBefore(如 24h)来启用定期轮换。操作符会生成密码并存入名为 <角色名>-password 的 Secret,同时 PostgreSQL 通过 VALID UNTIL 强制密码有效期。
生成的密码存储在哪个 Secret 中?包含哪些内容?
操作符会创建一个类型为 kubernetes.io/basic-auth 的 Secret,名称默认为 DatabaseRole 名称加 -password 后缀(可通过 password.secret 自定义)。该 Secret 包含三个键:username(角色名)、password(生成的密码)、pgpass(可直接用于 .pgpass 文件的连接行)。
如何手动触发密码立即轮换?
可以通过注解 DatabaseRole 来触发立即轮换,例如:kubectl annotate databaserole angus cnpg.io/rotatePassword=now。操作符执行轮换后会移除该注解,并通过 PasswordRotated 或 PasswordRotationIgnored 事件告知结果。
为什么 CloudNativePG 默认不自动轮换密码?
因为 PostgreSQL 角色只有一个密码,轮换时没有重叠期,一旦执行 ALTER ROLE,旧密码立即对新连接失效。默认不轮换可避免对缓存密码的应用程序造成连接中断,但建议为会重新读取 Secret 的消费者启用 duration。
如何自定义生成密码的长度和字符规则?
可以通过 password.criteria 块自定义,例如设置 length(长度)、digits(数字个数)、symbols(符号个数)、symbolCharacters(允许的符号集)、noUpper(是否禁用大写字母)、allowRepeat(是否允许重复字符)。默认生成 24 个字符,包含数字和大小写字母,不含符号。
使用 generate 模式时,如果已存在同名 Secret 会怎样?
操作符不会覆盖或删除不属于它的 Secret。如果所选名称的 Secret 已存在,冲突会显示在 status.password.message 中,并且不会生成新密码,直到你解决冲突。
从 generate 模式切换到其他模式时,生成的 Secret 会如何处理?
切换到 external 或 setNull 时,操作符会删除生成的 Secret 并将密码设为 NULL;切换到 secret 时也会删除生成的 Secret(即使 password.secret 指向它),因为操作符会删除自己创建的资源。建议在切换前将密码复制到自己的 Secret 中。