sepgsql 是一个可加载模块,支持基于 SELinux 安全策略的按标签强制访问控制(MAC)。
当前实现有重大限制,并未对所有动作实施强制访问控制。参见 Section F.40.7。
该模块与 SELinux 集成,在 PostgreSQL 通常提供的安全检查之上提供一个附加的安全检查层。从 SELinux 的视角看,该模块让 PostgreSQL 充当一个用户空间对象管理器。由 DML 查询发起的每一次表或函数访问都会对照系统安全策略进行检查。这一检查是 PostgreSQL 常规 SQL 权限检查之外的补充。
SELinux 的访问控制决策使用安全标签做出,标签以诸如 system_u:object_r:sepgsql_table_t:s0 这样的字符串表示。每个访问控制决策涉及两个标签:尝试执行动作的主体的标签,以及要对其执行操作的客体的标签。由于这些标签可以应用于任何种类的对象,存储在数据库中的对象的访问控制决策可以(并且在启用该模块时确实会)遵循与其他任何类型对象(如文件)相同的通用准则。这种设计旨在让集中的安全策略能够保护信息资产,与这些资产的存储细节无关。
SECURITY LABEL 语句允许为数据库对象分配安全标签。
sepgsql 只能在启用了 SELinux 的 Linux 2.6.28 或更高版本上使用。它在任何其他平台上都不可用。你还需要 libselinux 2.1.10 或更高版本,以及 selinux-policy 3.9.13 或更高版本(不过一些发行版可能把必要的规则向后移植到较老的策略版本中)。
sestatus 命令可以检查 SELinux 的状态。典型显示如下:
$ sestatus SELinux status: enabled SELinuxfs mount: /selinux Current mode: enforcing Mode from config file: enforcing Policy version: 24 Policy from config file: targeted
如果 SELinux 被禁用或未安装,你必须先配置好它,再安装该模块。
要构建该模块,请指定 --with-sepgsql(使用
make 和 autoconf 时)或 -Dsepgsql=enabled(使用
meson 时)。
构建时请确保已安装 libselinux-devel RPM。
要使用该模块,必须在 postgresql.conf 的 shared_preload_libraries 参数中包含 sepgsql。以任何其他方式加载时该模块都无法正确工作。模块加载后,应在每个数据库中执行 sepgsql.sql。这会安装安全标签管理所需的函数,并分配初始安全标签。
下面的示例展示如何初始化一个安装了 sepgsql 函数与安全标签的全新数据库集簇。请根据你的安装调整所示路径:
$ export PGDATA=/path/to/data/directory
$ initdb
$ vi $PGDATA/postgresql.conf
change
#shared_preload_libraries = '' # (change requires restart)
to
shared_preload_libraries = 'sepgsql' # (change requires restart)
$ for DBNAME in template0 template1 postgres; do
postgres --single -F -c exit_on_error=true $DBNAME \
</usr/local/pgsql/share/contrib/sepgsql.sql >/dev/null
done
请注意,根据你的 libselinux 与 selinux-policy 的具体版本,你可能会看到以下部分或全部通知:
/etc/selinux/targeted/contexts/sepgsql_contexts: line 33 has invalid object type db_blobs /etc/selinux/targeted/contexts/sepgsql_contexts: line 36 has invalid object type db_language /etc/selinux/targeted/contexts/sepgsql_contexts: line 37 has invalid object type db_language /etc/selinux/targeted/contexts/sepgsql_contexts: line 38 has invalid object type db_language /etc/selinux/targeted/contexts/sepgsql_contexts: line 39 has invalid object type db_language /etc/selinux/targeted/contexts/sepgsql_contexts: line 40 has invalid object type db_language
这些消息无害,应当忽略。
如果安装过程顺利完成,现在就可以正常启动服务器了。
当 PG_TEST_EXTRA 包含 sepgsql 时会运行
sepgsql 测试套件(参见
Section 31.1.3)。这种方式适合 PostgreSQL 的开发过程。此外,还有一种运行测试的方式,用来检查某个数据库实例是否已为 sepgsql 正确配置。
由于 SELinux 的性质,运行 sepgsql 的回归测试需要几个额外的配置步骤,其中一些必须以 root 身份完成。
手动测试必须在已配置的 PostgreSQL 构建树的 contrib/sepgsql 目录中运行。虽然它们需要构建树,但测试被设计为针对已安装的服务器执行,也就是说它们对应于 make installcheck,而非 make check。
首先,按照 Section F.40.2 中的说明在一个可用的数据库中设置 sepgsql。注意,当前的操作系统用户必须能够以超级用户身份、无需密码认证地连接数据库。
其次,构建并安装回归测试的策略包。sepgsql-regtest 策略是一个特殊用途的策略包,它提供在回归测试期间允许的一组规则。它应当从策略源文件 sepgsql-regtest.te 构建,方法是使用 SELinux 提供的 Makefile 执行 make。你需要在系统上找到合适的 Makefile;下面显示的路径只是示例。(该 Makefile 通常由 selinux-policy-devel 或 selinux-policy RPM 提供。)构建完成后,使用 semodule 命令安装该策略包,该命令把提供的策略包加载进内核。如果包被正确安装, 应当把 semodule -lsepgsql-regtest 列为可用的策略包:
$ cd .../contrib/sepgsql $ make -f /usr/share/selinux/devel/Makefile $ sudo semodule -u sepgsql-regtest.pp $ sudo semodule -l | grep sepgsql sepgsql-regtest 1.07
第三,开启 sepgsql_regression_test_mode。出于安全原因,sepgsql-regtest 中的规则默认不启用;sepgsql_regression_test_mode 参数启用启动回归测试所需的规则。可以用 setsebool 命令开启它:
$ sudo setsebool sepgsql_regression_test_mode on $ getsebool sepgsql_regression_test_mode sepgsql_regression_test_mode --> on
第四,确认你的 shell 在 unconfined_t 域中运行:
$ id -Z unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
如有必要,调整工作域的细节请参见 Section F.40.8。
最后,运行回归测试脚本:
$ ./test_sepgsql
该脚本会尝试验证你已正确完成所有配置步骤,然后运行 sepgsql 模块的回归测试。
完成测试后,建议禁用 sepgsql_regression_test_mode 参数:
$ sudo setsebool sepgsql_regression_test_mode off
你可能更希望彻底移除 sepgsql-regtest 策略:
$ sudo semodule -r sepgsql-regtest
sepgsql.permissive (boolean)
#
该参数让 sepgsql 无视系统设置以宽容(permissive)模式运作。默认值为 off。该参数只能在 postgresql.conf 文件或服务器命令行中设置。
该参数开启时,即使 SELinux 整体处于强制(enforcing)模式,sepgsql 也会以宽容模式运作。该参数主要用于测试目的。
sepgsql.debug_audit (boolean)
#该参数使审计消息无视系统策略设置而打印。默认值为 off,即消息根据系统设置打印。
SELinux 的安全策略本身也有规则控制是否记录特定访问。默认情况下,访问违规会被记录,而被允许的访问不会。
该参数强制开启所有可能的日志记录,无视系统策略。
SELinux 的安全模型把所有访问控制规则描述为主体实体(通常是数据库的客户端)与客体实体(例如数据库对象)之间的关系,每一方都由安全标签标识。如果尝试访问一个未打标签的对象,该对象会被视为被分配了 unlabeled_t 标签。
目前,sepgsql 允许把安全标签分配给模式、表、列、序列、视图与函数。使用 sepgsql 时,支持类型的数据库对象在创建时会被自动分配安全标签。该标签称为默认安全标签,它依据系统安全策略决定,系统安全策略把创建者的标签、分配给新对象父对象的标签,以及可选的被构造对象的名称作为输入。
新的数据库对象基本上继承父对象的安全标签,除非安全策略有被称为类型转换规则的特殊规则,此时可能应用不同的标签。对模式而言,父对象是当前数据库;对表、序列、视图与函数而言,父对象是所在模式;对列而言,父对象是所在的表。
对表而言,会根据语句的种类为所有被引用的目标表检查 db_table:select、db_table:insert、db_table:update 或 db_table:delete;此外,对包含 WHERE 或 RETURNING 子句中引用的列、作为 UPDATE 数据来源等的所有表,也会检查 db_table:select。
每个被引用的列也会检查列级权限。不仅使用 SELECT 读取的列会检查 db_column:select,其他 DML 语句中引用的列也会检查;被 UPDATE 或 INSERT 修改的列还会检查 db_column:update 或 db_column:insert。
例如,考虑:
UPDATE t1 SET x = 2, y = func1(y) WHERE z = 100;
这里,t1.x 被更新,因此会检查 db_column:update;t1.y 既被更新又被引用,因此会检查 db_column:{select update};t1.z 只被引用,因此会检查 db_column:select。在表级还会检查 db_table:{select update}。
对序列而言,当我们用 SELECT 引用序列对象时会检查 db_sequence:get_value;不过请注意,我们目前不检查执行 lastval() 等相应函数的权限。
对视图而言,会检查 db_view:expand,然后对从视图展开出来的对象逐个检查任何其他所需的权限。
对函数而言,当用户尝试在查询中执行函数或通过快速路径调用时,会检查 db_procedure:{execute}。如果该函数是可信过程,还会检查 db_procedure:{entrypoint} 权限,确认它可以作为可信过程的入口点。
要访问任何模式对象,需要在包含它的模式上具备 db_schema:search 权限。当对象被不带模式限定地引用时,不具备该权限的模式不会被搜索(就像用户对模式没有 USAGE 特权一样)。如果存在显式的模式限定,而用户对被命名模式不具备所需的权限,则会发生错误。
客户端必须被允许访问所有被引用的表与列,即使它们来自随后被展开的视图,这样我们才能以与引用表内容的方式无关的一致访问控制规则行事。
默认的数据库特权系统允许数据库超级用户用 DML 命令修改系统目录,并引用或修改 toast 表。sepgsql 启用时这些操作被禁止。
SELinux 为每种对象类型定义了若干权限来控制常见操作,例如创建、修改、删除与安全标签的重新打标。此外,若干对象类型还有控制其特有操作的专用权限,例如在特定模式内添加或删除名称条目。
创建新的数据库对象需要 create 权限。SELinux 会基于客户端的安全标签与新对象建议的安全标签授予或拒绝该权限。在某些情况下,还需要额外的特权:
CREATE DATABASE 另外需要对源数据库或模板数据库的 getattr 权限。
创建模式对象另外需要对父模式的 add_name 权限。
创建表另外需要创建每个单独表列的权限,就像每个表列都是单独的顶层对象一样。
创建被标记为 LEAKPROOF 的函数另外需要 install 权限。(为现有函数设置 LEAKPROOF 时也会检查该权限。)
执行 DROP 命令时,会对被移除的对象检查 drop。通过 CASCADE 间接删除的对象也会检查权限。删除特定模式内的对象(表、视图、序列与过程)另外需要对模式本身的 remove_name 权限。
执行 ALTER 命令时,对每种对象类型都会在被修改的对象上检查 setattr;对表的索引或触发器这类附属对象例外,此时权限改为在父对象上检查。在某些情况下,还需要额外的权限:
把对象移动到新模式,另外需要旧模式上的 remove_name 权限与新模式上的 add_name 权限。
对函数设置 LEAKPROOF 属性需要 install 权限。
对对象使用 SECURITY LABEL,另外需要对象结合其旧安全标签的 relabelfrom 权限,以及对象结合其新安全标签的 relabelto 权限。(在安装了多个标签提供者、用户尝试设置不受 SELinux 管理的安全标签的情况下,此处只应检查 setattr。由于实现限制,目前并未这样做。)
可信过程类似于安全定义者函数或 setuid 命令。SELinux 提供一种特性,允许可信代码使用与客户端不同的安全标签运行,通常目的是对敏感数据提供高度受控的访问(例如,可能省略某些行,或降低存储值的精度)。一个函数是否充当可信过程由其安全标签与操作系统安全策略控制。例如:
postgres=# CREATE TABLE customer (
cid int primary key,
cname text,
credit text
);
CREATE TABLE
postgres=# SECURITY LABEL ON COLUMN customer.credit
IS 'system_u:object_r:sepgsql_secret_table_t:s0';
SECURITY LABEL
postgres=# CREATE FUNCTION show_credit(int) RETURNS text
AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
FROM customer WHERE cid = $1'
LANGUAGE sql;
CREATE FUNCTION
postgres=# SECURITY LABEL ON FUNCTION show_credit(int)
IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
SECURITY LABEL
以上操作应当由管理用户执行。
postgres=# SELECT * FROM customer; ERROR: SELinux: security policy violation postgres=# SELECT cid, cname, show_credit(cid) FROM customer; cid | cname | show_credit -----+--------+--------------------- 1 | taro | 1111-2222-3333-xxxx 2 | hanako | 5555-6666-7777-xxxx (2 rows)
在此例中,普通用户无法直接引用 customer.credit,但可信过程 show_credit 允许用户打印客户的信用卡号,其中部分数字被遮蔽。
如果安全策略允许,可以使用 SELinux 的动态域转换特性把客户端进程(即客户端域)的安全标签切换到新的上下文。客户端域需要 setcurrent 权限,以及从旧域到新域的 dyntransition 权限。
动态域转换应被慎重对待,因为它允许用户按自己的意愿切换自己的标签、进而切换自己的特权,而非(像可信过程那样)由系统强制规定。因此,dyntransition 权限只有在用于切换到特权集合小于原域的域时才被认为是安全的。例如:
regression=# select sepgsql_getcon();
sepgsql_getcon
-------------------------------------------------------
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
(1 row)
regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c4');
sepgsql_setcon
----------------
t
(1 row)
regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c1023');
ERROR: SELinux: security policy violation
在上面的示例中,我们被允许从较大的 MCS 范围 c1.c1023 切换到较小的范围 c1.c4,但切回被拒绝了。
动态域转换与可信过程的组合能够支持一种契合连接池软件典型进程生命周期的有趣用例。即使你的连接池软件不被允许运行大多数 SQL 命令,你也可以允许它通过 sepgsql_setcon() 函数在可信过程内部切换客户端的安全标签;这应当需要某种凭证来授权切换客户端标签的请求。此后,该会话将拥有目标用户的特权,而非连接池的特权。连接池随后可以再次使用带 NULL 参数的 sepgsql_setcon() 恢复安全标签的更改,同样是在具有适当权限检查的可信过程内部调用。关键在于:只有可信过程真正拥有更改生效安全标签的权限,并且只有在提供正确凭证时才会这么做。当然,为了安全运作,凭证存储(表、过程定义或其他形式)必须防止未授权访问。
Table F.32 展示了可用的函数。
Table F.32. Sepgsql 函数
函数 描述 |
|---|
返回客户端域,即客户端当前的安全标签。 |
在安全策略允许的情况下,把当前会话的客户端域切换到新域。它也接受 |
如果 mcstrans 守护进程正在运行,把给定的限定格式 MLS/MCS 范围转换为原始格式。 |
如果 mcstrans 守护进程正在运行,把给定的原始格式 MLS/MCS 范围转换为限定格式。 |
为当前数据库中的所有对象设置初始安全标签。参数可以为 |
由于实现限制,某些 DDL 操作不检查权限。
由于实现限制,DCL 操作不检查权限。
PostgreSQL 支持行级访问,但 sepgsql 不支持。
即使用户不被允许引用某个对象,sepgsql 也不试图隐藏该对象的存在。例如,即使我们无法获得对象的内容,也可以从主键冲突、外键违规等结果推断出不可见对象的存在。绝密表的存在无法被隐藏;我们只希望隐藏其内容。
该 wiki 页面提供了简要概述、安全设计、架构、管理与即将推出的特性。
该文档提供了在系统上管理 SELinux 的广泛知识。它主要面向 Red Hat 操作系统,但不限于此。
该文档回答关于 SELinux 的常见问题。它主要面向 Fedora,但不限于 Fedora。
KaiGai Kohei <kaigai@ak.jp.nec.com>