sepgsql
是一个基于SELinux安全策略的
支持基于标签的强制访问控制(MAC)模块。
当前的实现具有明显的限制,并且不支持对所有动作的强制访问控制。详见 第 F.35.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,你就必须在安装这个模块 之前先安装或者启用它。
要编译这个模块,应该在你的 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
这些消息是无害的并且应该被忽略。
如果该安装过程完成时没有出现错误,就可以正常启动服务器了。
由于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
-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
中的规则默认没有被启用。
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
sepgsql.permissive
(boolean
)
不管系统设置如何,这个参数让sepgsql
在自由模式中运行。
默认值为关闭。这个参数只能在postgresql.conf
文件中或者
服务器命令行上被设置。
当这个参数为打开时,sepgsql
在自由模式中运行,即便
SELinux 运行在强制模式中也是如此。这个参数主要用于测试目的。
sepgsql.debug_audit
(boolean
)
不管系统策略设置如何,这个参数启用打印审计消息。默认值为关闭,表示将 根据系统设置打印消息。
SELinux的安全性策略也具有控制是否记录特定访问的 规则。默认情况下,违法访问将会被记录,但是被允许的访问则不会被记录。
这个参数强制打开所有可能的记录而不管该系统策略。
SELinux的安全模型把所有访问控制规则描述为一个
主体(典型的是一个数据库客户端)和一个客体(例如一个数据库对象)之
间的关系, 每一个这样的关系被一个安全标签标识。如果尝试访问一个未加
标签的客体,会认为该客体被分配了标签unlabeled_t
。
当前,sepgsql
允许把安全标签分配给模式、表、列、
序列、视图和函数。在使用sepgsql
时,安全标签会
在所支持的数据库对象创建时自动分配给它们。这种标签被称为默认安全标签
并且根据系统安全性策略决定,默认安全标签被用来输入创建者标签、分配给
新对象父对象的标签以及所构造对象的可选名称。
一个新数据库对象基本上会继承父对象的安全标签,不过当安全策略具有特殊的 类型转换规则时,将会应用一个不同的标签。对于模式,其父对象是当前数据库。 对于表、序列、视图和函数,父对象是包含它的模式。对于列,其父对象是包含 它的表。
对于表,根据语句的种类会对所有被引用的目标表检查
db_table:select
、db_table:insert
、
db_table:update
或者db_table:delete
。此外,对于
所有其列被WHERE
或RETURNING
子句引用、作为
UPDATE
的数据源(以及其他情况)的表,
都要检查db_table:select
。
对每一个被引用的列也将检查列级权限。不仅在使用SELECT
读取列
时会检查db_column:select
,在其他 DML 语句中引用列时也要检查。
对于被UPDATE
或者INSERT
修改的列也将检查
db_column:update
或者db_column: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
。当前由于实现限制没有这样做。)。
可信过程类似于 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
允许用户在打印客户的信用卡号时
把一些数字掩盖掉。
如果安全性策略允许,可以使用 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()
逆转这次安全标签改变,当然再次的调用也要在一个可信过程中配合适当
的权限检查进行。这里的要点是只有可信过程实际具有权限来更改有效的安全
标签,并且只有在得到适当的证据后才这样做。当然,对于安全操作,必须
保护证据存储(表、过程定义或者其他什么)不会受到未经授权的访问。
我们全面拒绝LOAD命令,因为任何模块的装载都 可能很轻易地绕过安全策略的强制保护。
表 F.29展示了可用的函数。
表 F.29. 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 名称。 |
收到实现的限制,一些 DDL 操作无法检查权限。
由于实现限制,DCL 操作不检查权限。
PostgreSQL支持行级访问,但是
sepgsql
不支持行级访问。
sepgsql
不会尝试隐藏一个特定对象的存在,即便是
用户不被允许引用该对象。例如,即便我们无法得到一个不可见对象
的内容,我们也可以通过主键冲突、外键违背等等结果来推知该对象
的存在。一个绝密表的存在无法被隐藏,我们只希望能够隐藏其内容。
这个 wiki 页面提供了一个简单的综述、安全性设计、架构、 管理和即将到来的特性。
这个文档提供了广泛的知识来管理系统上的 SELinux。它主要关注 Red Hat 操作系统,但是并不仅限于此。
这个文档回答了很多SELinux 的常见问题。它主要关注 Fedora,但是并不仅限于此。
KaiGai Kohei <kaigai@ak.jp.nec.com>