sepgsql 是一个可加载模块,支持基于 SELinux 安全策略和安全标签的强制访问控制(MAC)。
当前实现存在重大限制,并不会对所有操作实施强制访问控制。详见 Section F.38.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 被禁用或尚未安装,则必须先安装并配置 它,之后才能安装本模块。
要构建此模块,使用 make 和 autoconf 时请指定 --with-selinux,使用 meson 时请指定 -Dselinux={ auto | enabled | disabled }。 还要确保构建时已经安装 libselinux-devel RPM。
要使用此模块,必须将 sepgsql 包含在 shared_preload_libraries 参数中,该参数位于 postgresql.conf 内。如果以其他任何方式加载该模块,它都 无法正确工作。 模块加载后,应在每个数据库中执行 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
这些消息没有害处,应予忽略。
如果安装过程无错误完成,此时就可以正常启动服务器。
sepgsql 测试套件会在 PG_TEST_EXTRA 包含 sepgsql 时运行(见 Section 31.1.3)。 这种方式适用于 PostgreSQL 的开发过程。另一种方式是 运行测试,以检查某个数据库实例是否已为 sepgsql 正确设置。
由于 SELinux 的特性,运行 sepgsql 的回归测试需要额外执行若干配置步骤,其中部分 步骤必须以 root 身份完成。
手工测试必须在一个已配置好的 PostgreSQL 构建树中的 contrib/sepgsql 目录下运行。虽然这些测试需要构建树, 但它们被设计为针对已安装的服务器执行,也就是说更接近于 make installcheck,而不是 make check。
首先,在一个可用数据库中设置 sepgsql,并遵循 Section F.38.2 中的说明。请注意,当前操作系统用户必 须能够无需密码认证,以超级用户身份连接到该数据库。
第二,构建并安装回归测试所需的策略包。sepgsql-regtest 策略是一个专用策略包,提供了一组允许在回归测试期间使用的规则。它应从策 略源文件 sepgsql-regtest.te 构建,构建过程通过使用 make 和由 SELinux 提供的 Makefile 完成。需要在本机上找 到合适的 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.38.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 无论系统设置如何都以宽容模式运行。 默认值为关闭。该参数只能在 postgresql.conf 文件中 或服务器命令行上设置。
当该参数打开时,sepgsql 会以宽容模式运行,即使 SELinux 整体处于强制模式也是如此。该参数主要用于测试。
sepgsql.debug_audit (boolean) #该参数会在不考虑系统策略设置的情况下启用审计消息输出。 默认值为关闭,这意味着消息将按系统设置输出。
SELinux 的安全策略本身也有规则来控制是否记录特 定的访问。默认情况下,访问违例会被记录,而被允许的访问不会被记录。
该参数会在不考虑系统策略的情况下,强制打开所有可能的日志记录。
SELinux 的安全模型把全部访问控制规则描述为主体 实体(通常是数据库客户端)与对象实体(例如数据库对象)之间的关系,这两 者都由安全标签标识。如果尝试访问一个未带标签的对象,则会把它视为被赋予 了 unlabeled_t 标签。
当前,sepgsql 允许为模式、表、列、序列、视图和函数分 配安全标签。使用 sepgsql 时,安全标签会在受支持的数据 库对象创建时自动分配。这个标签称为默认安全标签,其取值由系统安全策略决 定;策略的输入包括创建者的标签、新对象父对象的标签,以及待创建对象的名 称(如果需要)。
新数据库对象基本上会继承父对象的安全标签,但如果安全策略中存在称为类型 转换规则的特殊规则,则可能会应用不同的标签。对于模式,父对象是当前数据 库;对于表、序列、视图和函数,父对象是其所在模式;对于列,父对象是其所 在表。
对于表,会根据语句类型,对所有被引用的目标表检查 db_table:select、db_table:insert、 db_table:update 或 db_table:delete; 此外,对于其列在 WHERE 或 RETURNING 子句中被引用的所有表,以及作为 UPDATE 数据源的所有表等, 还会检查 db_table:select。
每个被引用的列也都会检查列级权限。db_column:select 不仅会对通过 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.29 展示了可用函数。
Table F.29. 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>
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。