一、企业权限管理的目标

企业级权限管理不是简单地“让用户能连上数据库”,而是要回答几个问题:

1
2
3
4
5
6
7
8
谁可以登录?
谁可以连接哪个数据库?
谁可以访问哪个 schema?
谁可以读写哪些表?
谁可以修改结构?
谁可以管理权限?
谁执行过高危操作?
账号离职或下线后如何安全清理?

权限治理的目标是:

  • 满足业务访问需要
  • 遵守最小权限原则
  • 降低误操作和泄露风险
  • 方便审计
  • 方便账号生命周期管理
  • 避免权限长期失控

二、最小权限原则

最小权限原则是企业权限管理的核心。

含义是:

1
一个用户或程序只拥有完成工作所必需的最小权限。

不推荐:

1
2
GRANT ALL PRIVILEGES ON DATABASE sales_db TO app_user;
ALTER ROLE app_user SUPERUSER;

推荐:

1
2
3
4
GRANT CONNECT ON DATABASE sales_db TO app_user;
GRANT USAGE ON SCHEMA sales TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA sales TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA sales TO app_user;

如果应用不需要删数据,就不要给 DELETE

如果应用不需要建表,就不要给 schema 的 CREATE

如果报表用户只需要查询,就只给 SELECT

三、职责分离

职责分离,也就是 SoD,Separation of Duties。

不同角色承担不同职责,避免一个账号权限过大。

常见角色设计:

职责示例角色权限范围
DBA 管理dba_admin数据库管理、权限管理、运维操作
应用读写app_user业务表 CRUD
报表只读report_user查询业务数据
ETL 导入etl_user指定 schema 的写入或批量导入
备份backup_user读取备份所需数据
监控monitor_user查看统计视图、连接状态
发布迁移migration_user建表、建索引、变更结构

一个典型原则:

1
2
3
4
应用运行账号 ≠ DDL 迁移账号
只读分析账号 ≠ 业务写入账号
普通 DBA 账号 ≠ 超级用户账号
备份账号 ≠ 应用账号

这样即使某个账号泄露,影响范围也相对有限。

四、使用权限组而不是直接给用户授权

不推荐直接给每个用户授权:

1
2
3
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO john;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO sarah;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO tom;

推荐使用 NOLOGIN 角色作为权限组:

1
2
3
4
5
6
7
8
9
CREATE ROLE sales_readonly NOLOGIN;

GRANT CONNECT ON DATABASE sales_db TO sales_readonly;
GRANT USAGE ON SCHEMA sales TO sales_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO sales_readonly;

GRANT sales_readonly TO john;
GRANT sales_readonly TO sarah;
GRANT sales_readonly TO tom;

这样以后要调整只读权限,只需要调整 sales_readonly

如果某个人离职,只需要:

1
2
REVOKE sales_readonly FROM john;
DROP ROLE john;

权限模型会更加清晰。

五、推荐的角色分层模型

可以采用三层模型:

1
2
3
第一层:对象所有者
第二层:权限组角色
第三层:登录用户

示例:

1
2
3
4
5
sales_owner:拥有 sales schema 和对象,不用于业务登录
sales_readonly:只读权限组,NOLOGIN
sales_app_rw:应用读写权限组,NOLOGIN
report_user:登录用户,继承 sales_readonly
app_user:登录用户,继承 sales_app_rw

创建示例:

1
2
3
4
5
6
7
8
9
CREATE ROLE sales_owner NOLOGIN;
CREATE ROLE sales_readonly NOLOGIN;
CREATE ROLE sales_app_rw NOLOGIN;

CREATE USER report_user WITH PASSWORD 'StrongPassword';
CREATE USER app_user WITH PASSWORD 'StrongPassword';

GRANT sales_readonly TO report_user;
GRANT sales_app_rw TO app_user;

对象由 sales_owner 拥有,普通应用用户只拥有访问权限,不拥有对象本身。

这样可以避免应用用户误删、误改对象所有权相关内容。

六、对象所有者和访问者要分开

PostgreSQL 中,对象所有者天然拥有对象的管理能力。

如果应用用户就是表 owner,它可能拥有很多额外能力,例如修改表、删除表、授权他人等。

更稳妥的设计是:

1
2
owner 角色拥有对象
app_user 只被授予必要 DML 权限

例如:

1
2
CREATE ROLE sales_owner NOLOGIN;
CREATE SCHEMA sales AUTHORIZATION sales_owner;

创建对象时使用 owner 或迁移账号执行。

然后给应用权限组授权:

1
2
GRANT USAGE ON SCHEMA sales TO sales_app_rw;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA sales TO sales_app_rw;

这样应用账号可以使用对象,但不是对象所有者。

七、迁移账号和运行账号分离

很多系统使用 Flyway、Liquibase、Django migration、Rails migration 等工具自动变更数据库结构。

建议区分:

1
2
app_runtime_user:应用运行账号,只做 DML
app_migration_user:发布迁移账号,可以 DDL

运行账号:

1
2
3
GRANT CONNECT ON DATABASE sales_db TO app_runtime_user;
GRANT USAGE ON SCHEMA sales TO app_runtime_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA sales TO app_runtime_user;

迁移账号:

1
2
GRANT CONNECT ON DATABASE sales_db TO app_migration_user;
GRANT USAGE, CREATE ON SCHEMA sales TO app_migration_user;

这样应用正常运行时没有建表、改表权限。只有发布流程使用迁移账号进行结构变更。

八、账号生命周期管理

企业中账号应有生命周期:

1
2
3
4
5
6
7
8
9
申请
审批
创建
授权
使用
审计
变更
回收
删除

创建账号时建议记录:

  • 账号用途
  • 负责人
  • 所属系统
  • 授权范围
  • 有效期
  • 是否允许外部连接
  • 是否需要审计

PostgreSQL 中可以设置有效期:

1
2
3
CREATE USER report_user
WITH PASSWORD 'StrongPassword'
VALID UNTIL '2026-12-31';

限制连接数:

1
ALTER ROLE app_user CONNECTION LIMIT 50;

临时禁用登录:

1
ALTER ROLE old_user NOLOGIN;

九、删除用户的正确流程

不能简单认为:

1
DROP ROLE old_user;

一定能成功。

如果该用户拥有对象,或者仍然有权限依赖,可能会报错。

常见安全流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 1. 禁止登录
ALTER ROLE old_user NOLOGIN;

-- 2. 转移其拥有的对象
REASSIGN OWNED BY old_user TO dba_user;

-- 3. 删除其权限依赖
DROP OWNED BY old_user;

-- 4. 删除角色
DROP ROLE old_user;

含义:

1
2
3
REASSIGN OWNED:把 old_user 拥有的对象转给其他角色
DROP OWNED:删除 old_user 在当前数据库中的权限依赖
DROP ROLE:删除角色

注意:DROP OWNEDREASSIGN OWNED 作用于当前数据库。如果角色在多个数据库中拥有对象,需要分别处理。

十、权限审计

定期审计权限非常重要。

查看角色:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SELECT
    rolname,
    rolsuper,
    rolcreatedb,
    rolcreaterole,
    rolcanlogin,
    rolreplication,
    rolconnlimit,
    rolvaliduntil
FROM pg_roles
ORDER BY rolname;

查看角色成员关系:

1
2
3
4
5
6
7
8
SELECT
    r.rolname AS role_name,
    m.rolname AS member_name,
    am.admin_option
FROM pg_auth_members am
JOIN pg_roles r ON r.oid = am.roleid
JOIN pg_roles m ON m.oid = am.member
ORDER BY r.rolname, m.rolname;

查看表权限:

1
2
3
4
5
6
7
8
SELECT
    grantee,
    table_schema,
    table_name,
    privilege_type
FROM information_schema.table_privileges
WHERE grantee NOT IN ('postgres', 'PUBLIC')
ORDER BY grantee, table_schema, table_name, privilege_type;

检查超级用户:

1
2
3
SELECT rolname
FROM pg_roles
WHERE rolsuper;

检查能创建角色的用户:

1
2
3
SELECT rolname
FROM pg_roles
WHERE rolcreaterole;

这些账号都需要重点关注。

十一、监控账号设计

监控账号通常不需要业务表读写权限。

可以创建:

1
CREATE USER monitor_user WITH PASSWORD 'StrongPassword';

PostgreSQL 新版本中可以使用内置监控角色,例如:

1
GRANT pg_monitor TO monitor_user;

pg_monitor 包含一组查看系统状态的权限,适合监控工具使用。

如果只需要读取统计信息,也可以使用更细的内置角色,具体取决于版本和监控需求。

不要为了监控方便直接给:

1
SUPERUSER

十二、备份账号设计

PostgreSQL 提供了一些内置角色用于数据访问和备份场景,例如:

1
2
3
pg_read_all_data
pg_write_all_data
pg_monitor

如果备份工具需要读取全部数据,可以考虑:

1
GRANT pg_read_all_data TO backup_user;

但这个权限影响范围很大,意味着可以读取所有数据。生产环境应结合备份工具、合规要求和审计策略使用。

对于逻辑备份账号,要确认是否还需要:

1
2
3
4
5
连接数据库
读取 schema
读取表
读取序列
执行必要函数

十三、权限变更规范

企业环境中,权限变更建议遵守:

1
2
3
4
5
6
1. 不直接在线上手工随意授权
2. 授权 SQL 进入变更流程
3. 明确授权原因、对象、角色和有效期
4. 高危权限需要审批
5. 定期回收临时权限
6. 所有权限变更可追溯

高危权限包括:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
SUPERUSER
CREATEDB
CREATEROLE
REPLICATION
WITH GRANT OPTION
schema CREATE
表 DELETE / TRUNCATE
敏感函数 EXECUTE
pg_read_all_data
pg_write_all_data

十四、总结

企业级 PostgreSQL 权限设计的核心不是“把 SQL 跑通”,而是建立一套可维护的权限体系。

推荐原则:

1
2
3
4
5
6
7
8
使用 NOLOGIN 角色作为权限组
使用 LOGIN 角色作为实际账号
对象 owner 与访问用户分离
应用运行账号与迁移账号分离
只授予必要权限
默认回收 public schema 的 CREATE 权限
定期审计超级用户和高危权限
删除用户前先处理对象归属和权限依赖

一个健康的权限体系,应该让 DBA 能清楚回答:

1
2
3
4
5
6
这个账号是谁在用?
它为什么需要这些权限?
它能访问哪些数据?
它是否能修改结构?
它的权限何时过期?
它是否还能继续保留?

如果这些问题答不上来,就说明权限治理还需要继续完善。