PostgreSQL权限管理实践

基于user模型

模式和用户同名

只有一个模式

# postgres用户连接
psql -U postgres -c "create user test password 'test';"
psql -U postgres -c "create database testdb with owner=test;" 
psql -U postgres -d testdb -c "CREATE SCHEMA test AUTHORIZATION test;"
#test用户连接创建自定义对象:
psql -U test -d testdb -c "create table test_t(id int)"

这样的好处是,test用户连接testdb默认直接连接到了test模式,因为search_path默认值如下:

postgres=# select name,setting,unit,context from pg_settings where name ~* 'search_path';
    name     |     setting     | unit | context 
-------------+-----------------+------+---------
 search_path | "$user", public |      | user
(1 row)

test用户为读写用户,后期如果需要使用只读用户,直接创建用户授权即可

-- 1、创建用户及指定密码:
create user readonly with encrypted password 'readonly';
-- 2、设置用户默认事务只读:
alter user readonly set default_transaction_read_only=on;
-- 3、赋予用户连接数据库权限:
grant connect on database testdb to readonly;
-- 4、切换到指定数据库:
\c testdb
-- 5、赋予用户权限,查看public模式下所有表:
grant usage on schema public to readonly;
alter default privileges in schema public grant select on tables to readonly;
-- 6、赋予指定模式下用户表、序列查看权限:
grant usage on schema test to readonly;
grant select on all sequences in schema test to readonly;
grant select on all tables in schema test to readonly;
alter default privileges in schema test grant select on tables to readonly;

有多个模式

创建数据库

psql -U postgres -c "create user test password 'test';"
psql -U postgres -c "create database testdb with owner=test;"

创建业务用户和模式

psql -U postgres -c "create user user1 password 'user1';"
psql -U postgres -c "create user user2 password 'user2';"
psql -U postgres -d testdb -c "CREATE SCHEMA user1 AUTHORIZATION user1;"
psql -U postgres -d testdb -c "CREATE SCHEMA user2 AUTHORIZATION user2;"

给业务用户授权数据库权限

psql -U postgres -c "grant all privileges on database testdb to user1;"
psql -U postgres -c "grant all privileges on database testdb to user2;"

需要几个模式就创建几个用户,用户和模式同名,用户只允许在和自己同名的模式下创建自定义对象,可以授权其他模式的读权限

授权user2查询user1模式下的查询权限:

grant usage on schema user1 to user2;
grant select on all tables in schema user1 to user2;
alter default privileges in schema user1 grant select on tables to user2;

后期如果还需要创建只读用户,和只有一个模式创建只读用户的场景一样

模式和用户不同名

-- 创建父角色(不能登录,昨晚对象的属主)
create role test;
-- 业务用户
create user test1 password 'test1';
create user test2 password 'test2';
grant test to test1,test2;
-- 创建数据库
create database testdb with owner=test;
-- 创建模式
\c testdb
create schema test_schema authorization test;

业务用户test1连接testdb库,创建自定义对象

-- psql -U test1 -d testdb
create table test_schema.test_t(id int);

 注意此时test1用户创建的表test2用户无权访问(表的属主为test1)

可以把owner转为test用户即可

reassign owned by test1 to test;

基于role模型

权限管理设计模型

该模型简单有效,推荐绝大多数客户使用。

  • 1个RDS PostgreSQL高权限账号,具有所有权限,由少量资深DBA掌握。
  • 项目或者团队维度,1个资源owner账号,2个基本Role:{project}_role_readwrite{project}_role_readonly
  • 在Role的基础上创建业务账号:业务账号user = role + login权限
  • 1个项目或团队可以有多个Schema。权限分配尽量以Schema或Role为单位。
  • 业务表请勿放到schema public中。因为PostgreSQL默认所有用户对schema public都有CREATEUSAGE权限。

权限规划示例

本示例以项目维度进行权限管理示例,同样也适用于团队维度。

  • DBA拥有RDS PostgreSQL实例的高权限账号,名称是dbsuperuser。
  • 业务项目名称是rdspg,新建schema名称是rdspg、rdspg_1。

项目中新增的资源owner账号和Role规划如下:

user/Role schema中表权限 schema中存储过程权限
rdspg_owner (user),是唯一的项目资源owner账号 DDL:CREATE、DROP、ALTER
DQL:SELECT
DML:UPDATE、INSERT、DELETE
DDL:CREATE、DROP、ALTER
DQL:SELECT,调用存储过程
rdspg_role_readwrite (role) DQL:SELECT
DML:UPDATE、INSERT、DELETE
DQL(SELECT,调用存储过程) ,若存储过程有DDL操作,会抛出权限相关错误。
rdspg_role_readonly (role) DQL(SELECT) DQL(SELECT,调用存储过程),若存储过程有DDL或者DML操作,会抛出权限相关错误。

新增业务账号时,根据不同需求,采用如下管理模式创建:

  • rdspg_readwrite = rdspg_role_readwrite + login权限
  • rdspg_readonly = rdspg_role_readonly + login权限

配置步骤:

1、创建项目资源owner账号rdspg_owner和项目Role。DBA使用dbsuperuser高权限账号执行如下操作。

-- rdspg_owner 是项目管理账号
CREATE USER rdspg_owner WITH LOGIN PASSWORD 'asdfy181BASDfadasdbfas';
-- 创建业务数据库
create database testdb owner=rdspg_owner;
\c testdb

CREATE ROLE rdspg_role_readwrite;
CREATE ROLE rdspg_role_readonly;

-- 设置: 对于rdspg_owner 创建的表,rdspg_role_readwrite 有 DQL(SELECT)、DML(UPDATE、INSERT、DELETE)权限。
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON TABLES TO rdspg_role_readwrite;

-- 设置: 对于rdspg_owner 创建的SEQUENCES,rdspg_role_readwrite 有 DQL(SELECT)、DML(UPDATE、INSERT、DELETE)权限。
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON SEQUENCES TO rdspg_role_readwrite;

-- 设置: 对于 rdspg_owner 创建的表, rdspg_role_readonly 只有 DQL(SELECT)权限。
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT SELECT ON TABLES TO rdspg_role_readonly;

2、创建rdspg_readwrite、rdspg_readonly业务账号。DBA使用dbsuperuser高权限账号执行如下操作。

-- rdspg_readwrite只有 DQL(SELECT)、DML(UPDATE、INSERT、DELETE)权限。
CREATE USER rdspg_readwrite WITH LOGIN PASSWORD 'dfandfnapSDhf23hbEfabf';
GRANT rdspg_role_readwrite TO rdspg_readwrite;

-- rdspg_readonly只有 DQL(SELECT)权限。
CREATE USER rdspg_readonly WITH LOGIN PASSWORD 'F89h912badSHfadsd01zlk';
GRANT rdspg_role_readonly TO rdspg_readonly;

3、创建schema rdspg,并授权给项目Role。DBA使用dbsuperuser高权限账号执行如下操作。

-- schema rdspg的owner是 rdspg_owner账号
CREATE SCHEMA rdspg AUTHORIZATION rdspg_owner;

-- 授权ROLE相关SCHEMA访问权限。
GRANT USAGE ON SCHEMA rdspg TO rdspg_role_readwrite;
GRANT USAGE ON SCHEMA rdspg TO rdspg_role_readonly;

**说明: **rdspg_readwrite和rdspg_readonly自动继承了相关Role的权限变更,不需要再额外操作。

应用场景示例

场景1:使用rdspg_owner账号:对schema rdspg中的表进行DDL(CREATE、DROP、ALTER)操作

\c testdb rdspg_owner
CREATE TABLE rdspg.test(id bigserial primary key, name text);
CREATE INDEX idx_test_name on rdspg.test(name);

场景2:使用 rdspg_readwrite/rdspg_readonly 账号进行业务开发

业务开发遵循最小权限原则,尽量使用rdspg_readonly账号,需要DML操作的地方才使用rdspg_readwrite账号。这样也方便在业务层做读写分离

说明:

  • 业务层做读写分离,避免了自动读写分离中间件proxy带来的额外成本和性能损耗。
  • 即使目前还没有使用只读实例,也建议区分 readonly客户端、readwrite客户端,为使用只读实例做准备。readonly客户端建议使用readonly账号,最小权限原则,规避权限误用。
  • readonly客户端,使用readonly账号,设置JDBC URL:只读实例1地址,只读实例2地址,读写实例地址
  • readwrite客户端,使用readwrite账号,设置JDBC URL:读写实例地址

使用rdspg_readwrite账号,对schema rdspg中的表进行DQL(SELECT)、DML(UPDATE、INSERT、DELETE)操作:

\c testdb rdspg_readwrite
INSERT INTO rdspg.test (name) VALUES('name0'),('name1');
SELECT id,name FROM rdspg.test LIMIT 1;

-- rdspg_readwrite没有 DDL(CREATE、DROP、ALTER)权限
CREATE TABLE rdspg.test2(id int);
ERROR:  permission denied for schema rdspg
LINE 1: create table rdspg.test2(id int);

DROP TABLE rdspg.test;
ERROR:  must be owner of table test

ALTER TABLE rdspg.test ADD id2 int;
ERROR:  must be owner of table test

CREATE INDEX idx_test_name on rdspg.test(name);
ERROR:  must be owner of table test

使用rdspg_readonly账号,对schema rdspg中的表进行DQL(SELECT)操作:

\c testdb rdspg_readonly
INSERT INTO rdspg.test (name) VALUES('name0'),('name1');
ERROR:  permission denied for table test

SELECT id,name FROM rdspg.test LIMIT 1;
 id | name
----+-------
  1 | name0
(1 row)

场景3:不同项目交叉授权

有个新项目employee,创建role和user以及授权如下:

\c testdb dbsuperuser
CREATE ROLE employee_role_readwrite;
CREATE ROLE employee_role_readonly;

ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON TABLES TO employee_role_readwrite;
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT ALL ON SEQUENCES TO employee_role_readwrite;
ALTER DEFAULT PRIVILEGES FOR ROLE rdspg_owner GRANT SELECT ON TABLES TO employee_role_readonly;

CREATE USER employee_readwrite WITH LOGIN PASSWORD 'dfandfnapSDhf23hbEfabf';
GRANT employee_role_readwrite TO employee_readwrite;
CREATE USER employee_readonly WITH LOGIN PASSWORD 'F89h912badSHfadsd01zlk';
GRANT employee_role_readonly TO employee_readonly;

现在需要为账号employee_readwrite增加rdspg项目的表只读权限。DBA使用dbsuperuser高权限账号做如下操作:

-- 给账号 employee_readwrite 加上 rdspg_role_readonly 权限集合。
\c testdb dbsuperuser
GRANT rdspg_role_readonly TO employee_readwrite;

场景4:项目新增 schema rdspg_1,并授权给项目Role

rdspg_readwrite、rdspg_readonly、employee_readwrite账号自动继承了相关Role的权限变更,不需要再额外操作。DBA使用dbsuperuser 高权限账号做如下操作:

\c testdb dbsuperuser
CREATE SCHEMA rdspg_1 AUTHORIZATION rdspg_owner;

-- 授权ROLE相关SCHEMA访问权限。
-- CREATE 使得 rdspg_owner 对schema rdspg_1中的表有 DDL(CREATE、DROP、ALTER)权限。
GRANT USAGE ON SCHEMA rdspg_1 TO rdspg_role_readwrite;
GRANT USAGE ON SCHEMA rdspg_1 TO rdspg_role_readonly;

账号权限查询

psql客户端,使用元命令\du查看所有用户和拥有的角色:

image-20251126230011754

从上述查询结果示例中可以看出:employee_readwrite账号的Member of列中,内容为rdspg_role_readonly,employee_role_readwrite,因此,此账号对employee项目表具有DQL和DML权限,对rdspg项目表具有DQL权限。

使用SQL查询

SELECT r.rolname, r.rolsuper, r.rolinherit,
  r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,
  r.rolconnlimit, r.rolvaliduntil,
  ARRAY(SELECT b.rolname
        FROM pg_catalog.pg_auth_members m
        JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)
        WHERE m.member = r.oid) as memberof
, r.rolreplication
, r.rolbypassrls
FROM pg_catalog.pg_roles r
WHERE r.rolname !~ '^pg_'
ORDER BY 1;

参考资料

https://help.aliyun.com/zh/rds/apsaradb-rds-for-postgresql/manage-permissions-in-an-apsaradb-rds-for-postgesql-instance

https://support.huaweicloud.com/bestpractice-rds-pg/rds_pg_0035.html

posted @ 2025-11-26 23:06  kahnyao  阅读(10)  评论(0)    收藏  举报