第三方 DBA 需要查看慢查询、定位锁等待,甚至在事故中终止异常会话,但这不意味着他们必须获得 PostgreSQL 超级用户。更稳妥的方案是创建一个专用登录角色,再用 PostgreSQL 的预定义角色逐项授予监控、会话管理和维护能力。
这个模型的重点不是“尽量少给权限”,而是把工作范围翻译成可审计的数据库权限:能观察什么、能执行什么、明确不能接触什么。
为什么超级用户不是省事,而是扩大事故半径
PostgreSQL 超级用户会绕过数据库中的权限检查,影响范围也不局限于业务表。部分超级用户操作能够接触服务器文件、配置和操作系统能力。一旦凭据泄露、脚本写错,或者第三方工具执行了超出预期的命令,风险就不再只是“多看了几张表”。
典型的第三方 DBA 工作通常只需要几类能力:
- 查看活动会话、锁等待、缓冲区、复制延迟和后台进程状态。
- 取消失控查询或终止卡住的普通用户会话。
- 在约定的维护窗口触发检查点。
- 对不属于自己的表执行
VACUUM、ANALYZE或REINDEX等维护操作。
这些能力可以分别通过 pg_monitor、pg_signal_backend、pg_checkpoint 和 pg_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;
它覆盖 VACUUM、ANALYZE、REINDEX、CLUSTER 和 REFRESH MATERIALIZED VIEW 等维护操作。这里仍然需要变更控制,因为 REINDEX、CLUSTER 或物化视图刷新可能获取较强锁、消耗大量 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 账号,应该能清楚回答三件事:它能看什么、能做什么,以及合作结束时如何完整移除。