1、故事起源
我们的项目使用了PG数据库,在项目开发阶段,为了省事偷懒,直接将应用连接数据库的用户给了supperuser权限,用起来简直不要太爽。随着开发进度接近中后期,开始筹建用户验收环境,在该环境我依旧直接给了超级用户权限,谁曾想就在前几天甲方爸爸哗啦啦的给了一堆漏洞清单,让我们抓紧整改。一头雾水的我不得不重操旧业,开始四处学习PG的用户权限相关知识。
2、干货记录
2.1 、创建用户时的权限分配
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 :被忽略,但是为向后兼容性而存在。
示例:
--不需要密码
postgres=# CREATE ROLE aikes LOGIN;
CREATE ROLE
--需要密码
postgres=# CREATE USER aikes WITH PASSWORD '10JQKA';
CREATE ROLE
备注:
1、create user命令等同于create role,除了一点:create user有隐含的login权限;而create role不会有该权限。
2、创建该用户后,还不能直接登录。需要修改 pg_hba.conf 文件,trust不验证密码,MD5验证密码(后文有说明)
①:本地登陆:local all all trust
②:远程登陆:host all all 192.168.100.133/32 md5
补充:
创建复制用户
CREATE USER abc REPLICATION LOGIN ENCRYPTED PASSWORD '';
CREATE USER abc REPLICATION LOGIN ENCRYPTED PASSWORD 'abc';
ALTER USER work WITH ENCRYPTED password '';
创建scheme 角色
CREATE ROLE abc;
CREATE DATABASE abc WITH OWNER abc ENCODING UTF8 TEMPLATE template0;
\c abc
创建schema
CREATE SCHEMA abc;
ALTER SCHEMA abc OWNER to abc;
revoke create on schema sche from sche ;
创建用户
create user abc with ENCRYPTED password '';
GRANT abc to abc;
ALTER ROLE abc WITH abc;
##创建读写账号
CREATE ROLE abc_rw;
CREATE ROLE abc_rr;
##赋予访问数据库权限,schema权限
grant connect ON DATABASE abc to abc_rw;
GRANT USAGE ON SCHEMA abc TO abc_rw;
##赋予读写权限
grant select,insert,update,delete ON ALL TABLES IN SCHEMA abc to abc;
赋予序列权限
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA abc to abc;
赋予默认权限
ALTER DEFAULT PRIVILEGES IN SCHEMA abc GRANT select,insert,update,delete ON TABLES TO abc;
赋予序列权限
ALTER DEFAULT PRIVILEGES IN SCHEMA abc GRANT ALL PRIVILEGES ON SEQUENCES TO abc;
#用户对db要有连接权限
grant connect ON DATABASE abc to abc;
#用户要对schema usage 权限,不然要select * from schema_name.table ,不能用搜索路径
GRANT USAGE ON SCHEMA abc TO abc;
grant select ON ALL TABLES IN SCHEMA abc to abc;
ALTER DEFAULT PRIVILEGES IN SCHEMA abc GRANT select ON TABLES TO abc;
create user abc_w with ENCRYPTED password '';
create user abc_r with ENCRYPTED password '';
GRANT abc_rw to abc_w;
GRANT abc_rr to abc_r;
2.2、用户权限授予和收回
权限说明:
SELECT:允许从指定表,视图或序列的任何列或列出的特定列进行SELECT。也允许使用COPY TO。在UPDATE或DELETE中引用现有列值也需要此权限。对于序列,此权限还允许使用currval函数。对于大对象,此权限允许读取对象。
INSERT:允许将新行INSERT到指定的表中。如果列出了特定列,则只能在INSERT命令中为这些列分配(因此其他列将接收默认值)。也允许COPY FROM。
UPDATE:允许更新指定表的任何列或列出的特定列,需要SELECT权限。
DELETE:允许删除指定表中的行,需要SELECT权限。
TRUNCATE:允许在指定的表上创建触发器。
REFERENCES:允许创建引用指定表或表的指定列的外键约束。
TRIGGER:允许在指定的表上创建触发器。
CREATE:对于数据库,允许在数据库中创建新的schema、table、index。
CONNECT:允许用户连接到指定的数据库。在连接启动时检查此权限。
TEMPORARY、TEMP:允许在使用指定数据库时创建临时表。
EXECUTE:允许使用指定的函数或过程以及在函数。
USAGE:对于schema,允许访问指定模式中包含的对象;对于sequence,允许使用currval和nextval函数。对于类型和域,允许在创建表,函数和其他模式对象时使用类型或域。
ALL PRIVILEGES:一次授予所有可用权限。
授予权限
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] }
ON { [ TABLE ] table_name [, ...]
| ALL TABLES IN SCHEMA schema_name [, ...] }
TO role_specification [, ...] [ WITH GRANT OPTION ]
##单表授权:授权aikes账号可以访问schema为sche 的demo表
grant select,insert,update,delete on sche.demo to aikes;
##所有表授权:grant select,insert,update,delete on all tables in schema sche to aikes;
GRANT { { USAGE | SELECT | UPDATE }
[, ...] | ALL [ PRIVILEGES ] }
ON { SEQUENCE sequence_name [, ...]
| ALL SEQUENCES IN SCHEMA schema_name [, ...] }
TO role_specification [, ...] [ WITH GRANT OPTION ]
##序列(自增键)属性授权,指定sche schema下的seq_id_seq 给aikes用户
grant select,update on sequence sche.seq_id_seq to aikes;
##序列(自增键)属性授权,给用户aikes授权sche schema下的所有序列
grant select,update on all sequences in schema sche to aikes;
GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
ON DATABASE database_name [, ...]
TO role_specification [, ...] [ WITH GRANT OPTION ]
##连接数据库权限,授权aikes 用户连接数据库dbdemo
grant connect on database dbdemo to aikes ;
GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
ON SCHEMA schema_name [, ...]
TO role_specification [, ...] [ WITH GRANT OPTION ]
##连接schema权限,授权aikes访问sche schema权限
grant usage on schema sche to aikes;
GRANT { CREATE | ALL [ PRIVILEGES ] }
ON TABLESPACE tablespace_name [, ...]
TO role_specification [, ...] [ WITH GRANT OPTION ]
GRANT { USAGE | ALL [ PRIVILEGES ] }
ON TYPE type_name [, ...]
TO role_specification [, ...] [ WITH GRANT OPTION ]
GRANT role_name [, ...] TO role_name [, ...] [ WITH ADMIN OPTION ]
##把aikes用户的权限授予用户mark。grant aikes to mark;
撤销权限
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] }
ON { [ TABLE ] table_name [, ...]
| ALL TABLES IN SCHEMA schema_name [, ...] }
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
##移除用户aikes在schema sche 上所有表的select权限
revoke select on all tables in schema sche from aikes;
##序列
REVOKE [ GRANT OPTION FOR ]
{ { USAGE | SELECT | UPDATE }
[, ...] | ALL [ PRIVILEGES ] }
ON { SEQUENCE sequence_name [, ...]
| ALL SEQUENCES IN SCHEMA schema_name [, ...] }
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
##库
REVOKE [ GRANT OPTION FOR ]
{ { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
ON DATABASE database_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ USAGE | ALL [ PRIVILEGES ] }
ON FOREIGN DATA WRAPPER fdw_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT]
REVOKE [ GRANT OPTION FOR ]
{ USAGE | ALL [ PRIVILEGES ] }
ON FOREIGN SERVER server_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT]
REVOKE [ GRANT OPTION FOR ]
{ EXECUTE | ALL [ PRIVILEGES ] }
ON { { FUNCTION | PROCEDURE | ROUTINE } function_name [ ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) ] [, ...]
| ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA schema_name [, ...] }
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ USAGE | ALL [ PRIVILEGES ] }
ON LANGUAGE lang_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
ON LARGE OBJECT loid [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
##schena权限
REVOKE [ GRANT OPTION FOR ]
{ { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
ON SCHEMA schema_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ CREATE | ALL [ PRIVILEGES ] }
ON TABLESPACE tablespace_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
{ USAGE | ALL [ PRIVILEGES ] }
ON TYPE type_name [, ...]
FROM { [ GROUP ] role_name | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
REVOKE [ ADMIN OPTION FOR ]
role_name [, ...] FROM role_name [, ...]
[ CASCADE | RESTRICT ]
修改用户属性
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
##示例 alter role aikes with superuser;
2.3、配置文件中的访问控制
通过修改pg_hba.conf文件,实现访问控制
METHOD解释:
trust:允许无条件连接,允许任何PostgreSQL用户身份登录,而无需密码或任何其他身份验证。
reject:拒绝任何条件连接,这对于从组中“过滤掉”某些主机非常有用。
scram-sha-256:执行SCRAM-SHA-256身份验证以验证用户的密码。
md5:执行SCRAM-SHA-256或MD5身份验证以验证用户的密码。
password:要提供未加密的密码以进行身份验证。由于密码是通过网络以明文形式发送的,因此不应在不受信任的网络上使用。
gss:使用GSSAPI对用户进行身份验证,这仅适用于TCP / IP连接。
sspi:使用SSPI对用户进行身份验证,这仅适用于Windows。
ident:通过联系客户端上的ident服务器获取客户端的操作系统用户名,并检查它是否与请求的数据库用户名匹配。 Ident身份验证只能用于TCP / IP连接。为本地连接指定时,将使用对等身份验证。
peer:从操作系统获取客户端的操作系统用户名,并检查它是否与请求的数据库用户名匹配。这仅适用于本地连接。
ldap:使用LDAP服务器进行身份验证。
radius:使用RADIUS服务器进行身份验证。
cert:使用SSL客户端证书进行身份验证。
pam:使用操作系统提供的可插入身份验证模块(PAM)服务进行身份验证。
bsd:使用操作系统提供的BSD身份验证服务进行身份验证。
# TYPE DATABASE USER ADDRESS METHOD
local all all trust
--在本地允许任何用户无密码登录
local all all peer
--操作系统的登录用户和pg的用户是否一致,一致则可以登录
local all all ident
--操作系统的登录用户和pg的用户是否一致,一致则可以登录
host all all 192.168.163.0/24 md5
--指定客户端IP访问通过md5身份验证进行登录
host all all 192.168.163.132/32 password
--指定客户端IP通过passwotd身份验证进行登录
host all all 192.168.54.1/32 reject
host all all 192.168.0.0/16 ident
host all all 127.0.0.1 255.255.255.255 trust
...
设置成功后需要重新加载配置文件,不需要重启数据库
postgres=# select pg_reload_conf();
pg_reload_conf
----------------
true
通过SQL可以查看配置:
postgres=# select * from pg_hba_file_rules;
line_number | type | database | user_name | address | netmask | auth_method | options | error
-------------+-------+---------------+-----------+---------------+-----------------------------------------+-------------+---------+-------
87 | host | {all} | {all} | 192.168.163.0 | 255.255.255.0 | md5 | |
92 | local | {all} | {all} | | | peer | |
94 | host | {all} | {all} | 127.0.0.1 | 255.255.255.255 | md5 | |
96 | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | md5 | |
99 | local | {replication} | {all} | | | peer | |
100 | host | {replication} | {all} | 127.0.0.1 | 255.255.255.255 | md5 | |
101 | host | {replication} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | md5 | |
3、实际使用授权示例
#访问库
postgres=# grant connect on database db to aikes;
GRANT
#访问schmea
postgres=# grant usage on schema sche to aikes;
GRANT
#访问表
postgres=# grant select,insert,update,delete on all tables in schema sche to aikes;
GRANT
#如果访问自增序列,需要授权
postgres=# grant select,update on all sequences in schema sche to aikes;
GRANT
注意:上面的授权只对历史的一些对象授权,后期增加的对象是没有权限的,需要给个默认权限
#默认表权限
postgres=# ALTER DEFAULT PRIVILEGES IN SCHEMA sche GRANT select,insert,update,delete ON TABLES TO aikes;
ALTER DEFAULT PRIVILEGES
#默认自增序列权限
postgres=# ALTER DEFAULT PRIVILEGES IN SCHEMA sche GRANT select,update ON sequences TO aikes;
ALTER DEFAULT PRIVILEGES
4、查看对象权限
通过指令 \dp 或者下列SQL可以查看到数据库对象权限划分
SELECT n.nspname as "Schema",
c.relname as "Name",
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'S' THEN 'sequence' WHEN 'f' THEN 'foreign table' END as "Type",
pg_catalog.array_to_string(c.relacl, E'\n') AS "Access privileges",
pg_catalog.array_to_string(ARRAY(
SELECT attname || E':\n ' || pg_catalog.array_to_string(attacl, E'\n ')
FROM pg_catalog.pg_attribute a
WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL
), E'\n') AS "Column privileges",
pg_catalog.array_to_string(ARRAY(
SELECT polname
|| CASE WHEN polcmd != '*' THEN
E' (' || polcmd || E'):'
ELSE E':'
END
|| CASE WHEN polqual IS NOT NULL THEN
E'\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)
ELSE E''
END
|| CASE WHEN polwithcheck IS NOT NULL THEN
E'\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)
ELSE E''
END || CASE WHEN polroles <> '{0}' THEN
E'\n to: ' || pg_catalog.array_to_string(
ARRAY(
SELECT rolname
FROM pg_catalog.pg_roles
WHERE oid = ANY (polroles)
ORDER BY 1
), E', ')
ELSE E''
END
FROM pg_catalog.pg_policy pol
WHERE polrelid = c.oid), E'\n')
AS "Policies"
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')
AND n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)
ORDER BY 1, 2;
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+--------------------------------+----------+-------------------------+-------------------+----------
public | emp | table | dbuser=arwdDxt/dbuser +| |
| | | postgres=arwd/dbuser | |
public | dept | table | dbuser=arwdDxt/dbuser +| |
| | | postgres=arwd/dbuser | |
public | company | table | dbuser=arwdDxt/dbuser +| |