sepgsql
是一个可加载模块,支持基于 SELinux 安全策略的基于标签的强制访问控制 (MAC)。
当前实现有很大的限制,不强制实施所有操作的强制访问控制。请参见 章节 F.38.7。
此模块与 SELinux 集成,以提供超出 PostgreSQL 通常提供的安全检查的另一层安全保护。从 SELinux 的角度来看,此模块允许 PostgreSQL 充当用户空间对象管理器。对系统安全策略检查 DML 查询发起的所有表或函数访问。此检查是对 PostgreSQL 执行的常规 SQL 权限检查的补充。
SELinux 访问控制决策是使用安全标签做出的,安全标签由字符串表示,例如 system_u:object_r:sepgsql_table_t:s0
。每项访问控制决策涉及两个标签:尝试执行该操作的主体的标签和所要执行操作的对象的标签。由于这些标签可应用于任何类型的对象,因此可以将针对存储在数据库中的对象的访问控制决策(使用此模块时也是如此)置于与任何其他类型的对象(例如文件)使用的相同通用标准下。该设计的目的是允许集中安全策略保护信息资产,而不管这些资产的存储方式的具体情况。
SECURITY LABEL
语句允许将安全标签分配给数据库对象。
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 已禁用或未安装,您必须先设置该产品,然后才能安装此模块。
要构建此模块,请指定 --with-selinux
(与 make 和 autoconf 搭配使用时)或 -Dselinux={ auto | enabled | disabled }
(与 meson 搭配使用时)。请确保在构建时安装了 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
这些消息是无害的,应予以忽略。
如果安装过程没有报错完成,您现在可以正常启动服务器。
由于 SELinux 的特性,运行 sepgsql
的回归测试需要几个额外的配置步骤,其中一些必须以 root 用户身份进行。普通 make check
或 make installcheck
命令不会运行回归测试;您必须设置配置,然后手动调用测试脚本。必须在已配置的 PostgreSQL 构建目录树的 contrib/sepgsql
目录中运行测试。虽然它们需要一个构建目录树,但测试设计为对已安装的服务器执行,也就是说,它们可与 make installcheck
相比,而不能与 make check
相比。
首先,根据 第 F.38.2 节 中的说明,在工作数据库中设置 sepgsql
。请注意,当前操作系统用户必须能够在不使用密码验证的情况下以超级用户身份连接到数据库。
其次,为回归测试构建并安装策略包。 sepgsql-regtest
策略是一个特殊用途的策略包,它提供了一组规则,允许在回归测试期间执行。它应该从策略源文件 sepgsql-regtest.te
构建,该文件使用 SELinux 提供的 Makefile 使用 make
进行构建。您需要在系统上找到合适的 Makefile;下面显示的路径只是一个示例。(此 Makefile 通常由 selinux-policy-devel
或 selinux-policy
RPM 提供。)构建后,使用 semodule
命令安装此策略包,该命令将提供的策略包加载到内核中。如果包安装正确,
应该将 semodule
-lsepgsql-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.38.8 节,了解如何调整工作域的详细信息。
最后,运行回归测试脚本
$ ./test_sepgsql
此脚本将尝试验证您是否正确完成了所有配置步骤,然后将运行 sepgsql
模块的回归测试。
完成测试后,建议禁用 sepgsql_regression_test_mode
参数。
$ sudo setsebool sepgsql_regression_test_mode off
您可能更喜欢完全移除 sepgsql-regtest
策略。
$ sudo semodule -r sepgsql-regtest
sepgsql.permissive
(boolean
) 无论系统设置如何,此参数都允许 sepgsql
在允许模式中运作。默认为关闭。此参数只能在 postgresql.conf
文件或服务器命令行中设置。
当此参数处于开启状态时,即使 SELinux 通常在强制模式下工作,sepgsql
也会在允许模式下运作。此参数主要用于测试目的。
sepgsql.debug_audit
(boolean
) 启用此参数会输出审计消息,而不管系统策略设置如何。默认为关闭,这意味着将根据系统设置打印消息。
SELinux 的安全策略也具有规则,用于控制是否记录特定访问。默认情况下,会记录访问冲突,但不会记录允许的访问。
无论系统策略如何,此参数都会强制开启所有可能的日志记录。
SELinux 的安全模型将所有访问控制规则描述为主题实体(通常是数据库的客户端)和对象实体(例如数据库对象)之间的关系,每个实体均由安全标签标识。如果尝试访问未标记的对象,则该对象将被视为已分配标签 unlabeled_t
。
当前,sepgsql
允许将安全标签分配给模式、表、列、序列、视图和函数。在使用 sepgsql
期间,系统会在创建受支持的数据库对象时自动为其分配安全标签。此标签称为默认安全标签,是根据系统安全策略来决定的。该策略以创建者的标签、分配给新对象的父对象的安全标签为输入,且可选地以构造对象的名称为输入。
新建的数据库对象常继承父对象的安全性标签,除非安全性政策具有称为类型转换规则这样的特殊规则,否则可能应用不同的标签。对于模式,父对象是当前数据库;对于表、序列、视图和函数,它是包含模式;对于列,它是包含表。
对于表,将根据语句类型对所有引用的目标表检查 db_table:select
、db_table:insert
、db_table:update
或 db_table:delete
;此外,还将检查 db_table:select
,以检查 WHERE
或 RETURNING
子句中引用的所有表,作为 UPDATE
的数据源,依此类推。
还将针对每个引用的列检查列级别权限。db_column:select
不仅针对使用 SELECT
读取的列检查,还针对其他 DML 语句中引用的列检查;db_column:update
或 db_column:insert
还将针对由 UPDATE
或 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
被启用时,禁止这些操作。
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
。由于实现限制,目前尚未完成此操作。)
受信任的程序类似于安全定义程序或 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
允许用户在对部分数字进行掩码处理的情况下打印客户的信用卡号。
如果安全策略允许,可以使用 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()
函数来允许该软件切换客户端的安全标签;应获取一些凭证才能授权切换客户端标签的请求。此后,此会话将具有目标用户而不是连接池的权限。连接池以后可以通过再次使用 sepgsql_setcon()
及 NULL
参数恢复安全标签更改,同样也可在具有适当权限检查的可信过程中调用。此处的目的是,只有可信过程实际上具有更改有效安全标签的权限,而且只有在给定适当凭证后才会这样做。当然,要进行安全操作,凭证存储(表、过程定义或其他)必须受到保护,以防止未经授权的访问。
我们全面拒绝 LOAD
命令,因为加载的任何模块都可以轻松规避安全策略实施。
表 F.30 显示可用函数。
表 F.30. Sepgsql 函数
函数 说明 |
---|
返回客户端域,即客户端当前的安全标签。 |
如果安全策略允许,将切换当前会话的客户端域到新的域。它也接受 |
如果 mcstrans 守护进程正在运行,将给定的合格 MLS/MCS 范围转换为原始格式。 |
如果 mcstrans 守护进程正在运行,将给定的原始 MLS/MCS 范围转换为合格格式。 |
为当前数据库中的所有对象设置初始安全标签。该参数可以是 |
由于实现限制,一些 DDL 操作不会检查权限。
由于实现限制,DCL 操作不会检查权限。
PostgreSQL 支持行级访问,但sepgsql
不支持。
sepgsql
不会尝试隐藏特定对象的已存在,即使该用户没有引用该对象的权限。例如,我们可推断隐藏对象的已存在,其原因在于主键冲突、外键违规等,即使无法获取该对象的具体内容。不能隐藏极机密表的存在;我们只能希望隐藏它的具体内容。
此 wiki 页面提供了简要概览、安全设计、架构、管理和即将到来的功能。
本文档提供广泛的知识,可用于管理您系统上的SELinux。它主要关注 Red Hat 操作系统,但这并非其限制。
本文档回答了关于SELinux的常见问题。它主要关注 Fedora,但并未限制于 Fedora。
KaiGai Kohei <[email protected]>