受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

F.38. sepgsql — 基于 SELinux 标签的强制访问控制(MAC)安全模块 #

sepgsql 是一个可加载模块,支持基于 SELinux 安全策略和安全标签的强制访问控制(MAC)。

Warning

当前实现存在重大限制,并不会对所有操作实施强制访问控制。详见 Section F.38.7

F.38.1. 概述 #

该模块与 SELinux 集成,在 PostgreSQL 通常提供的安全检查之外再增加一层 安全检查。从 SELinux 的角度看,该模块使 PostgreSQL 能够充当用户空间对象管理器。由 DML 查询发起的每一次表或函数访问,都会依据系统安全策略进行检查。这种检查是 对 PostgreSQL 常规 SQL 权限检查的补充。

SELinux 的访问控制决策通过安全标签作出,安全标签 以 system_u:object_r:sepgsql_table_t:s0 这样的字符串表示。 每个访问控制决策都涉及两个标签:尝试执行操作的主体标签,以及要执行该操 作的对象标签。由于这些标签可以应用到任意类型的对象,数据库中存储对象的 访问控制决策也可以与文件等其他类型对象一样,遵循同一套一般性标准;使 用该模块时也确实如此。这样的设计旨在让集中式安全策略能够保护信息资产, 而不受这些资产具体存储方式的影响。

SECURITY LABEL 语句允许为数据库对象分配安全标签。

F.38.2. 安装 #

sepgsql 只能在启用了 SELinuxLinux 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

请注意,具体会看到以下通知中的哪些,取决于所使用的 libselinuxselinux-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

这些消息没有害处,应予忽略。

如果安装过程无错误完成,此时就可以正常启动服务器。

F.38.3. 回归测试 #

由于 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-develselinux-policy RPM 提供。)构建完成后,使用 semodule 命令安装该策略包,该命令会将提供的策略包装载到 内核中。如果安装正确,semodule -l 应该把 sepgsql-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

F.38.4. GUC 参数 #

sepgsql.permissive (boolean) #

该参数使 sepgsql 无论系统设置如何都以宽容模式运行。 默认值为关闭。该参数只能在 postgresql.conf 文件中 或服务器命令行上设置。

当该参数打开时,sepgsql 会以宽容模式运行,即使 SELinux 整体处于强制模式也是如此。该参数主要用于测试。

sepgsql.debug_audit (boolean) #

该参数会在不考虑系统策略设置的情况下启用审计消息输出。 默认值为关闭,这意味着消息将按系统设置输出。

SELinux 的安全策略本身也有规则来控制是否记录特 定的访问。默认情况下,访问违例会被记录,而被允许的访问不会被记录。

该参数会在不考虑系统策略的情况下,强制打开所有可能的日志记录。

F.38.5. 特性 #

F.38.5.1. 受控对象类 #

SELinux 的安全模型把全部访问控制规则描述为主体 实体(通常是数据库客户端)与对象实体(例如数据库对象)之间的关系,这两 者都由安全标签标识。如果尝试访问一个未带标签的对象,则会把它视为被赋予 了 unlabeled_t 标签。

当前,sepgsql 允许为模式、表、列、序列、视图和函数分 配安全标签。使用 sepgsql 时,安全标签会在受支持的数据 库对象创建时自动分配。这个标签称为默认安全标签,其取值由系统安全策略决 定;策略的输入包括创建者的标签、新对象父对象的标签,以及待创建对象的名 称(如果需要)。

新数据库对象基本上会继承父对象的安全标签,但如果安全策略中存在称为类型 转换规则的特殊规则,则可能会应用不同的标签。对于模式,父对象是当前数据 库;对于表、序列、视图和函数,父对象是其所在模式;对于列,父对象是其所 在表。

F.38.5.2. DML 权限 #

对于表,会根据语句类型,对所有被引用的目标表检查 db_table:selectdb_table:insertdb_table:updatedb_table:delete; 此外,对于其列在 WHERERETURNING 子句中被引用的所有表,以及作为 UPDATE 数据源的所有表等, 还会检查 db_table:select

每个被引用的列也都会检查列级权限。db_column:select 不仅会对通过 SELECT 读取的列进行检查,也会对其他 DML 语句中被引用的列进行检查;对于被 UPDATEINSERT 修改的列,还会检查 db_column:updatedb_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 后,这些操作都会被禁 止。

F.38.5.3. DDL 权限 #

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。由于实现限制,当前尚未这样做。)

F.38.5.4. 受信任过程 #

受信任过程类似于安全定义器函数或 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 允许用户打印客户信用卡号,并对其 中部分数字做掩码处理。

F.38.5.5. 动态域转换 #

如果安全策略允许,可以利用 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(),从而恢复这一安全标签变更。这里的关键 在于,真正有权修改生效安全标签的只有受信任过程,而且它只会在获得适当凭据 时这样做。当然,为了安全运行,凭据存储(表、过程定义或其他载体)必须防 止未授权访问。

F.38.5.6. 杂项 #

我们一律拒绝 LOAD 命令,因为装入任意模块都可能轻易绕过安 全策略的强制执行。

F.38.6. sepgsql 函数 #

Table F.30 展示了可用函数。

Table F.30. sepgsql 函数

函数

描述

sepgsql_getcon () → text

返回客户端域,也就是客户端当前的安全标签。

sepgsql_setcon ( text ) → boolean

如果安全策略允许,将当前会话的客户端域切换到新域。它也接受 NULL 输入,表示请求切换到客户端的原始域。

sepgsql_mcstrans_in ( text ) → text

如果 mcstrans 守护进程正在运行,则把给定的限定格式 MLS/MCS 范围转换为 原始格式。

sepgsql_mcstrans_out ( text ) → text

如果 mcstrans 守护进程正在运行,则把给定的原始 MLS/MCS 范围转换为限定 格式。

sepgsql_restorecon ( text ) → boolean

为当前数据库内的所有对象建立初始安全标签。参数可以是 NULL,也可以是用作系统默认值替代方案的 specfile 文件名。


F.38.7. 限制 #

数据定义语言(DDL)权限

由于实现限制,某些 DDL 操作不会检查权限。

数据控制语言(DCL)权限

由于实现限制,DCL 操作不会检查权限。

行级访问控制

PostgreSQL 支持行级访问,但 sepgsql 不支持。

隐蔽通道

sepgsql 不会试图隐藏某个对象的存在,即使用户无权引用 它也是如此。例如,即便我们无法取得一个不可见对象的内容,也仍然可以从主 键冲突、外键违例等结果中推断出它的存在。绝密表的存在无法隐藏;我们只希 望隐藏其内容。

F.38.8. 外部资源 #

SE-PostgreSQL Introduction

该 wiki 页面提供了简要概述,并介绍了安全设计、体系结构、管理以及未来特 性。

SELinux User's and Administrator's Guide

该文档提供了在系统上管理 SELinux 所需的广泛知 识。它主要聚焦于 Red Hat 操作系统,但并不限于此。

Fedora SELinux FAQ

该文档回答了关于 SELinux 的常见问题。它主要 聚焦于 Fedora,但并不限于 Fedora。

F.38.9. 作者 #

KaiGai Kohei

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。