F.35. sepgsql

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

警告

当前的实现具有明显的限制,并且不支持对所有动作的强制访问控制。详见 第 F.35.7 节

F.35.1. 概述

这个模块和SELinux集成在一起在 PostgreSQL提供的安全检查之上提供了一个 额外的安全检查层。从SELinux的角度来看,这个模块允许 PostgreSQL作为一个用户空间对象管理器。 对每一次由 DML 查询发起的表或者函数访问将根据系统安全策略进行检查。这种 检查是在PostgreSQL执行的常规 SQL 权限 检查之外进行的。

SELinux访问控制决定是通过使用安全标签 来做出的,安全标签使用system_u:object_r:sepgsql_table_t:s0 这样的字符串表示。每个访问控制决定涉及两个标签:尝试执行该动作的主体的 标签以及要在其上执行该动作的客体的标签。由于这些标签可以被应用于任何种 类的对象,对于存储在数据库中的对象的(用这个模块做出的)访问控制决定服 从于用于任意其他类型对象(例如文件)的同一种一般准则。这种设计是为了允 许一种中央安全策略来保护信息资产,而不依赖于这些资产是如何存储的。

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

F.35.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,你就必须在安装这个模块 之前先安装或者启用它。

要编译这个模块,应该在你的 PostgreSQL configure 命令中包括 选项--with-selinux。还要确定编译时安装了 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

请注意,如果你具有特定版本的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.35.3. 回归测试

由于SELinux的本质,为sepgsql 运行回归测试要求一些额外的配置步骤,某些步骤还需要由 root 来完成。该回归测试 无法通过普通的make check或者make installcheck命令运行, 你必须建立配置并且接着手工调用测试脚本。这些测试必须在一个已配置 PostgreSQL 编译树的contrib/sepgsql目录中运行。尽管它们要求一个编译树,但是 这些测试被设计成在一个已安装服务器上执行,也就是说它们可以比得上 make installcheck(而不是make check)。

首先,根据第 F.35.2 节中的指导在一个工作数据库中设置 sepgsql。注意当前操作系统用户必须能够不使用口令认证作 为超级用户连接到该数据库。

第二,为该回归测试编译和安装策略包。sepgsql-regtest策略是一个 特殊的策略包,它提供一组在回归测试浅见要被允许的规则。它应该从策略源文件 sepgsql-regtest.te编译,这需要通过使用 make和一个 SELinux 提供的 Makefile 完成。你将需要 在你自己的系统上找到合适的 Makefile,下面展示的路径只是一个例子。一旦编译好, 使用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中的规则默认没有被启用。 the 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

如果有必要,可以参考第 F.35.8 节来调整你的工作域。

最后,运行该回归测试脚本:

$ ./test_sepgsql

这个脚本将尝试验证你已经正确地完成了所有的配置步骤,接下来它将运行 sepgsql模块的回归测试。

完成测试后,推荐你禁用 sepgsql_regression_test_mode参数:

$ sudo setsebool sepgsql_regression_test_mode off

你可能想要完全移除sepgsql-regtest策略:

$ sudo semodule -r sepgsql-regtest

F.35.4. GUC 参数

sepgsql.permissive (boolean)

不管系统设置如何,这个参数让sepgsql在自由模式中运行。 默认值为关闭。这个参数只能在postgresql.conf文件中或者 服务器命令行上被设置。

当这个参数为打开时,sepgsql在自由模式中运行,即便 SELinux 运行在强制模式中也是如此。这个参数主要用于测试目的。

sepgsql.debug_audit (boolean)

不管系统策略设置如何,这个参数启用打印审计消息。默认值为关闭,表示将 根据系统设置打印消息。

SELinux的安全性策略也具有控制是否记录特定访问的 规则。默认情况下,违法访问将会被记录,但是被允许的访问则不会被记录。

这个参数强制打开所有可能的记录而不管该系统策略。

F.35.5. 特性

F.35.5.1. 控制对象类

SELinux的安全模型把所有访问控制规则描述为一个 主体(典型的是一个数据库客户端)和一个客体(例如一个数据库对象)之 间的关系, 每一个这样的关系被一个安全标签标识。如果尝试访问一个未加 标签的客体,会认为该客体被分配了标签unlabeled_t

当前,sepgsql允许把安全标签分配给模式、表、列、 序列、视图和函数。在使用sepgsql时,安全标签会 在所支持的数据库对象创建时自动分配给它们。这种标签被称为默认安全标签 并且根据系统安全性策略决定,默认安全标签被用来输入创建者标签、分配给 新对象父对象的标签以及所构造对象的可选名称。

一个新数据库对象基本上会继承父对象的安全标签,不过当安全策略具有特殊的 类型转换规则时,将会应用一个不同的标签。对于模式,其父对象是当前数据库。 对于表、序列、视图和函数,父对象是包含它的模式。对于列,其父对象是包含 它的表。

F.35.5.2. DML 权限

对于表,根据语句的种类会对所有被引用的目标表检查 db_table:selectdb_table:insertdb_table:update或者db_table:delete。此外,对于 所有其列被WHERERETURNING子句引用、作为 UPDATE的数据源(以及其他情况)的表, 都要检查db_table:select

对每一个被引用的列也将检查列级权限。不仅在使用SELECT读取列 时会检查db_column:select,在其他 DML 语句中引用列时也要检查。 对于被UPDATE或者INSERT修改的列也将检查 db_column:update或者db_column:insert

例如,考虑:

UPDATE t1 SET x = 2, y = md5sum(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.35.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.35.5.4. 可信过程

可信过程类似于 SECURITY DEFINER 函数或者 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.35.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

在上面的这个例子中,我们被允许从较大范围的c1.c1023切换到 较小范围的c1.c4,但却禁止切换回去。

动态域转换和可信过程的组合开启了一种有趣的使用案例,它适合典型的 连接池软件的处理生命周期。即便你的连接池软件不被允许运行大部分的 SQL 命令,你可以从一个可信过程中使用 sepgsql_setcon()函数允许它切换该客户端的安全标签, 这个过程应该采用一些证据来授权该请求切换该客户端标签。之后,这个 会话将会具有目标用户而不是连接池的特权。该连接池之后可以用 NULL参数再次调用sepgsql_setcon() 逆转这次安全标签改变,当然再次的调用也要在一个可信过程中配合适当 的权限检查进行。这里的要点是只有可信过程实际具有权限来更改有效的安全 标签,并且只有在得到适当的证据后才这样做。当然,对于安全操作,必须 保护证据存储(表、过程定义或者其他什么)不会受到未经授权的访问。

F.35.5.6. 杂项

我们全面拒绝LOAD命令,因为任何模块的装载都 可能很轻易地绕过安全策略的强制保护。

F.35.6. Sepgsql 函数

表 F-30展示了可用的函数。

表 F-30. Sepgsql 函数

sepgsql_getcon() returns text 返回该客户端域,也就是该客户端当前的安全标签。
sepgsql_setcon(text) returns bool 如果安全性策略允许,把当前会话的客户端域切换到一个新的域。它也接受 NULL输入,并且把它当做是切换到该客户端原始域的请求。
sepgsql_mcstrans_in(text) returns text如果 mcstrans 守护进程在运行中,把给定的限定 MLS/MCS 范围 翻译成原始格式。
sepgsql_mcstrans_out(text) returns text如果 mcstrans 守护进程在运行中,把给定的原始 MLS/MCS 范围 翻译成限定格式。
sepgsql_restorecon(text) returns bool 在当前数据库中为所有对象设置初始安全标签。参数可能是 NULL,或者是 一个被用作系统默认 specfile 替代品的 specfile 名称。

F.35.7. 限制

数据定义语言(DDL)权限

收到实现的限制,一些 DDL 操作无法检查权限。

数据控制语言(DCL)权限

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

行级访问控制

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

隐蔽通道

sepgsql不会尝试隐藏一个特定对象的存在,即便是 用户不被允许引用该对象。例如,即便我们无法得到一个不可见对象 的内容,我们也可以通过主键冲突、外键违背等等结果来推知该对象 的存在。一个绝密表的存在无法被隐藏,我们只希望能够隐藏其内容。

F.35.8. 外部资源

SE-PostgreSQL 介绍

这个 wiki 页面提供了一个简单的综述、安全性设计、架构、 管理和即将到来的特性。

Fedora SELinux User Guide

这个文档提供了广泛的知识来管理系统上的 SELinux。它主要关注 Fedora,但是并不仅限于此。

Fedora SELinux FAQ

这个文档回答了很多SELinux 的常见问题。它主要关注 Fedora,但是并不仅限于此。

F.35.9. 作者

KaiGai Kohei