Postgresql 中文操作指南

F.40. sepgsql — SELinux-, label-based mandatory access control (MAC) security module #

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

Warning

当前实现有明显的限制,并不对所有操作实施强制访问控制。请参阅 Section F.40.7

F.40.1. Overview #

此模块与 SELinux 集成以提供一层附加安全检查,这超出了 PostgreSQL 通常提供的安全性。从 SELinux 的角度来看,此模块允许 PostgreSQL 充当用户空间对象管理器。DML 查询启动的每次表或函数访问都会根据系统安全策略进行检查。此检查是除了 PostgreSQL 执行的常规 SQL 权限检查之外的附加检查。

SELinux 访问控制决定是使用安全标签做出的,安全标签由诸如 system_u:object_r:sepgsql_table_t:s0 的字符串表示。每个访问控制决定涉及两个标签:尝试执行操作的主体的标签,以及要执行操作的对象的标签。由于这些标签可应用于任何类型的对象,所以可以为存储在数据库中的对象做出访问控制决定(且使用此模块后,将受到访问控制决定),访问控制决定遵循用于任何其他类型对象(如文件)的相同通用标准。此设计旨在允许集中式安全策略保护信息资产,而不论这些资产如何存储。

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

F.40.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.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

请注意,根据您拥有的 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.40.3. Regression Tests #

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

首先,根据 Section F.40.2中的说明,在工作数据库中设置 sepgsql。请注意,当前操作系统用户必须能够连接到数据库,并以超级用户身份连接,而不进行密码身份验证。

其次,为回归测试构建并安装策略包。 sepgsql-regtest 策略是一个特殊用途的策略包,它提供了一组在回归测试期间允许的规则。它应从策略源文件 sepgsql-regtest.te 中构建,使用 SELinux 提供的 Makefile 完成 make 。您需要在系统上找到适当的 Makefile;下面显示的路径仅是示例。(通常由 selinux-policy-develselinux-policy RPM 提供此 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.40.8,以了解如何根据需要调整你的工作域的详细信息。

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

$ ./test_sepgsql

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

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

$ sudo setsebool sepgsql_regression_test_mode off

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

$ sudo semodule -r sepgsql-regtest

F.40.4. GUC Parameters #

  • sepgsql.permissive (boolean) #

    • 此参数能让 sepgsql 以宽容模式运行,而不管系统设置如何。默认情况下关闭。此参数只能在 postgresql.conf 文件或服务器命令行中设置。

    • 当打开此参数时,sepgsql 以宽容模式运行,即便 SELinux 在执行模式中运行也是如此。此参数主要是出于测试目的才有用。

  • sepgsql.debug_audit (boolean) #

    • 此参数允许打印审计消息,而不受系统策略设置的影响。默认情况下关闭,这意味着消息将按照系统设置打印。

    • SELinux 的安全策略也有规则來控制是否记录特定访问。默认情况下,会记录违规行为,但不会记录允许的访问。

    • 此参数强制针对所有可能的日志记录都打开,而不论系统策略为何。

F.40.5. Features #

F.40.5.1. Controlled Object Classes #

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

目前,sepgsql 允许将安全标签分配给模式、表格、列、序列、视图和函数。当 sepgsql 正在使用中时,会自动在创建时向受支持的数据库对象分配安全标签。此标签称为默认安全标签,并根据系统安全策略来决定,该策略以创建者的标签、分配给新对象的父对象和可选的构造对象的名称作为输入。

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

F.40.5.2. DML Permissions #

对于数据表,db_table:selectdb_table:insertdb_table:updatedb_table:delete 会针对所有引用的目标数据表进行 checked,具体取决于语句的类型;此外,对于 WHERERETURNING 子句中引用的列包含的数据表,db_table:select 也会针对其进行 checked,例如作为 UPDATE 的数据源。

针对每个引用的列也应该 checked 列级别权限。db_column:select 不仅对使用 SELECT 读取的列进行 checked,也对其他 DML 语句中引用的列进行 checked;db_column:updatedb_column:insert 也应该针对被 UPDATEINSERT 修改的列进行 checked。

例如,考虑:

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

这里,db_column:update 将针对 t1.x 进行 checked,因为它正在更新;db_column:{select update} 将针对 t1.y 进行 checked,因为它同时会被更新和引用;db_column:select 将针对 t1.z 进行 checked,因为它只会被引用。db_table:{select update} 也应该针对表级别进行 checked。

对于序列,在使用 SELECT 引用序列对象时将会检查 db_sequence:get_value;但是,请注意我们当前不会检查对于 lastval() 等相应函数执行的权限。

对于视图,将会检查 db_view:expand,然后将单独地检查从视图展开的对象上的任何其他必需权限。

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

为了访问任何模式对象,需要在包含模式上对 db_schema:search 有权限。未提供此权限的模式在引用对象却没有模式限定符的情况下,将不会被搜索(就像用户在模式上没有 USAGE 权限一样)。如果指定了明确的模式限定符,而用户在指定的模式上没有必需的权限,则会发生错误。

必须允许客户端访问所有引用的表和列,即使它们源自随后展开的视图,这样我们就可以应用与表内容引用方式无关的一致访问控制规则。

默认数据库权限系统允许数据库超级用户使用 DML 命令修改系统目录,以及引用或修改 toast 表。如果启用了 sepgsql,则禁止这些操作。

F.40.5.3. DDL Permissions #

SELinux 定义了几个权限来控制每种对象类型的常见操作;比如创建、更改、删除和重贴安全标签。此外,几个对象类型具有特殊权限来控制它们的特征操作;比如在特定模式内添加或删除名称条目。

创建新数据库对象需要 create 权限。SELinux 依据客户端的安全标签和新对象的提议安全标签授予或拒绝此权限。在某些情况下,需要获得其他权限:

执行 DROP 命令时,将会在要删除的对象上检查 drop。还需要检查通过 CASCADE 间接删除的对象的权限。此外,删除特定模式中包含的对象(表、视图、序列和过程)需要该模式上的 remove_name

执行 ALTER 命令时,将会为每种对象类型检查要修改的对象上的 setattr,但是子对象(如表的索引或触发器)例外,因为这时候将在父对象上检查权限。在某些情况下,需要获得其他权限:

F.40.5.4. Trusted Procedures #

受信任的过程与安全定义程序函数或 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.40.5.5. Dynamic Domain Transitions #

如果安全策略允许,可以使用 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.40.5.6. Miscellaneous #

我们全面拒绝 LOAD 命令,因为加载的任何模块都可以轻易规避安全策略强制执行。

F.40.6. Sepgsql Functions #

Table F.31显示了可用功能。

Table F.31. Sepgsql Functions

Function

Description

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.40.7. Limitations #

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

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

  • 数据控制语言 (DCL) 权限

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

  • Row-level access control

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

  • Covert channels

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

F.40.8. External Resources #