F.34. sepgsql

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

警告

当前的实现有很大的局限性,并不为所有的动作都执行强制访问控制。 参阅第 F.34.7 节

F.34.1. 概述

这个模块与SELinux结合,提供一个PostgreSQL 正常提供的安全检查的附加层。从SELinux来看,这个模块允许 PostgreSQL起到用户空间对象管理者的作用。 DML查询发起的每个表和函数访问都将针对系统安全策略做检查。 这个检查是PostgreSQL执行的通常的SQL权限检查之外的东西。

SELinux访问控制的决策是利用安全标签做出的, 安全标签以字符串的形式表示,如system_u:object_r:sepgsql_table_t:s0。 每个访问控制决策包含两个标签:尝试执行动作的主体的标签, 和要被执行操作的对象的标签。因为这些标签可以用于任意类型的对象, 所以存储在数据库中的对象的访问控制决策和任何其他类型的对象(比如,文件)一样, 会服从相同的通用标准(用这个模块是这样的)。 这个设计是为了允许集中的安全策略对信息资产的保护独立于这些资产的存储细节。

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

F.34.2. 安装

sepgsql只能用于Linux 2.6.28 或更高的启用SELinux的系统。 在其他的平台上不能使用。也需要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或者没有安装SELinux, 那么必须在安装这个模块之前先配置该产品。

要编译生成这个模块,在你的PostgreSQL configure命令中包含选项 --with-selinux。确保在编译时已经安装了 libselinux-devel RPM。

要使用这个模块,必须在postgresql.confshared_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.34.3. 回归测试

由于SELinux的性质,为sepgsql 运行回归测试需要几个额外的配置步骤,其中一些必须由root用户完成。 回归测试不通过一个普通的make checkmake installcheck 命令来运行;你必须设置配置,然后手动的调用测试脚本。 测试必须在configure过了的PostgreSQL编译树的contrib/sepgsql目录中运行。 尽管它们需要一个编译树,但是测试是针对一个已经安装的服务器执行的, 这一点它们类似于make installcheck而不是make check

第一步,根据第 F.34.2 节中的指示, 在一个运行的数据库中设置sepgsql。 请注意,当前的操作系统用户必须能够作为超级用户不需要密码认证的连接到数据库。

第二步,为回归测试编译和安装策略包。sepgsql-regtest策略是一个特定用途的策略包, 提供了一组在回归测试期间被允许的规则。应该使用make和SELinux提供的Makefile,从策略源文件sepgsql-regtest.te编译生成。 你需要在你的系统上定位适当的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中的规则缺省是不启用的; 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.34.8 节获取调整你的工作域的详细信息。

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

$ ./test_sepgsql

这个脚本将尝试验证你已经正确的做了所有的配置步骤, 然后它将为sepgsql模块运行归回测试。

完成测试之后,建议你禁用sepgsql_regression_test_mode参数:

$ sudo setsebool sepgsql_regression_test_mode off

你可能想要彻底删除sepgsql-regtest策略:

$ sudo semodule -r sepgsql-regtest

F.34.4. GUC 参数

sepgsql.permissive (boolean)

这个参数使得sepgsql能够在许可的模式运行,无视系统的设置。 缺省为off。这个参数只能在postgresql.conf 文件中或者在服务器的命令行设置。

当这个参数为on时,sepgsql函数在许可模式, 即使SELinux通常运行在强制模式。这个参数对于测试目的尤其有用。

sepgsql.debug_audit (boolean)

这个参数启用审计消息的输出,无视系统策略设置。缺省是off, 意味着消息将根据系统设置来输出。

SELinux的安全策略也有规则控制是否记录详细的访问。 缺省的,只记录违规的访问。

这个参数强制打开所有可能的记录,无视系统的策略。

F.34.5. 特性

F.34.5.1. 控制对象类

SELinux的安全模型描述了所有的访问控制规则, 作为主体的实体(通常,数据库的一个客户端)和客体的实体(比如一个数据库对象) 的关系,每一个都由安全标签来鉴别。如果尝试访问一个没有标签的对象, 那么该对象被当做是分配了unlabeled_t标签。

目前,sepgsql允许分配安全标签给模式、表、字段、序列、 视图和函数。当正在使用sepgsql, 创建对象时会自动分配安全标签给支持的数据库对象。 这个标签被称为缺省的安全标签,并且是根据系统的安全策略决定的。 系统的安全策略使用创建人的标签,分配给新对象的父对象的标签以及可选的被构造对象的名称作为输入。

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

F.34.5.2. DML 权限

对于表,根据语句的类型,db_table:selectdb_table:insertdb_table:updatedb_table:delete会为所有引用的目标表做检查; 另外,db_table:select 也为所有包含WHERERETURNING子句中的引用字段的表做检查, UPDATE的数据源也是如此,等等。

也将为每个引用的字段检查字段级别的权限。db_column:select 不只是检查被SELECT读取的字段,也检查在其他DML语句中引用的字段; db_column:updatedb_column:insert 也将检查被UPDATEINSERT修改的字段。

例如,考虑:

UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;

这里,db_column:update将为t1.x做检查, 因为它被更新了,db_column:{select update} 将为t1.y做检查,因为它被更新和引用了, db_column:select将为t1.z做检查, 因为它被引用了。db_table:{select update} 也将在表级别做检查。

对于序列,当我们使用SELECT引用一个序列对象时,对 db_sequence:get_value做检查;不过,请注意, 我们当前不对相应的函数的执行做权限检查,如lastval()

对于视图,将检查db_view:expand, 然后任何其它需要的权限都将分别在从视图扩展的对象上做检查。

对于函数,当用户尝试将函数作为查询的一部分执行,或使用fast-path调用时, 会对db_procedure:{execute}做检查。如果这个函数是一个受信任的程序, 那么也会检查db_procedure:{entrypoint}的权限, 看看它是否可以作为受信任的程序的入口点来执行。

为了访问任何模式对象,在包含的模式上需要有db_schema:search权限。 当被引用的对象没有模式限定时,没有这个权限的模式将不会被搜索 (就好像用户在这个模式上没有USAGE权限)。 如果给出了明确的模式限定,并且用户在命名的模式上没有必需的权限, 那么将会引发一个错误。

客户端必须被允许访问所有引用的表和字段,即使它们起源于随后扩张的视图, 所以我们应用一致的访问控制规则,独立于引用表内容的方式。

缺省的数据库权限系统允许数据库超级用户使用DML命令修改系统表, 引用和修改toast表。当启用sepgsql时,禁止这些操作。

F.34.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.34.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.34.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.34.5.6. 其他

我们彻底拒绝LOAD命令, 因为加载的任何模块都能很容易的绕开安全策略实施。

F.34.6. Sepgsql 函数

表 F-27显示了可用的函数。

表 F-27. 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守护进程正在运行,则转化给出的行MCS/MCS范围为限定的格式。
sepgsql_restorecon(text) returns bool 为当前数据库中所有的对象设置初始的安全标签。参数可以是NULL, 或者是specfile的名字,作为系统缺省值的替代。

F.34.7. 限制

数据定义语言 (DDL) 权限

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

数据控制语言 (DCL) 权限

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

行级别的访问控制

PostgreSQL不支持行级别的访问;因此sepgsql也不支持。

隐藏通道

sepgsql没有试图隐藏某一对象的存在,即使不允许用户访问它。 例如,我们可以推断一个不可见对象的存在,根据主键冲突、外键冲突等, 虽然我们不能获得该对象的内容。最高机密表的存在不能被隐藏;我们只希望能隐藏它的内容。

F.34.8. 外部资源

SE-PostgreSQL 介绍

这个wiki页提供了一个简要概述、安全设计、构造、管理和即将到来的特性。

Fedora SELinux 用户指南

这个文档提供了在你的系统上管理SELinux的广泛的知识。 它主要集中于Fedora,但是不局限于Fedora。

Fedora SELinux FAQ

这个文档回答了关于SELinux的常见问题。 它主要集中于Fedora,但是不局限于Fedora。

F.34.9. 作者

KaiGai Kohei