快速开始编写自定义 SELinux 策略
Table of Contents
要在系统上运行受 SELinux 限制的新应用程序,请准备自定义策略文件来补充发行版 SELinux 策略强制执行的规则。
生成策略模板
policycoreutils-devel
包提供的sepolicy 生成
工具可帮助您执行初始步骤。该工具生成一个基本策略模块、一个 Makefile 和一个安装脚本。该脚本构建并安装策略模块,并重新标记 .fc 文件中定义的路径。您可以作为普通用户使用该命令,例如:
$ sepolicy generate --init <path_to_my_app_binary>
有关更多详细信息,请参阅 RHEL 使用 SELinux 文档中的为自定义应用程序创建和实施 SELinux 策略部分。
如果您更喜欢图形界面和向导,可以使用policycoreutils-gui
包提供的selinux-polgengui
工具。
生成的策略模板包含以下策略文件:
- <mypolicy>.te - 定义策略规则以及应用程序使用的新类型和域
- <mypolicy>.fc - 包含文件上下文定义,换句话说,用于标记与应用程序相关的文件的说明
- <mypolicy>.if - 包含接口,这些接口是其他策略模块用来与此策略交互的策略宏
根据应用程序行为允许访问
在系统上安装初始策略后,您可以根据应用程序所需的访问权限对其进行改进。
要在日志中包含所有访问的路径,请启用完整审核:
# audictl -d never,task
# auditctl -w /etc/ -p w
或者,打开/etc/audit/rules.d/audit.rules
文件,删除“-a task,never”(如果存在),添加“-w /etc/ -p w”,然后重新启动审核守护程序。
要记录所有违反 SELinux 策略的行为,请将 SELinux 切换到宽容模式:
# setenforce 0
setenforce 0
命令将整个系统上的 SELinux 设置为宽松模式,直到下次重新启动为止。您可以仅将新域切换到宽容模式,并将 SELinux 策略的其余部分保持在强制模式:
# semanage permissive -a <myapp_t>
请注意, sepolicygenerate
命令将宽松的 <myapp_t>
规则添加到策略模块中。因此,如果您保留了该规则,则可以跳过此步骤。
启用完全审核和宽容模式后,在新的 SELinux 域中运行应用程序。要查看应用程序所需的所有访问权限,请测试尽可能多的用例。系统以AVC (SELinux访问拒绝日志)的形式记录当前策略中不允许的所有访问请求。
处理 AVC
当 SELinux 拒绝某个操作时,系统会将访问向量缓存 (AVC) 消息添加到/var/log/audit/audit.log
和/var/log/messages
文件中,或者 Journal 守护程序会记录拒绝。
您可以使用ausearch
命令显示最近的AVC ,例如:
# ausearch -m AVC,USER_AVC -ts recent
-ts 最近
参数将搜索限制为最近 10 分钟,但您可以使用时间戳来代替。
Audit2allow
工具可以处理 AVC 并为我们的策略生成相应的允许规则。
每当您的自定义规则与其他策略包中定义的资源交互时,如果可能,请使用接口而不是简单的允许规则。Audit2allow -R
命令尝试查找覆盖用例的接口。您必须检查此命令的每个结果,以确保生成的策略不会太宽松。
政策宏
策略宏也称为模式,或者在更复杂的情况下称为接口,它生成多个规则,这些规则一起工作以允许某些用例。
示例:
- 接口:
mysql_read_config(<domain>)
- 授予指定域(本例中为 httpd_t)对 MySQL 配置文件的读取访问权限init_daemon_domain(<domain>, <file_type>)
- 提升作为域的第一个参数给出的类型,而被 init 守护进程用作入口点可执行文件
- 图案:
rw_files_pattern(<domain>, <dir_type>, <file_type>)
- 给出对标记的文件进行读写访问 位于标记为的目录中 domtrans_pattern(<source>, <entrypoint>, <target>)
- 设置自动域转换:当进程在域中运行时
Fedora、RHEL 和 CentOS 中可用的接口在 selinux-policy Github 存储库的模块部分中定义。每个策略模块都包含一组接口,作为其他模块访问该模块中定义的资源的手段。
每个宏都有必须指定的固定数量的参数。如果您不确定相应的参数,请检查宏主体中的参数引用,其形式为$
字符后跟索引(例如$1
)。或者,检查selinux-policy-doc
包提供的/usr/share/doc/selinux-policy/html/interfaces.html
文件中的接口索引。
可选政策
每当您使用贡献模块之一中定义的接口时,您必须将其包含在可选策略块中。
例如,Apache 模块使用kerberos 模块中定义的kerberos_read_keytab接口:
optional_policy(`
kerberos_read_keytab(httpd_t)
')
optional_policy块确保您可以在没有可选模块的情况下使用策略模块。在这种情况下, apache模块可以在删除了kerberos模块的系统上使用。如果您省略Optional_policy块,则当您授予其资源访问权限的任何模块丢失时,安装新策略模块会导致错误。
扩展宏
如果名称不言自明, selinux-policy-devel
包提供的宏扩展器
工具会显示每个宏包含的内容。使用带有完整宏名称(包括所有参数)的Macro-expander
来查看宏生成的所有允许规则,例如:
# macro-expander "mysql_read_config(httpd_t)"
allow httpd_t mysqld_etc_t:dir { getattr search open read lock ioctl };
allow httpd_t mysqld_etc_t:file { open { getattr read ioctl lock } };
allow httpd_t mysqld_etc_t:lnk_file { getattr read };
请注意,宏扩展器
列表默认情况下仅允许规则。要查看宏的完整内容,例如 type_transition 规则或属性分配,请使用Macro-expander -M <>
并忽略模块头和所有require块:
$ macro-expander -M "domtrans_pattern(<source>, <entrypoint>, <target>)"
module expander 1.0.0;
require {
role system_r;
...
}
allow <source> <entrypoint>:file { getattr open map read execute ioctl execute_no_trans };
allow <source> <target>:process transition;
type_transition <source> <entrypoint>:process <target>;
allow <target> <source>:fd use;
allow <target> <source>:fifo_file { getattr read write append ioctl lock };
allow <target> <source>:process sigchld;
权限集
与其他策略宏不同,权限集不使用任何参数,仅用作通常一起使用的权限集的简写。
示例:
- rw_file_perms - { 打开 getattr 读写附加 ioctl 锁 }
- create_dir_perms - { getattr create }
- append_fifo_file_perms - { getattr 打开附加锁 ioctl }
所有权限集均在obj_perm_sets.spt文件中定义。由于宏扩展器
默认仅显示允许规则,因此您必须按如下方式调整命令以扩展独立权限集:
$ macro-expander -M "manage_blk_file_perms" | tail -n 1
{ create open getattr setattr read write append rename link unlink ioctl lock }
在这种情况下,带有-M
选项的宏扩展器
会生成包含查询的完整模块,您可以忽略模块头和require
块。
或者,提供“虚拟”允许规则,例如:
$ macro-expander "allow _ _:_ manage_blk_file_perms"
allow _ _:_ { create open getattr setattr read write append rename link unlink ioctl lock }
缺少接口
如果您想要允许不存在接口的有效访问要求,请使用gen_require
语句解决该问题。
在以下示例中, dhcpd策略模块需要基于audit2allow -R
命令输出中列出的 AVC 的附加访问权限:
$ audit2allow -R
type=AVC msg=audit(1674838508.590:840): avc: denied { write } for pid=2383 comm="dhcpd" name="bluetooth.conf" dev="vda2" ino=2 scontext=system_u:system_r:dhcpd_t:s0 tcontext=system_u:object_r:bluetooth_conf_t:s0 tclass=file permissive=1
require {
type bluetooth_conf_t;
type dhcpd_t;
class file write;
}
#============= dhcpd_t ==============
allow dhcpd_t bluetooth_conf_t:file write;
不存在用于写入bluetooth_conf_t文件的接口,但快速搜索 selinux-policy Github 存储库上的bluetooth.if文件会显示bluetooth_read_config ,您可以针对此用例进行修改:
interface(`bluetooth_read_config',`
gen_require(`
type bluetooth_conf_t;
')
allow $1 bluetooth_conf_t:file read_file_perms;
')
您可以通过将read_file_perms
替换为write_file_perms
1并填写接口参数 ( $1
-> dhcpd_t
) 来修复丢失的访问权限。
您还必须使用optional_policy
块包围添加的策略 - 就像您使用蓝牙模块中定义的实际接口一样。
optional_policy(`
gen_require(`
type bluetooth_conf_t;
')
allow dhcpd_t bluetooth_conf_t:file write_file_perms;
')
进程上下文(域)
分配给正在运行的进程的类型通常称为域。默认情况下,新进程继承其父进程的上下文。例如,用户在unconfined_t
域中运行的 Bash shell 中输入cat
命令会导致一个新进程也作为unconfined_t
运行。
您可以使用type_transition规则更改此行为,该规则是domtrans_pattern
(域转换模式)的一部分:
domtrans_pattern(<source>, <entrypoint>, <target>)
此模式使用type_transition规则定义到<target>
域的自动转换,并添加一组允许转换的规则。它通常用作init_daemon_domain(<target>, <entrypoint>)
接口的一部分,以促进init_t
(init 守护进程域)和新创建的域之间的转换。
文件上下文
新文件继承分配给创建它们的目录的类型。要在不更改应用程序代码的情况下分配自定义类型,请使用filetrans_pattern
宏:
filetrans_pattern(<process_domain>, <directory>, <target_type>, <class(es)>, [<name>])
其中:
- <class(es)> - 规则适用的对象类(文件、目录、套接字等)
- [
] - 将规则限制为给定名称的对象(可选)
例如:
filetrans_pattern(httpd_t, var_t, httpd_cache_t, file, "apache_cache")
当以httpd_t
身份运行的进程在标记为var_t
的目录中创建名为apache_cache
的文件时,系统会在生成的文件上设置httpd_cache_t
标签。
您可以在更专门的宏中使用filetrans_pattern
,其中<directory>
参数是根据特定位置预先填充的,例如:
files_var_filetrans(httpd_t, httpd_cache_t, { file dir })
有关更多详细信息,请参阅《RHEL 7 SELinux 用户和管理员指南》中的文件名转换部分。
默认文件上下文定义
所有策略模块都包含一个文件上下文定义文件<module>.fc
,它确定其资源的默认 SELinux 上下文。此上下文由标记实用程序使用,例如matchpathcon或Restorecon 。
每个定义都具有以下格式:
pathname_regexp [file_type] security_context
其中:
- pathname_regexp - 定义可能采用正则表达式形式的路径名
- file_type - 表示对象类,可以为空(表示所有对象类),也可以为以下之一:
- -b - 块设备
- -c - 字符设备
- -d - 目录
- -p - 命名管道 (FIFO)
- -l - 符号链接
- -s - 套接字文件
- ---普通文件
- security_context - 分配给路径的默认安全上下文
有关更多详细信息,请参阅《RHEL 7 SELinux 用户和管理员指南》中的SELinux 上下文 - 标记文件部分。
CIL 错误故障排除
当您将编译的策略模块加载到系统上时,编译器会将模块代码翻译为通用中间语言 ( CIL )。这意味着加载模块时遇到的大多数错误都会根据策略的 CIL 版本进行报告,如以下简单策略示例所示,该策略的类型定义已存在于系统策略中:
$ sepolicy generate --init /usr/bin/cat
$ echo "type abrt_t;" >> cat.te
$ sudo make -f /usr/share/selinux/devel/Makefile cat.pp
Compiling targeted cat module
Creating targeted cat.pp policy package
rm tmp/cat.mod.fc tmp/cat.mod
$ sudo semodule -i cat.pp
Re-declaration of type abrt_t
Previous declaration of type at /var/lib/selinux/targeted/tmp/modules/400/cat/cil:6
Bad type declaration at /var/lib/selinux/targeted/tmp/modules/400/cat/cil:6
Failed to build AST
semodule: Failed!
由于一些与CIL相关的错误消息不明确,您可能需要检查模块源代码的CIL翻译中的相应行。在本例中,错误与第 6 行有关。使用pp
工具将策略二进制文件转换为 CIL,并显示有问题的行:
$ cat cat.pp | /usr/libexec/selinux/hll/pp > cat.cil
$ head -6 cat.cil | tail -1
(type abrt_t)
有关 CIL 及其关键字与源策略语言有何不同的更多信息,请参阅 SELinux 项目 wiki 上的CIL 策略语言部分。
政策示例
通过执行以下示例步骤,您可以为bootupd
包创建并测试新的 SELinux 策略。该软件包包含一项现在不受限制的服务。
显示bootupd
软件包安装的所有文件:
$ rpm -ql bootupd
/usr/bin/bootupctl
/usr/lib/.build-id
/usr/lib/.build-id/e8
/usr/lib/.build-id/e8/3c00b4a3c33f2858d6730e9ba95d8286ab6dde
/usr/lib/.build-id/e8/3c00b4a3c33f2858d6730e9ba95d8286ab6dde.1
/usr/lib/systemd/system/bootupd.service
/usr/lib/systemd/system/bootupd.socket
/usr/libexec/bootupd
/usr/share/doc/bootupd
/usr/share/doc/bootupd/README.md
/usr/share/licenses/bootupd
/usr/share/licenses/bootupd/LICENSE
在列出的文件中,您可以看到两个单元文件以及两个二进制文件, bootupd
和bootupctl
(远程命令行界面)。您可以忽略其余文件来准备新的 SELinux 策略。
bootupd.socket
文件指示该服务使用/var/run/bootupd.sock
套接字。由于bootupd
服务的单元文件使用 init 系统,因此请将--init
参数与sepolicygenerate
命令结合使用。根据bootupd.service
文件的内容,您知道/usr/libexec/bootupd
是服务二进制文件。您使用其余文件(单元和套接字文件)作为--writepath
参数的值,以便sepolicy generated
命令将它们包含在策略中:
$ sepolicy generate --init /usr/libexec/bootupd -w /usr/lib/systemd/system/bootupd.service /usr/lib/systemd/system/bootupd.socket /var/run/bootupd.sock
Created the following files:
/home/user/bootupd/bootupd.te # Type Enforcement file
/home/user/bootupd/bootupd.if # Interface file
/home/user/bootupd/bootupd.fc # File Contexts file
/home/user/bootupd/bootupd_selinux.spec # Spec file
/home/user/bootupd/bootupd.sh # Setup Script
bootupd.te
类型强制文件包含四种新类型:
* bootupd_t
- bootupd
进程的域
* bootupd_exec_t
- bootupd
二进制文件类型
* bootupd_var_run_t
- /var/run/bootupd.sock
套接字的类型
* bootupd_unit_file_t
- 两个单元文件的文件类型
init_daemon_domain(bootupd_t, bootupd_exec_t)
宏确保当 init 守护进程执行bootupd
二进制文件时,生成的进程在bootupd_t
域中运行。
生成的模块的其余部分主要是 init 守护进程策略模板,除了授予bootupd_var_run_t
访问权限的宏组之外:
* manage_dirs_pattern(bootupd_t, bootupd_var_run_t, bootupd_var_run_t)
- 管理标记为bootupd_var_run_t 的
目录
* Manage_files_pattern(bootupd_t, bootupd_var_run_t, bootupd_var_run_t)
- 管理标记为bootupd_var_run_t 的
文件
* manage_lnk_files_pattern(bootupd_t, bootupd_var_run_t, bootupd_var_run_t)
- 管理标记为bootupd_var_run_t 的
链接文件
* files_pid_filetrans(bootupd_t, bootupd_var_run_t, { dir file lnk_file })
- 如果bootupd_t
在标记为var_run_t
的目录中创建文件,则生成的文件标记为bootupd_var_run_t
/var/run/bootupd.sock
路径不存在,因此sepolicygenerate
生成目录、文件和链接文件的访问宏。因为/var/run/bootupd.sock
是一个套接字文件,所以您可以删除所有三个manage_*
模式。selinux-policy Github 存储库上的file_patterns.spt文件包含多种可供您使用的套接字模式,例如rw_sock_files_pattern
。
由于套接字文件是由systemd
(而不是bootupd
)作为使用systemctl start bootupd.socket
的结果生成的,因此files_pid_filetrans
宏也是多余的。如果您的应用程序二进制文件生成套接字文件,则必须将 { dir file lnk_file } 替换为
{ sock_file } ,而不是删除带有 files_pid_filetrans` 的行
。
bootupd.fc
文件上下文配置文件还包含提供给sepolicygenerate
命令的所有路径:
/usr/lib/systemd/system/bootupd.service -- gen_context(system_u:object_r:bootupd_unit_file_t,s0)
/usr/lib/systemd/system/bootupd.socket -- gen_context(system_u:object_r:bootupd_unit_file_t,s0)
/usr/libexec/bootupd -- gen_context(system_u:object_r:bootupd_exec_t,s0)
/var/run/bootupd.sock -- gen_context(system_u:object_r:bootupd_var_run_t,s0)
同样, sepolicy generated
无法正确识别套接字文件。因此,您必须将对象类从--
(普通文件)更改为-s
(套接字文件):
/var/run/bootupd.sock -s gen_context(system_u:object_r:bootupd_var_run_t,s0)
有关对象类的完整列表,请参阅默认文件上下文定义部分。
完成此步骤后,新策略将与包匹配。编译并安装它:
$ make -f /usr/share/selinux/devel/Makefile bootupd.pp
Compiling targeted bootupd module
Creating targeted bootupd.pp policy package
rm tmp/bootupd.mod.fc tmp/bootupd.mod
$ sudo semodule -i bootupd.pp
在受新文件上下文定义影响的每个路径上使用Restorecon
命令以在系统上应用设置:
$ sudo restorecon -v /usr/libexec/bootupd /usr/lib/systemd/system/bootupd.socket /usr/lib/systemd/system/bootupd.service
Relabeled /usr/libexec/bootupd from system_u:object_r:bin_t:s0 to system_u:object_r:bootupd_exec_t:s0
Relabeled /usr/lib/systemd/system/bootupd.socket from system_u:object_r:systemd_unit_file_t:s0 to system_u:object_r:bootupd_unit_file_t:s0
Relabeled /usr/lib/systemd/system/bootupd.service from system_u:object_r:systemd_unit_file_t:s0 to system_u:object_r:bootupd_unit_file_t:s0
启用完整审核后,运行该服务:
$ sudo systemctl start bootupd.socket
$ sudo systemctl start bootupd
检查文件上下文和服务进程的域。在日志中搜索 AVC:
$ ls -lZ /var/run/bootupd.sock
srw-------. 1 root root system_u:object_r:bootupd_var_run_t:s0 0 Mar 14 10:45 /var/run/bootupd.sock
$ ps -axZ | grep bootup[d]
system_u:system_r:bootupd_t:s0 2508 ? Ss 0:00 /usr/libexec/bootupd daemon -v
$ sudo ausearch -m AVC | tee avc.log
----
time->Tue Mar 14 10:24:55 2023
type=PROCTITLE msg=audit(1678803895.889:613): proctitle=2F7573722F6C6962657865632F626F6F74757064006461656D6F6E002D76
type=SYSCALL msg=audit(1678803895.889:613): arch=c000003e syscall=41 success=yes exit=4 a0=1 a1=80002 a2=0 a3=8080808080808080 items=0 ppid=1 pid=1995 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="bootupd" exe="/usr/libexec/bootupd" subj=system_u:system_r:bootupd_t:s0 key=(null)
type=AVC msg=audit(1678803895.889:613): avc: denied { create } for pid=1995 comm="bootupd" scontext=system_u:system_r:bootupd_t:s0 tcontext=system_u:system_r:bootupd_t:s0 tclass=unix_dgram_socket permissive=1
----
time->Tue Mar 14 10:24:55 2023
type=PROCTITLE msg=audit(1678803895.889:614): proctitle=2F7573722F6C6962657865632F626F6F74757064006461656D6F6E002D76
type=SYSCALL msg=audit(1678803895.889:614): arch=c000003e syscall=44 success=yes exit=8 a0=4 a1=557718261310 a2=8 a3=4000 items=0 ppid=1 pid=1995 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="bootupd" exe="/usr/libexec/bootupd" subj=system_u:system_r:bootupd_t:s0 key=(null)
type=AVC msg=audit(1678803895.889:614): avc: denied { sendto } for pid=1995 comm="bootupd" path="/run/systemd/notify" scontext=system_u:system_r:bootupd_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclass=unix_dgram_socket permissive=1
----
time->Tue Mar 14 10:24:55 2023
type=PROCTITLE msg=audit(1674838508.590:840): proctitle=2F7573722F6C6962657865632F626F6F74757064006461656D6F6E002D76
type=PATH msg=audit(1674838508.590:840): item=0 name="/boot/efi" inode=413 dev=fc:02 mode=040755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:boot_t:s0 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(1674838508.590:840): cwd="/usr"
type=SYSCALL msg=audit(1674838508.590:840): arch=c000003e syscall=137 success=yes exit=0 a0=7fff3dc6b6a0 a1=7fff3dc6c7c0 a2=9 a3=fff items=1 ppid=1 pid=2383 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="bootupd" exe="/usr/libexec/bootupd" subj=system_u:system_r:bootupd_t:s0 key=(null)
type=AVC msg=audit(1674838508.590:840): avc: denied { getattr } for pid=2383 comm="bootupd" name="/" dev="vda2" ino=2 scontext=system_u:system_r:bootupd_t:s0 tcontext=system_u:object_r:fs_t:s0 tclass=filesystem permissive=1
即使服务在新域中运行并且套接字文件具有正确的标签,SELinux 仍然报告策略中不允许的三个访问向量。有关 AVC 消息的详细说明,请参阅 RHEL 9 使用 SELinux 文档中审核日志部分中的 SELinux 拒绝。
您可以使用audit2allow
工具获取新策略中缺少的允许规则的建议:
$ audit2allow -i avc.log
#============= bootupd_t ==============
allow bootupd_t fs_t:filesystem getattr;
allow bootupd_t kernel_t:unix_dgram_socket sendto;
allow bootupd_t self:unix_dgram_socket create;
由于您无法编译包含另一个策略模块( fs_t
和kernel_t
)中定义的类型的策略模块,因此您必须对包含必要规则的接口或宏使用带有-R
选项的audit2allow
:
$ audit2allow -R -i avc.log
require {
type bootupd_t;
class unix_dgram_socket create;
}
#============= bootupd_t ==============
allow bootupd_t self:unix_dgram_socket create;
fs_getattr_xattr_fs(bootupd_t)
kernel_dgram_send(bootupd_t)
使用Macro-expander验证建议的接口是否完全涵盖所需的用例后,将它们添加到策略模块中。您还必须将源自贡献模块的任何接口包含在optional_policy块中。首先在宽容模式下部署新策略,并在各种系统配置上测试尽可能多的使用场景。
其他资源
- 宏扩展器
- 文件策略模式
- 其他常用宏
- 参考政策宏观说明
- 定制政策包装指南
- 布尔值 - 条件策略规则
- SETools - 政策信息
- 编写自定义 SELinux 策略
- 如何明智地创建自定义 SELinux 策略模块
-
在 selinux-policy Github 存储库的obj_perm_sets.spt中找到↩︎
Comments