F.36. sepgsql

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

Warning

当前的实现有很多限制,并且不对所有操作强制执行强制访问控制。参见Section F.36.7

F.36.1. Overview

该模块与 SELinux 集成在一起,以提供除 PostgreSQL 通常提供的功能之外的另一层安全检查。从 SELinux 的角度来看,此模块允许 PostgreSQL 充当用户空间对象 Management 器。将对照系统安全策略检查 DML 查询启动的每个表或函数访问。该检查是对 PostgreSQL 执行的常规 SQL 权限检查的补充。

SELinux 访问控制决策是使用安全性标签(由诸如system_u:object_r:sepgsql_table_t:s0的字符串表示)做出的。每个访问控制决策都涉及两个标签:尝试执行该操作的主题的标签,以及要对其执行操作的对象的标签。由于这些标签可以应用于任何种类的对象,因此数据库中存储的对象的访问控制决策可以(并通过此模块)遵循与用于任何其他类型的对象(例如文件)的相同的通用标准。此设计旨在允许一种集中式安全策略来保护信息资产,而与这些资产的存储方式无关。

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

F.36.2. Installation

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 被禁用或未安装,则必须先设置该产品,然后再安装此模块。

要构建此模块,请在 PostgreSQL configure命令中包含选项--with-selinux。确保在构建时安装了libselinux-devel RPM。

要使用此模块,必须在postgresql.confshared_preload_libraries参数中包括sepgsql。如果以任何其他方式加载,模块将无法正常运行。加载模块后,应在每个数据库中执行sepgsql.sql。这将安装安全标签 Management 所需的功能,并分配初始安全标签。

这是一个示例,显示了如何使用安装的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

这些消息是无害的,应该忽略。

如果安装过程顺利完成,则可以正常启动服务器。

F.36.3. 回归测试

由于 SELinux 的性质,对sepgsql运行回归测试需要几个额外的配置步骤,其中一些步骤必须以 root 用户身份完成。回归测试将不会通过普通的make checkmake installcheck命令运行;您必须设置配置,然后手动调用测试脚本。测试必须在已配置的 PostgreSQL 构建树的contrib/sepgsql目录中运行。尽管它们需要构建树,但是测试被设计为针对已安装的服务器执行,也就是说,它们与make installcheck而不是make check相当。

首先,按照Section F.36.2中的说明在工作数据库中设置sepgsql。请注意,当前的 os 用户必须能够以超级用户身份连接到数据库,而无需进行密码身份验证。

其次,构建并安装用于回归测试的策略包。 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中的规则; 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.36.8以获取有关调整工作域的详细信息。

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

$ ./test_sepgsql

该脚本将尝试验证您是否正确完成了所有配置步骤,然后它将对sepgsql模块运行回归测试。

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

$ sudo setsebool sepgsql_regression_test_mode off

您可能希望完全删除sepgsql-regtest策略:

$ sudo semodule -r sepgsql-regtest

F.36.4. GUC 参数

  • sepgsql.permissive ( boolean )
    • 无论系统设置如何,此参数都可使sepgsql在许可模式下运行。默认为关闭。只能在postgresql.conf文件或服务器命令行中设置此参数。

启用此参数后,即使 SELinux 通常在强制模式下运行,sepgsql也会在许可模式下运行。此参数主要用于测试目的。

  • sepgsql.debug_audit ( boolean )
    • 无论系统策略设置如何,此参数都可以打印审核消息。默认设置为关闭,这意味着将根据系统设置打印消息。

SELinux 的安全策略还具有控制是否记录特定访问的规则。默认情况下,记录访问冲突,但不记录允许的访问。

无论系统策略如何,此参数都会强制打开所有可能的日志记录。

F.36.5. Features

F.36.5.1. 受控对象类

SELinux 的安全模型将所有访问控制规则描述为主题实体(通常是数据库的 Client 端)与对象实体(例如数据库对象)之间的关系,每个实体都由安全标签标识。如果尝试访问未标记的对象,则将该对象视为已为其分配了标签unlabeled_t

当前,sepgsql允许将安全标签分配给架构,表,列,序列,视图和功能。使用sepgsql时,安全标签会在创建时自动分配给支持的数据库对象。此标签称为默认安全标签,是根据系统安全策略决定的,该策略将创建者的标签,分配给新对象的父对象的标签以及所构造对象的名称作为 Importing。

新的数据库对象基本上继承了父对象的安全标签,除非安全策略具有称为类型转换规则的特殊规则,在这种情况下,可以应用其他标签。对于模式,父对象是当前数据库。对于表,序列,视图和函数,它是包含的架构;对于列,它是包含表。

F.36.5.2. DML 权限

对于表,根据语句的类型为所有引用的目标表检查db_table:selectdb_table:insertdb_table:updatedb_table:delete;此外,还将检查db_table:select以查找包含WHERERETURNING子句中引用的列的所有表,作为UPDATE的数据源,依此类推。

还将为每个引用的列检查列级权限。不仅检查使用SELECT读取的列db_column:select,还检查其他 DML 语句中引用的列;还将检查db_column:updatedb_column:insert是否有被UPDATEINSERT修改的列。

例如,考虑:

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

在这里,将检查db_column:updatet1.x,因为它正在更新,将检查db_column:{select update}t1.y,因为它已被更新和引用,并且将检查db_column:selectt1.z,因为它仅被引用。 db_table:{select update}也将在表级别检查。

对于序列,当我们使用SELECT引用序列对象时会检查db_sequence:get_value;但是,请注意,我们目前不检查执行相应功能(例如lastval())的权限。

对于视图,将检查db_view:expand,然后将分别检查从视图扩展的对象的所有其他必需权限。

对于功能,当用户尝试执行功能作为查询的一部分或使用快速路径调用时,将检查db_procedure:{execute}。如果此函数是受信任过程,则它还会检查db_procedure:{entrypoint}许可权,以检查它是否可以作为受信任过程的入口点执行。

为了访问任何架构对象,必须对包含的架构具有db_schema:search权限。当引用没有架构资格的对象时,将不搜索没有此权限的架构(就像用户对架构没有USAGE特权一样)。如果存在显式架构限定,则如果用户对命名架构没有必需的权限,则会发生错误。

必须允许 Client 端访问所有引用的表和列,即使它们来自于随后扩展的视图,也是如此,以便我们应用一致的访问控制规则,而与引用表内容的方式无关。

默认的数据库特权系统允许数据库超级用户使用 DML 命令修改系统目录,并引用或修改 Toast 表。启用sepgsql时,禁止这些操作。

F.36.5.3. DDL 权限

SELinux 定义了几种权限来控制每种对象类型的通用操作。例如创建,更改,删除和重新标记安全标签。另外,一些对象类型具有特殊的权限来控制其 Feature 操作。例如在特定架构中添加或删除名称条目。

创建新的数据库对象需要create权限。 SELinux 将基于 Client 端的安全标签和新对象的建议安全标签来授予或拒绝此权限。在某些情况下,还需要其他特权:

  • 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权限及其新的安全标签。 (如果安装了多个标签提供程序,并且用户尝试设置安全标签,但该标签不是由 SELinuxManagement 的,则此处仅应检查setattr.由于实施限制,目前不执行此操作.)

F.36.5.4. 受信任的程序

受信任的过程类似于安全定义器函数或 setuid 命令。 SELinux 提供了一种功能,允许使用与 Client 端不同的安全标签来运行受信任的代码,通常是为了提供对敏感数据的高度受控的访问(例如,可能会省略行,或者可能会降低存储值的精度) 。函数是否充当受信任过程由其安全标签和 os 安全策略控制。例如:

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

上述操作应由 Management 用户执行。

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允许用户打印出某些数字被屏蔽的 Client 的信用卡号。

F.36.5.5. 动态域转换

如果安全策略允许,可以使用 SELinux 的动态域转换功能将 Client 端进程的安全标签(Client 端域)切换到新的上下文。Client 端域需要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()函数从受信任过程中切换 Client 端的安全标签;否则,您将无法使用该连接池。应当使用一些凭据来授权请求切换 Client 端标签。此后,此会话将具有目标用户的特权,而不是连接池的特权。连接池程序以后可以再次使用带有NULL参数的sepgsql_setcon()来还原安全标签的更改,该参数再次在具有适当权限检查的受信任过程中调用。这里的要点是,只有受信任的过程实际上才有权更改有效的安全标签,并且只有在给定适当的凭据后才能这样做。当然,为了安全操作,必须保护凭据存储区(表,过程定义或其他内容)免受未经授权的访问。

F.36.5.6. Miscellaneous

我们全面拒绝LOAD命令,因为加载的任何模块都可以轻松绕过安全策略的实施。

F.36.6. Sepgsql 函数

Table F.30显示可用功能。

表 F.30.Sepgsql 函数

sepgsql_getcon() returns text返回 Client 端域,即 Client 端的当前安全标签。
sepgsql_setcon(text) returns bool如果安全策略允许,将当前会话的 Client 端域切换到新域。它还接受NULLImporting 作为转换到 Client 端原始域的请求。
sepgsql_mcstrans_in(text) returns text如果 mcstrans 守护程序正在运行,则将给定的合格 MLS/MCS 范围转换为原始格式。
sepgsql_mcstrans_out(text) returns text如果 mcstrans 守护程序正在运行,则将给定的原始 MLS/MCS 范围转换为合格格式。
sepgsql_restorecon(text) returns bool为当前数据库内的所有对象设置初始安全标签。参数可以为 NULL,也可以是将用作系统默认名称的规范文件的名称。

F.36.7. Limitations

  • 数据定义语言(DDL)权限

    • 由于实施限制,某些 DDL 操作不检查权限。
  • 数据控制语言(DCL)权限

    • 由于实施限制,DCL 操作不检查权限。
  • 行级访问控制

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

    • sepgsql不会尝试隐藏某个对象的存在,即使不允许用户引用它也是如此。例如,即使我们无法获得对象的内容,我们也可以推断出由于主键冲突,外键冲突等导致不可见对象的存在。最高机密表的存在无法隐藏;我们只希望隐藏其内容。

F.36.8. 外部资源

F.36.9. Author

凯盖 Kohei <[email protected]>