别把超级用户交出去:为第三方 DBA 设计 PostgreSQL 最小权限账号

2026-07-23 30 预计阅读时间: 1 分钟
来源: postgr.es AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

第三方 DBA 需要查看慢查询、定位锁等待,甚至在事故中终止异常会话,但这不意味着他们必须获得 PostgreSQL 超级用户。更稳妥的方案是创建一个专用登录角色,再用 PostgreSQL 的预定义角色逐项授予监控、会话管理和维护能力。

这个模型的重点不是“尽量少给权限”,而是把工作范围翻译成可审计的数据库权限:能观察什么、能执行什么、明确不能接触什么。

为什么超级用户不是省事,而是扩大事故半径

PostgreSQL 超级用户会绕过数据库中的权限检查,影响范围也不局限于业务表。部分超级用户操作能够接触服务器文件、配置和操作系统能力。一旦凭据泄露、脚本写错,或者第三方工具执行了超出预期的命令,风险就不再只是“多看了几张表”。

典型的第三方 DBA 工作通常只需要几类能力:

  • 查看活动会话、锁等待、缓冲区、复制延迟和后台进程状态。
  • 取消失控查询或终止卡住的普通用户会话。
  • 在约定的维护窗口触发检查点。
  • 对不属于自己的表执行 VACUUMANALYZEREINDEX 等维护操作。

这些能力可以分别通过 pg_monitorpg_signal_backendpg_checkpointpg_maintain 提供,无需创建一个长期存在的超级用户账号。

把权限拆成四个明确层次

只监控,不读取业务表

pg_monitor 聚合了一组监控权限,可用于查看服务器运行状态、活动查询、锁等待、缓冲区统计和复制状态:

GRANT pg_monitor TO external_dba;

它不会自动授予业务表的 SELECT 权限。不过仍要注意一个现实边界:监控视图中的 SQL 文本可能包含查询参数或直接拼接的字面量。应用应使用参数化查询,并避免把密码、令牌等秘密写进 SQL 注释或语句文本。

允许处理异常会话

只看见阻塞还不够。授予 pg_signal_backend 后,外部 DBA 可以取消查询或终止其他非超级用户的后端会话:

GRANT pg_signal_backend TO external_dba;

该角色不能终止超级用户自己的会话,因此内部 DBA 仍保留最终控制权。终止会话也可能触发事务回滚并产生额外 I/O,应该配合事故流程和审计记录使用。

按需授予检查点能力

PostgreSQL 15 引入了 pg_checkpoint,用于委派手动执行 CHECKPOINT 的能力:

GRANT pg_checkpoint TO external_dba;

手动检查点会集中写出脏页,可能造成 I/O 峰值。只有当第三方负责备份或维护流程时才应授予,不要把它并入默认监控账号。

PostgreSQL 17+ 的表维护能力

PostgreSQL 17 提供 pg_maintain,让角色可以维护不属于自己的表,而无需获得表数据的 SELECT 权限:

GRANT pg_maintain TO external_dba;

它覆盖 VACUUMANALYZEREINDEXCLUSTERREFRESH MATERIALIZED VIEW 等维护操作。这里仍然需要变更控制,因为 REINDEXCLUSTER 或物化视图刷新可能获取较强锁、消耗大量 I/O,并影响线上延迟。

一套可以直接改造的创建脚本

下面的脚本假设由内部管理员在目标数据库中执行。运行前把角色名改成实际名称;脚本先创建无特权登录角色,再授予监控与会话管理权限。密码通过 psql\password 交互设置,避免把明文密码写进 SQL 文件或 shell 历史。

-- provision_external_dba.sql
CREATE ROLE external_dba
  LOGIN
  NOSUPERUSER
  NOCREATEDB
  NOCREATEROLE
  NOREPLICATION
  NOBYPASSRLS
  CONNECTION LIMIT 5;

GRANT pg_monitor TO external_dba;
GRANT pg_signal_backend TO external_dba;

-- PostgreSQL 15+ 且合同范围需要手动检查点时启用:
-- GRANT pg_checkpoint TO external_dba;

-- PostgreSQL 17+ 且合同范围包含表维护时启用:
-- GRANT pg_maintain TO external_dba;

\password external_dba

执行方式:

psql --host=db.internal.example --username=postgres --dbname=appdb \
  --file=provision_external_dba.sql

CONNECTION LIMIT 5 可以降低外部脚本连接泄漏耗尽 max_connections 的概率,但它不是完整的资源隔离。生产环境还应考虑独立连接池、网络访问控制、TLS、登录审计,以及针对该角色设置合理的超时:

ALTER ROLE external_dba SET statement_timeout = '15min';
ALTER ROLE external_dba SET idle_in_transaction_session_timeout = '5min';

超时值必须结合实际诊断任务调整。设置得太短,会中断本来就需要较长时间的维护命令。

交付凭据前,必须做正向和反向验证

先确认角色没有危险标志:

SELECT
  rolname,
  rolsuper,
  rolcreatedb,
  rolcreaterole,
  rolreplication,
  rolbypassrls,
  rolconnlimit
FROM pg_roles
WHERE rolname = 'external_dba';

除连接上限外,相关布尔字段都应为 false。接着核对角色继承关系:

SELECT
  roleid::regrole AS granted_role,
  admin_option
FROM pg_auth_members
WHERE member = 'external_dba'::regrole
ORDER BY 1;

结果中只能出现经过批准的预定义角色,并且通常不应带有 admin_option

还要检查 PUBLIC。每个 PostgreSQL 角色天然都属于 PUBLIC,因此历史上遗留的一条公共授权可能绕过精心设计的账号权限:

SELECT table_schema, table_name
FROM information_schema.role_table_grants
WHERE grantee = 'PUBLIC'
  AND privilege_type = 'SELECT'
ORDER BY table_schema, table_name;

最后由管理员模拟该角色,确认它不能读取真实业务表。将示例表名替换为当前数据库中确实存在的应用表:

BEGIN;
SET LOCAL ROLE external_dba;
SELECT * FROM app.customer_orders LIMIT 1;
ROLLBACK;

SELECT 应返回 permission denied。如果查询成功,需要继续检查直接授权、继承角色、对象所有权、PUBLIC 权限以及视图或函数带来的间接访问路径。

明确保留给内部团队的操作

最小权限角色仍然不能替代内部平台或数据库管理员。修改 postgresql.conf、执行 ALTER SYSTEM、编辑 pg_hba.conf、重启 PostgreSQL、创建表空间、安装要求超级用户的扩展,以及部分底层复制配置,都应由内部团队控制。

确实需要第三方完成这些任务时,更合适的做法是使用有审批、有审计、限时失效的临时提权,而不是发放一个长期超级用户密码。维护结束后立即撤销权限,并复核审计日志。

上线与退出清单

交付账号前,至少确认以下事项:

  • 每名外部人员使用可追踪的独立身份,或通过受控的身份代理登录。
  • 网络入口仅允许约定来源,并强制 TLS。
  • 只授予合同范围需要的预定义角色。
  • 负向读取测试确实失败。
  • 会话终止、检查点和表维护操作有明确审批边界。
  • 凭据存放在密码管理系统中,并设置轮换和到期时间。
  • 合作结束后撤销成员关系、终止现有会话,再删除登录角色。

退出时可以这样操作:

ALTER ROLE external_dba NOLOGIN;
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE usename = 'external_dba'
  AND pid <> pg_backend_pid();

REVOKE pg_monitor, pg_signal_backend FROM external_dba;
-- 如果授予过,再执行:
-- REVOKE pg_checkpoint, pg_maintain FROM external_dba;

DROP ROLE external_dba;

如果 DROP ROLE 报告仍存在依赖,不要直接扩大清理范围。先查询并审查该角色是否意外拥有对象或获得了额外授权,再逐项撤销。一个设计良好的第三方 DBA 账号,应该能清楚回答三件事:它能看什么、能做什么,以及合作结束时如何完整移除。


相关推荐