本文主要综合测试用户和角色,包括role和user的区别,如何创建不用的用户,如何删除用户,并且测试了对象权限和系统权限的继承方式不同。包括在云平台的RDS上的用户方式建议,方便大家针对用户进行全面的理解
Table of Contents
- 1 用户和角色
1 用户和角色
1.1 create user 和create role的区别
- 在postgresql中,角色和用户是没有区别的,一个用户也是一个角色,可以把一个用户的权限赋予另一个用户;
- 用户和角色是全局的,在不同的数据库上查看,看到的用户是相同
- create user默认创建的用户具有login权限,create role创建的用户没有login权限
根据角色自身的设置不同,一个角色可以看做是一个数据库用户,或者一组数据库用户。
这里做个创建用户和角色的测试。
postgres@[local]:5432=#create user usertest; CREATE ROLE postgres@[local]:5432=#create role roletest; CREATE ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- ostest | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} roletest | Cannot login | {} usertest | | {} yanwei | Superuser, Create role, Create DB | {} 创建的role默认是不带login权限。无法登陆数据库。但是可以通过创建时候指定login权限,或者通过alter role调整login; postgres@[local]:5432=#alter role roletest login; ALTER ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- ostest | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} roletest | | {} usertest | | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#
复制
1.2 创建用户和角色语法
CREATE USER/ROLE name [ [ WITH ] option [ ... ] ] : 关键词 USER,ROLE; name 用户或角色名; where option can be: SUPERUSER | NOSUPERUSER :超级权限,拥有所有权限,默认nosuperuser。 | CREATEDB | NOCREATEDB :建库权限,默认nocreatedb。 | CREATEROLE | NOCREATEROLE :建角色权限,拥有创建、修改、删除角色,默认nocreaterole。 | INHERIT | NOINHERIT :继承权限,可以把除superuser权限继承给其他用户/角色,默认inherit。 | LOGIN | NOLOGIN :登录权限,作为连接的用户,默认nologin,除非是create user(默认登录)。 | REPLICATION | NOREPLICATION :复制权限,用于物理或则逻辑复制(复制和删除slots),默认是noreplication。 | BYPASSRLS | NOBYPASSRLS :安全策略RLS权限,默认nobypassrls。 | CONNECTION LIMIT connlimit :限制用户并发数,默认-1,不限制。正常连接会受限制,后台连接和prepared事务不受限制。 | [ ENCRYPTED ] PASSWORD 'password' | PASSWORD NULL :设置密码,密码仅用于有login属性的用户,不使用密码身份验证,则可以省略此选项。可以选择将空密码显式写为PASSWORD NULL。 加密方法由配置参数password_encryption确定,密码始终以加密方式存储在系统目录中。 | VALID UNTIL 'timestamp' :密码有效期时间,不设置则用不失效。 | IN ROLE role_name [, ...] :新角色将立即添加为新成员。 | IN GROUP role_name [, ...] :同上 | ROLE role_name [, ...] :ROLE子句列出一个或多个现有角色,这些角色自动添加为新角色的成员。 (这实际上使新角色成为“组”)。 | ADMIN role_name [, ...] :与ROLE类似,但命名角色将添加到新角色WITH ADMIN OPTION,使他们有权将此角色的成员资格授予其他人。 | USER role_name [, ...] :同上 | SYSID uid :被忽略,但是为向后兼容性而存在。
复制
1.3 创建用户实例
1.3.1 创建没有密码的账号
postgres@[local]:5432=#create role rolenopasswd; CREATE ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of --------------+------------------------------------------------------------+----------- ostest | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolenopasswd | Cannot login | {} roletest | | {} usertest | | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#alter user rolenopasswd login; ALTER ROLE --可以通过alter user命令调整login属性 验证密码: postgres@[local]:5432=#SELECT rolname,rolpassword FROM pg_authid WHERE rolname ~ 'role'; rolname | rolpassword --------------+------------- roletest | rolenopasswd | (2 rows)
复制
创建该用户后,还不能直接登录。需要修改 pg_hba.conf 文件(后面会对该文件进行说明),加入:
①:本地登陆:local all all trust
②:远程登陆:host all all 192.168.163.132/32 trust
添加密码方式
postgres@[local]:5432=#alter user rolenopasswd password 'yanwei'; ALTER ROLE postgres@[local]:5432=#SELECT rolname,rolpassword FROM pg_authid WHERE rolname ~ 'role'; rolname | rolpassword --------------+---------------------------------------------------------------------------------------------------------------------------------- ----- roletest | rolenopasswd | SCRAM-SHA-256$4096:9+jpBeOy71n1z3HwQVTnFQ==$gEUpC/dU7ayMYWuJpFTseN1fBVwNP517Ei7/tqJt3gQ=:GSmYCj3Ov7Gca71vk/W8+D5YTeXVfn2bJ39ZkZPB XY8= (2 rows) postgres@[local]:5432=#alter user rolenopasswd encrypted password 'yanwei1'; ALTER ROLE postgres@[local]:5432=#SELECT rolname,rolpassword FROM pg_authid WHERE rolname ~ 'role'; rolname | rolpassword --------------+---------------------------------------------------------------------------------------------------------------------------------- ----- roletest | rolenopasswd | SCRAM-SHA-256$4096:BrHwG1shRk1PWZGYTanCuw==$uH327xH1TXi31ofVxHAYhO+mrmexhc2viGNIBIeF0MQ=:Cz+Jz4q9TMX6K89hKlGrkumW49yLUhT4RagJw6jY ccU= (2 rows) postgres@[local]:5432=# postgres@[local]:5432=#show password_encryption postgres-# ; password_encryption --------------------- scram-sha-256 (1 row)
复制
口令总是以加密的方式存放在系统目录中。ENCRYPTED关键词没有实际效果,它只是为了向后兼容性而存在。加密的方法由配置参数password_encryption决定。如果当前的口令字符串已经是MD5加密或者SCRAM加密的格式,那么不管password_encryption的值是什么,口令字符串还是原样存储(因为系统无法解密以不同格式加密的口令字符串)。
1.3.2 创建密码的账号
postgres@[local]:5432=#create user userpasswd with password 'yanwei'; CREATE ROLE postgres@[local]:5432=#
复制
如果userpasswd远程数据库也是需要需要修改 pg_hba.conf 文件(后面会对该文件进行说明),加入:
host all all 192.168.245.119/32 md5
1.3.3 创建有时间限制的用户
postgres@[local]:5432=#create user userwithtime with password 'yanwei' VALID UNTIL '2022-01-01'; CREATE ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Me mber of --------------+------------------------------------------------------------+--- -------- ostest | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {} userwithtime | Password valid until 2022-01-01 00:00:00+08 | {} yanwei | Superuser, Create role, Create DB
复制
连接方式和1.3.2是一致,只是到具体的时间后就不可以连接了;密码到期后, 将无法认证通过,此处必须在客户端去访问数据库服务端验证密码有效期,如果是服务端用户访问不受有效期限制
那么如何将此用户修改永不过期呢
postgres@[local]:5432=#select * from pg_user; usename | usesysid | usecreatedb | usesuper | userepl | usebypassrls | passwd | valuntil | useconfig --------------+----------+-------------+----------+---------+--------------+----------+------------------------+----------- postgres | 10 | t | t | t | t | ******** | | pguser | 16387 | t | t | t | f | ******** | | yanwei | 16432 | t | t | f | f | ******** | | ostest | 16456 | f | f | f | f | ******** | | usertest | 16457 | f | f | f | f | ******** | | roletest | 16458 | f | f | f | f | ******** | | userpasswd | 16461 | f | f | f | f | ******** | | userwithtime | 16462 | f | f | f | f | ******** | 2022-01-01 00:00:00+08 | (8 rows) postgres@[local]:5432=#alter user userwithtime with valid until 'infinity'; ALTER ROLE postgres@[local]:5432=#select * from pg_user; usename | usesysid | usecreatedb | usesuper | userepl | usebypassrls | passwd | valuntil | useconfig --------------+----------+-------------+----------+---------+--------------+----------+----------+----------- postgres | 10 | t | t | t | t | ******** | | pguser | 16387 | t | t | t | f | ******** | | yanwei | 16432 | t | t | f | f | ******** | | ostest | 16456 | f | f | f | f | ******** | | usertest | 16457 | f | f | f | f | ******** | | roletest | 16458 | f | f | f | f | ******** | | userpasswd | 16461 | f | f | f | f | ******** | | userwithtime | 16462 | f | f | f | f | ******** | infinity |
复制
1.3.4 创建管理员账号、有创建db和创建用户权限、复制账号
-
创建有创建数据库和管理角色权限的用户admin:
postgres=# CREATE ROLE admin WITH CREATEDB CREATEROLE; CREATE ROLE
复制注意:拥有创建数据库,角色的用户,也可以删除和修改这些对象。
-
创建具有超级权限的用户:admin
postgres=# CREATE ROLE admin WITH SUPERUSER LOGIN PASSWORD 'admin'; CREATE ROLE
复制 -
创建复制账号:repl
postgres=# CREATE USER repl REPLICATION LOGIN ENCRYPTED PASSWORD 'repl'; CREATE ROLE
复制
1.4 alter user 和 alter role
1.4.1 语法
postgres@[local]:5432=#\h alter user Command: ALTER USER Description: change a database role Syntax: ALTER USER role_specification [ WITH ] option [ ... ] where option can be: SUPERUSER | NOSUPERUSER | CREATEDB | NOCREATEDB | CREATEROLE | NOCREATEROLE | INHERIT | NOINHERIT | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION | BYPASSRLS | NOBYPASSRLS | CONNECTION LIMIT connlimit | [ ENCRYPTED ] PASSWORD 'password' | PASSWORD NULL | VALID UNTIL 'timestamp' ALTER USER name RENAME TO new_name ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter { TO | = } { value | DEFAULT } ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter FROM CURRENT ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] RESET configuration_parameter ALTER USER { role_specification | ALL } [ IN DATABASE database_name ] RESET ALL where role_specification can be: role_name | CURRENT_USER | SESSION_USER URL: https://www.postgresql.org/docs/12/sql-alteruser.html postgres@[local]:5432=#\h alter role Command: ALTER ROLE Description: change a database role Syntax: ALTER ROLE role_specification [ WITH ] option [ ... ] where option can be: SUPERUSER | NOSUPERUSER | CREATEDB | NOCREATEDB | CREATEROLE | NOCREATEROLE | INHERIT | NOINHERIT | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION | BYPASSRLS | NOBYPASSRLS | CONNECTION LIMIT connlimit | [ ENCRYPTED ] PASSWORD 'password' | PASSWORD NULL | VALID UNTIL 'timestamp' ALTER ROLE name RENAME TO new_name ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter { TO | = } { value | DEFAULT } ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter FROM CURRENT ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] RESET configuration_parameter ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] RESET ALL where role_specification can be: role_name | CURRENT_USER | SESSION_USER URL: https://www.postgresql.org/docs/12/sql-alterrole.html postgres@[local]:5432=#
复制
语法是一样的
1.4.2 官方文档示例
更改一个角色的口令: ALTER ROLE davide WITH PASSWORD 'hu8jmn3'; 移除一个角色的口令: ALTER ROLE davide WITH PASSWORD NULL; 更改一个口令的失效日期,指定该口令应该在 2015 年 5 月 4 日中午 (在一个比UTC快 1 小时的时区)过期: ALTER ROLE chris VALID UNTIL 'May 4 12:00:00 2015 +1'; 让一个口令永远有效: ALTER ROLE fred VALID UNTIL 'infinity'; 让一个角色能够创建其他角色和新的数据库: ALTER ROLE miriam CREATEROLE CREATEDB; 为一个角色指定 maintenance_work_mem参数的非默认设置: ALTER ROLE worker_bee SET maintenance_work_mem = 100000; 为一个角色指定 client_min_messages参数的数据库相关的非 默认设置: ALTER ROLE fred IN DATABASE devel SET client_min_messages = DEBUG;
复制
1.5 删除用户
postgres@[local]:5432=#\h drop user Command: DROP USER Description: remove a database role Syntax: DROP USER [ IF EXISTS ] name [, ...]
复制
注意:DROP ROLE
移除指定的角色。要删除一个 超级用户角色,你必须自己就是一个超级用户。要删除一个非超级用户角 色,你必须具有CREATEROLE
特权。
1.5.1 REASSIGN OWNED和DROP OWNED
在pg中删除账号时,一般不能直接删除账号,要先将该账号上所有的对应权限收回,但往往这一步是比较繁琐的,可能当时赋权的对象类型很多,对象也比较多,虽然可以通过sql按照类型来收回针对整个schema的所有权限,但还是有可能漏掉的,可能有使用ALTER DEFAULT PRIVILEGES定义的默认权限。 那么如何快速删除角色和帐号呢,官方给的解决方案: 可以结合REASSIGN OWNED BY old_user TO other_user;和DROP OWNED BY ole_user;来实现。 REASSIGN OWNED BY old_user TO other_user; 语句是将old_user拥有owner权限的对象转移给other_user,那么old_user将不会再拥有owner权限的对象了。 DROP OWNED BY ole_user; 语句是在当前库中,删除用户拥有的对象;并收回在当前库赋予的任何权限。 REASSIGN OWNED BY old_user TO postgres; DROP OWNED BY ole_user; DROP USER old_user;
复制
测试过程:
postgres@[local]:5432=#grant all on test to ostest; GRANT postgres@[local]:5432=#select * from information_schema.table_privileges where grantee='ostest'; grantor | grantee | table_catalog | table_schema | table_name | privilege_type | is_grantable | with_hierarchy ----------+---------+---------------+--------------+------------+----------------+--------------+---------------- postgres | ostest | postgres | public | test | INSERT | NO | NO postgres | ostest | postgres | public | test | SELECT | NO | YES postgres | ostest | postgres | public | test | UPDATE | NO | NO postgres | ostest | postgres | public | test | DELETE | NO | NO postgres | ostest | postgres | public | test | TRUNCATE | NO | NO postgres | ostest | postgres | public | test | REFERENCES | NO | NO postgres | ostest | postgres | public | test | TRIGGER | NO | NO (7 rows) postgres@[local]:5432=#drop role ostest; ERROR: role "ostest" cannot be dropped because some objects depend on it DETAIL: privileges for table test postgres@[local]:5432=# List of roles Role name | Attributes | Member of --------------+------------------------------------------------------------+----------- ostest | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {} REASSIGN OWNED BY ostest TO postgres; DROP OWNED BY ostest; DROP USER ostest; postgres@[local]:5432=#REASSIGN OWNED BY ostest TO postgres; REASSIGN OWNED postgres@[local]:5432=#DROP OWNED BY ostest; DROP OWNED postgres@[local]:5432=#DROP USER ostest; DROP ROLE postgres@[local]:5432=# 这里就可以删除了
复制
1.6 系统默认角色
PostgreSQL提供了一组默认角色,它们提供对特定的、通常需要的、需要特权的功能和信息的访问。管理员可以把这些角色GRANT给其环境中的用户或者其他角色,让这些用户能够访问指定的功能和信息。
表 21.1. 默认角色
角色 | 允许的访问 |
---|---|
pg_read_all_settings | 读取所有配置变量,甚至是那些通常只对超级用户可见的变量。 |
pg_read_all_stats | 读取所有的pg_stat_*视图并且使用与扩展相关的各种统计信息,甚至是那些通常只对超级用户可见的信息。 |
pg_stat_scan_tables | 执行可能会在表上取得ACCESS SHARE 锁的监控函数(可能会持锁很长时间)。 |
pg_monitor | 读取/执行各种不同的监控视图和函数。 这角色是pg_read_all_settings ,pg_read_all_stats 和pg_stat_scan_tables 的成员。 |
pg_signal_backend | 发信号到其他后端亿取消查询或中止它的会话。 |
pg_read_server_files | 允许使用COPY以及其他文件访问函数从服务器上该数据库可访问的任意位置读取文件。 |
pg_write_server_files | 允许使用COPY以及其他文件访问函数在服务器上该数据库可访问的任意位置中写入文件。 |
pg_execute_server_program | 允许用运行该数据库的用户执行数据库服务器上的程序来配合COPY和其他允许执行服务器端程序的函数。 |
pg_monitor
、pg_read_all_settings
、pg_read_all_stats
和pg_stat_scan_tables
角色的目的是允许管理员能为监控数据库服务器的目的很容易地配置角色。它们授予一组常用的特权,这些特权允许角色读取各种有用的配置设置、统计信息以及通常仅限于超级用户的其他系统信息。
pg_signal_backend
角色想要允许管理员启用受信任的、但是非超级用户的、发送信号给其他后端的角色。 当前,此角色允许发送信号以取消另一个后端上的查询或终止其会话。不过授予此角色的用户不能向属于超级用户的后端发送信号。
pg_read_server_files
、pg_write_server_files
以及pg_execute_server_program
角色的目的是允许管理员有一些可信但不是超级用户的角色来访问文件以及以运行数据库的用户在数据库服务器上运行程序。 由于这些角色能够访问服务器文件系统上的任何文件,因此在直接访问文件时它们会绕过任何数据库级别的权限检查并且它们可以被用来得到超级用户级别的访问,因此在把这些角色授予给用户时应当特别小心。
1.7 用户组和继承测试
在 postgres 8.0 版本之后,用户(user)和用户组(group)的生成被 create role
语句统一起来了, 但是为了兼容,也保留了 create user
和 create group
语句。
role membership(role成员)为了管理上的方便,我们可以创建一个role group,然后可以将各用户或者有特殊权限的role组织在一起,各个role就是这个role group的membership.
role group 是不带login的role,因为pg使用role来表示所有的角色、用户、用户组,所以不要混淆,创建语句都是create role.
postgres@[local]:5432=#\h create group Command: CREATE GROUP Description: define a new database role Syntax: CREATE GROUP name [ [ WITH ] option [ ... ] ] where option can be: SUPERUSER | NOSUPERUSER | CREATEDB | NOCREATEDB | CREATEROLE | NOCREATEROLE | INHERIT | NOINHERIT | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION | BYPASSRLS | NOBYPASSRLS | CONNECTION LIMIT connlimit | [ ENCRYPTED ] PASSWORD 'password' | PASSWORD NULL | VALID UNTIL 'timestamp' | IN ROLE role_name [, ...] | IN GROUP role_name [, ...] | ROLE role_name [, ...] | ADMIN role_name [, ...] | USER role_name [, ...] | SYSID uid URL: https://www.postgresql.org/docs/12/sql-creategroup.html 语法和create user是一致的 IN ROLE role_name IN ROLE子句列出一个或多个现有的角色,新角色将被立即作为新成员加入到这些角色中(注意没有选项可以把新角色作为一个管理员加入,需要用一个单独的GRANT命令来完成)。 IN GROUP role_name IN GROUP是IN ROLE的一种已废弃的拼写方式。 ROLE role_name ROLE子句列出一个或者多个现有角色,它们会被自动作为成员加入到新角色中(这实际上新角色变成了一个“组”)。 ADMIN role_name ADMIN子句与ROLE相似,但是被提及的角色被使用WITH ADMIN OPTION加入到新角色中,让它们能够把这个角色中的成员关系授予给其他人。 USER role_name USER子句是ROLE子句的一个已废弃的拼写方式。 所有主要使用的方式是 IN ROLE ROLE role_name ADMIN role_name
复制
用户组通常是那些没有 login 权限,但是有其他 role 作为成员的 role, 当然,这只是一个约定,不是强制的,你也可以创建一个既能登录也包含其他 role 成员的用户组。
# 生成一个group role create role fugroup inherit; # 添加其他用户或组 grant fugroup to yanwei; List of roles Role name | Attributes | Member of --------------+------------------------------------------------------------+----------- fugroup | Cannot login | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {fugroup} #create role测试 postgres@[local]:5432=#create role rolegrouptest in role yanwei,pguser; CREATE ROLE postgres@[local]:5432=#/du postgres-# ^C postgres@[local]:5432=#\du ********* QUERY ********** 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; ************************** List of roles Role name | Attributes | Member of ---------------+------------------------------------------------------------+----------------- fugroup | Cannot login | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolegrouptest | Cannot login | {pguser,yanwei} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {fugroup} postgres@[local]:5432=#create role rolegrouptest2 admin yanwei,pguser; CREATE ROLE postgres@[local]:5432=#/du postgres-# ^C postgres@[local]:5432=#\du ********* QUERY ********** 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; ************************** List of roles Role name | Attributes | Member of ----------------+------------------------------------------------------------+-------------------------- fugroup | Cannot login | {} pguser | Superuser, Create role, Create DB, Replication | {rolegrouptest2} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolegrouptest | Cannot login | {pguser,yanwei} rolegrouptest2 | Cannot login | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {fugroup,rolegrouptest2} #授权测试 postgres@[local]:5432=#grant rolegrouptest2 to usertest; GRANT ROLE postgres@[local]:5432=#/du postgres-# ^C postgres@[local]:5432=#\du ********* QUERY ********** 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; ************************** List of roles Role name | Attributes | Member of ----------------+------------------------------------------------------------+-------------------------- fugroup | Cannot login | {} pguser | Superuser, Create role, Create DB, Replication | {rolegrouptest2} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolegrouptest | Cannot login | {pguser,yanwei} rolegrouptest2 | Cannot login | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {rolegrouptest2} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {fugroup,rolegrouptest2} List of roles Role name | Attributes | Member of ----------------+------------------------------------------------------------+-------------------------- fugroup | Cannot login | {} pguser | Superuser, Create role, Create DB, Replication | {rolegrouptest2} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolegrouptest | Cannot login | {pguser,yanwei} rolegrouptest2 | Cannot login | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {rolegrouptest2} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {fugroup,rolegrouptest2} postgres@[local]:5432=#revoke rolegrouptest2 from usertest; REVOKE ROLE postgres@[local]:5432=#\du ********* QUERY ********** 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; ************************** List of roles Role name | Attributes | Member of ----------------+------------------------------------------------------------+-------------------------- fugroup | Cannot login | {} pguser | Superuser, Create role, Create DB, Replication | {rolegrouptest2} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} rolegrouptest | Cannot login | {pguser,yanwei} rolegrouptest2 | Cannot login | {} rolenopasswd | Cannot login | {} roletest | | {} userpasswd | | {} usertest | | {} userwithtime | Password valid until infinity | {} yanwei | Superuser, Create role, Create DB | {fugroup,rolegrouptest2} postgres@[local]:5432=#
复制
1.7.1 等保要求的测试
等保要求应对登录的用户分配账户和权限
在postgresqll数据库中,认为用户就是一个相应的role,PostgreSQL使用角色的概念管理数据库访问权限。一个角色可以被看成是一个数据库用户或者是一个数据库用户组,这取决于角色被怎样设置。角色可以拥有数据库对象(例如,表和函数)并且能够把那些对象上的权限赋予给其他角色来控制谁能访问哪些对象。此外,还可以把一个角色中的成员资格授予给另一个角色,这样允许成员角色使用被赋予给另一个角色的权限。
继承group
如果新的角色是其他角色的成员,这些子句决定新角色是否从那些角色中“继承”特权,把新角色作为成员的角色称为新角色的父角色。一个带有INHERIT属性的角色能够自动使用已经被授予给其直接或间接父角色的任何数据库特权。如果没有INHERIT,在另一个角色中的成员关系只会把SET ROLE的能力授予给那个其他角色,只有在这样做后那个其他角色的特权才可用。如果没有指定,默认值是INHERIT。
1)直接继承
我们创建一个用户jack,两个角色 r1 r2;
postgres@[local]:5432=#create role jack login inherit; CREATE ROLE postgres@[local]:5432=#create role r1; CREATE ROLE postgres@[local]:5432=#create role r2; CREATE ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | Cannot login | {} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {}
复制
创建对应的表,授予相关的权限;tab1 - r1;tab2 -r2;tab3-jack
postgres@[local]:5432=#create table tab1 (id text); CREATE TABLE postgres@[local]:5432=#create table tab2 (id text); CREATE TABLE postgres@[local]:5432=#create table tab3 (id text); CREATE TABLE postgres@[local]:5432=#grant select on tab1 to r1; GRANT postgres@[local]:5432=#grant select on tab2 to r2; GRANT postgres@[local]:5432=#grant select on tab3 to jack; GRANT postgres@[local]:5432=#\d List of relations Schema | Name | Type | Owner --------+-----------+-------+---------- public | heap_t | table | postgres public | tab1 | table | postgres public | tab2 | table | postgres public | tab3 | table | postgres public | tbl_david | table | postgres public | test | table | postgres public | v_data | view | postgres (7 rows) postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | Cannot login | {} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#
复制
进行grant授权,使jack成为r1,r2的membership
postgres@[local]:5432=#grant r1 to jack ; GRANT ROLE postgres@[local]:5432=#grant r2 to jack ; GRANT ROLE postgres@[local]:5432=#/du postgres-# ^C postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {r1,r2} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | Cannot login | {} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#
复制
测试角色切换
jack 继承了r1,r2的权限,测试查询权限,结果是可以访问的,因为inherit是自动继承关系
postgres@[local]:5432=#\c - jack You are now connected to database "postgres" as user "jack". postgres@[local]:5432=>select * from tab1; id ---- (0 rows) postgres@[local]:5432=>select * from tab2; id ---- (0 rows) postgres@[local]:5432=>select * from tab3; id ---- (0 rows) postgres@[local]:5432=>
复制
2)间接继承
移除membership,此时,r1属于r2,然后jack又属于r1
postgres@[local]:5432=>\c - postgres You are now connected to database "postgres" as user "postgres". postgres@[local]:5432=# postgres@[local]:5432=#revoke r2 from jack; REVOKE ROLE postgres@[local]:5432=#grant r2 to r1; GRANT ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {r1} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | Cannot login | {r2} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#
复制
此时连接jack用户去测试r1,r2,r3访问
postgres@[local]:5432=#\c - jack You are now connected to database "postgres" as user "jack". postgres@[local]:5432=>select * from tab1; --因为jack是r1 group的member因此可以访问 id ---- (0 rows) postgres@[local]:5432=>select * from tab2; --因为r1同时又是r2的member,因此间接继承了访问权限 id ---- (0 rows) postgres@[local]:5432=>select * from tab3;--自己的权限 id ---- (0 rows) postgres@[local]:5432=> postgres@[local]:5432=>select * from information_schema.table_privileges where grantee in ('jack','r1','r2'); grantor | grantee | table_catalog | table_schema | table_name | privilege_type | is_grantable | with_hierarchy ----------+---------+---------------+--------------+------------+----------------+--------------+---------------- postgres | r1 | postgres | public | tab1 | SELECT | NO | YES postgres | r2 | postgres | public | tab2 | SELECT | NO | YES postgres | jack | postgres | public | tab3 | SELECT | NO | YES (3 rows) postgres@[local]:5432=>
复制
关闭r1的继承,alter role r1 noinherit;测试jack是不能访问到tab2的
postgres@[local]:5432=>\c - postgres You are now connected to database "postgres" as user "postgres". postgres@[local]:5432=#alter role r1 noinherit; ALTER ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {r1} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | No inheritance, Cannot login | {r2} --这里是非继承关系 r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#select * from information_schema.table_privileges where grantee in ('jack','r1','r2'); grantor | grantee | table_catalog | table_schema | table_name | privilege_type | is_grantable | with_hierarchy ----------+---------+---------------+--------------+------------+----------------+--------------+---------------- postgres | r1 | postgres | public | tab1 | SELECT | NO | YES postgres | r2 | postgres | public | tab2 | SELECT | NO | YES postgres | jack | postgres | public | tab3 | SELECT | NO | YES (3 rows) postgres@[local]:5432=#\c - jack You are now connected to database "postgres" as user "jack". postgres@[local]:5432=>select * from tab2; ---因为没有继承关系,因此jack无法享有tab2的查询权限了 ERROR: permission denied for table tab2 postgres@[local]:5432=>select * from tab1; id ---- (0 rows) postgres@[local]:5432=>
复制
那么r1 role是否可以访问tab2呢,一样不行的,只能通过手工set role方式访问
postgres@[local]:5432=>\c - postgres You are now connected to database "postgres" as user "postgres". postgres@[local]:5432=#alter role r1 login; ALTER ROLE postgres@[local]:5432=#\c - r1 You are now connected to database "postgres" as user "r1". postgres@[local]:5432=>select * from tab2; ERROR: permission denied for table tab2 postgres@[local]:5432=> 通过手工指定role方式可以。 postgres@[local]:5432=>set role r2; SET postgres@[local]:5432=>select * from tab2; id ---- (0 rows) postgres@[local]:5432=>
复制
3)系统权限的继承测试
首先把r1赋予create role权限,并且是inherit继承方式;
postgres@[local]:5432=#alter role r1 inherit; ALTER ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {r1} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | | {r2} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#alter role r1 createrole,replication; ERROR: syntax error at or near "," LINE 1: alter role r1 createrole,replication; ^ postgres@[local]:5432=#alter role r1 createrole replication; ALTER ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {r1} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | Create role, Replication | {r2} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#
复制
按照对象的继承方式,应该在jack role用户下执行可以直接create role,下面我们测试一下
postgres@[local]:5432=#\c - jack You are now connected to database "postgres" as user "jack". postgres@[local]:5432=>create role jackjack; ERROR: permission denied to create role postgres@[local]:5432=>
复制
看来系统权限是无法直接继承的,那么直接set role来测试一下呢
postgres@[local]:5432=>set role r1; SET postgres@[local]:5432=>create role jackjack; CREATE ROLE postgres@[local]:5432=>drop role jackjack ; DROP ROLE postgres@[local]:5432=>
复制
总结:系统权限不会继承,只有主动set才生效;角色属性login、superuser、createdb和createrole可以被认为是一种特殊权限,但是它们从来不会像数据库对象上的普通权限那样被继承。要使用这些属性,你必须实际set role到一个有这些属性之一的特定角色。
4)清理相关role
postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- jack | | {r1} pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} r1 | Create role, Replication | {r2} r2 | Cannot login | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#create role r1; ERROR: role "r1" already exists postgres@[local]:5432=# postgres@[local]:5432=#drop role r1; ERROR: role "r1" cannot be dropped because some objects depend on it DETAIL: privileges for table tab1 postgres@[local]:5432=#drop role r2; ERROR: role "r2" cannot be dropped because some objects depend on it DETAIL: privileges for table tab2 --按照提示说明有权限引用tab1 tab2 因此无法删除相关role 这时处理方式两种: 第一种:就是利用1.5.1的处理方式进行reassign owned和drop owned方式 第二种:直接删除相关对象,生产慎重: postgres@[local]:5432=#drop role r1; ERROR: role "r1" cannot be dropped because some objects depend on it DETAIL: privileges for table tab1 postgres@[local]:5432=#drop role r2; ERROR: role "r2" cannot be dropped because some objects depend on it DETAIL: privileges for table tab2 postgres@[local]:5432=#drop table tab tab1 tab2 tab3 postgres@[local]:5432=#drop table tab tab1 tab2 tab3 postgres@[local]:5432=#drop table tab2 postgres-# ; DROP TABLE postgres@[local]:5432=#drop table tab3; DROP TABLE postgres@[local]:5432=#drop role r1; ERROR: role "r1" cannot be dropped because some objects depend on it DETAIL: privileges for table tab1 postgres@[local]:5432=#drop table tab1; DROP TABLE postgres@[local]:5432=#drop role r1; DROP ROLE postgres@[local]:5432=#drop role r2; DROP ROLE postgres@[local]:5432=#drop role jack; DROP ROLE postgres@[local]:5432=#\du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- pguser | Superuser, Create role, Create DB, Replication | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} yanwei | Superuser, Create role, Create DB | {} postgres@[local]:5432=#
复制
1.7.2 RDS(PG)没有postgres用户的管理方式参考
背景:
没有了超级用户,无法使用 RDS 的普通用户,管理其他普通用户创建的对象,经常出现操作对象无权限的问题,尤其存在多用户的情况下。通常情况下:
- 一个普通用户只能在自己 owner 的DB下创建 schema;
- DB 下的对象有一个所属的 schema,普通用户可以在 public 下创建对象(如 table),在其他 schema 下创建对象需要 schema 的 owner 是当前用户或特别的授权;
- 管理一个数据库对象,需要是超级用户、或对象的 owner 是当前用户(在用户组权限之外)。
解决方法
在这里,我们提供一种通用的方法,可以使用 PG 的用户组和继承特性,做到使用一个普通用户管理多个其他用户。
-
使用 RDS 的根账号创建一个用于管理数据库和用户的管理员账号,他具有创建用户和创建 DB 的权限
postgres=# create user admin createdb createrole login password 'pgsql'; CREATE ROLE postgres=# \du List of roles Role name | Attributes | Member of -------------+------------------------------------------------+----------- admin | Create role, Create DB | {} test | Superuser | {}
复制 -
使用这个 admin 登陆 postgres,创建用于存放数据的DB
postgres=> create database dbadmin; CREATE DATABASE
复制 -
创建子账户subuser1 subuser2,并把他们的权限给admin
dbadmin=> create user subuser1 ; CREATE ROLE dbadmin=> create user subuser2 ; CREATE ROLE dbadmin=> grant subuser1 to admin; GRANT ROLE dbadmin=> grant subuser2 to admin; GRANT ROLE
复制 -
我们看到 admin 用户组管理了下列2个用户,也就是admin 拥有 subuser1,subuser2 的所有权限
dbadmin=> \du List of roles Role name | Attributes | Member of -------------+------------------------------------------------+--------------------- admin | Create role, Create DB | {subuser1,subuser2} subuser1 | | {} subuser2 | | {}
复制 -
使用 subuser1 或 subuser2 登陆数据库 dbadmin,public schema上创建 owner 是自己的表对象和其他对象。分别用3个用户创建3张表a b c用作测试,另外也可以使用 admin 用户创建分别属于 subuser1 和 subuser2 的 schema ,再创建 table
dbadmin=> \d List of relations Schema | Name | Type | Owner --------+------------+-------+---------- public | t_admin | table | admin public | t_subuser1 | table | subuser1 public | t_subuser2 | table | subuser2
复制 -
使用 admin 用户,可以对这3张表做管理,例如:
dbadmin=> select user; current_user -------------- admin (1 row) dbadmin=> drop table t_admin,t_subuser1,t_subuser2; DROP TABLE
复制 -
但是 subuser1 无法删除 t_admin 和 t_subuser2,同理 subuser2 也无法删除 t_admin 和 t_subuser1
dbadmin=> select user; current_user -------------- subuser1 (1 row) dbadmin=> drop table t_admin; ERROR: must be owner of relation t_admin dbadmin=> drop table t_subuser1; DROP TABLE dbadmin=> drop table t_subuser2; ERROR: must be owner of relation t_subuser2
复制 -
同理,可以使用 admin 用户,重置这3张表的 owner 到3个用户中的一个,而使用 subuser1 和 subuser2 则没有权限操作,例如:
dbadmin=> select user; current_user -------------- admin (1 row) dbadmin=> alter table t_admin owner to subuser1; ALTER TABLE dbadmin=> alter table t_subuser2 owner to subuser1; ALTER TABLE dbadmin=> alter table t_subuser1 owner to subuser2; ALTER TABLE dbadmin=> select user; current_user -------------- subuser1 (1 row) dbadmin=> \d List of relations Schema | Name | Type | Owner --------+------------+-------+---------- public | t_admin | table | subuser1 public | t_subuser1 | table | subuser2 public | t_subuser2 | table | subuser1 (3 rows) dbadmin=> alter table t_admin owner to admin; ERROR: must be member of role "admin" dbadmin=> alter table t_subuser1 owner to subuser1; ERROR: must be owner of relation t_subuser1
复制 -
最后,提一个完成上述功能的假设,admin 用户默认具有 INHERIT 权限,INHERIT 权限决定一个角色是否“继承”它所在组的角色的权限。一个带有 INHERIT 属性的角色可以自动使用已经赋与它直接或间接所在组的任何权限。没有 INHERIT,其它角色的成员关系只赋与该角色 SET ROLE 成其它角色的能力;其它角色的权限只是在这么做了之后才能获得。如果没有声明,缺省是 INHERIT。通过系统表, pg_roles 可以获得该用户的权限信息
dbadmin=> select rolname,rolinherit from pg_roles ; rolname | rolinherit -------------+------------ subuser1 | t subuser2 | t admin | t
复制
总结
使用 PG 用户组可以做到使用一个 PG 用户组管理所在组内的所有其他普通用户和他们的对象。RDS 用户设置一个用户组,既可以管理多个用户的对象,又可以实现一定程度上的权限隔离,同时权限又不会过大,推荐在云上使用该方式管理自己的数据库。