如何在SELinux中为新的后台程序编写策略
writing new policy for a daemon in red hat selinux guide, 给出了一般的步骤或者说一种方法学,这里打算翻译一些这一段,算是一种自我回顾总结,也希望对从事类似工作的人有所帮助。另外,SEBSD虽然是对 SELinux的ports,但是在Freebsd上进行配置与Red Hat还是有一些区别的,经过实践,我将SEBSD上有所不同的以[区别]附在旁边。
为新后台程序(daemon)编写策略这一节为从头编写新的策略文件提供一种可以遵循的总体方法。尽管这样比在local.te文件中增加几条规则要复杂的多,然而两者的概念是一致的。将应用程序置于TE规则之下,分析AVC的拒绝信息,反复增加规则直至权限问题得到解决。 新策略书写步骤
- 在Red Hat Enterprise Linux上配置好后台程序. 也就是说该程序在 /etc/init.d/ 下有启动脚本,并且可以使用 chkconfig 进行配置管理. 比如, 该配置步骤假定你将使用 service 命令控制daemon的启动和关闭. [FreeBSD中启动脚本的路径为/etc/rc.d/,并且不提供chkconfi和service命令]在本步骤中, 我们将为一个虚拟的 foo 软件包书写策略并和 foo 后台进程关联. 当开发自己的策略是请使用真实的后台进程名称.
- 创建文件 $SELINUX_SRC/domains/program/foo.te. [FreeBSD中$SELINUX_SRC相当于路径/etc/security/sebsd/policy]
- 在foo.te文件中调用宏daemon_domain为该daemon创建域:
daemon_domain(foo)
- 创建上下文配置文件, $SELINUX_SRC/file_contexts/program/foo.fc.
- 在文件 file.fc 中放置初始的配置列表. 也可以在稍后添加,这取决于 foo 程序的需要.
/usr/bin/foo -- system_u:object_r:foo_exec_t /var/run/foo.pid -- system_u:object_r:foo_var_run_t /etc/foo.conf -- system_u:object_r:foo_conf_t
- 使用 make load 命令加载新策略 [FreeBSD中要在/etc/security/sebsd/policy路径下执行make policy && make reload]
- 标记文件 foo :
restorecon /usr/bin/foo /var/run/foo.pid /etc/foo.conf
[目 前,SEBSD尚没有完成restorecon的ports,但该util的代码可以在 /usr/src/contrib/sebsd/policycoreutils 中找到,但我没有make成功,应为它需要libselinux的支持.在Freebsd下可以work around的办法是在policy路径下make default && make relabel]
- 启动daemon, service foo start.
[FreeBSD适当的编写脚本完成] - 检查审计日志中的拒绝信息:
grep "avc: denied" /var/log/messages > /tmp/avc_denials cat /tmp/avc_denials
自己要尽量熟悉该daemon所生成的错误信息. 我们将在 audit2allow 的帮助下配置策略, 但同时也需要理解拒绝信息的本质. 另外可以使用 seaudit 查看日志信息, 如同 Section 6.2 Using seaudit for Audit Log Analysis 中的解释一样。
[audit2allow工具可以在/usr/src/contrib/sebsd/policycoreutils 中找到,make install clean可以成功. seaudit没有在sebsd/FreeBSD下找到对应工具] - 使用 audit2allow 开始第一轮策略文件配置.
audit2allow -l -i /var/log/messages -o /etc/selinux/targeted/src/policy/domains/program/foo.te
检查生成的规则, 如果发现有规则赋予 foo_t 域对文件或目录的 read 权限, 将权限改为 { read gettatr }. 如果该域想要读取文件,它很可能需要这一权限(gettatr).
[audit2allow的使用可以查看其帮助或提示] - 查看 foo_t 域是否尝试创建网络套结字, 也就是说, AVC拒绝信息中的 udp_socket 或 tcp_socket 对象类:
avc: denied { create } for pid=7279 exe=/usr/bin/foo scontext=root:system_r:foo_t tcontext=root:system_r:foo_t tclass=udp_socket
如果有这种情况, 增加 can_network() 宏至 foo.te:
can_network(foo_t)
- 接着重复这些基本步骤来生成所需要的所有规则. 每一个增加到策略的规则集都可能揭示 foo_t 域所需要的额外权限.
- 启动 daemon.
- 读取 AVC 消息.
- 根据AVC消息书写策略, 使用 audit2allow 和自己的判断, 尽可能的使用宏.
- 加载新策略.
- 回到开始, 启动 daemon …
- 如果域试图访问 port_t, 这从AVC日志信息中的 tclass=tcp_socket 或 tclass=udp_socket 可以体现, 需要决定 foo 应该使用的端口号. 要诊断该信息, 在 foo.te 加入以下规则:
allow foo_t port_t:tcp_socket name_bind; auditallow foo_t port_t:tcp_socket name_bind;
该 auditallow 规则将有助于判断daemon尝试连接的端口信息.
- 对剩余的AVC拒绝信息重复以上过程. 当新策略配置消除了这些拒绝信息后, 可以再为 foo_t 域对这些特定的端口进行需要的配置.
- daemon 启动后, 确定 foo 所使用的端口号. 查看AVC允许信息并确认daemon连接到了那些端口:
lsof | grep foo.*TCP foo 2283 root 3u IPv6 3192 TCP *:4242 (LISTEN)
该 foo daemon 正在 4242 端口上进行监听.
- 删除一般的 port_t 规则, 替换为基于 foo_t 域所使用端口的特定规则.
type foo_port_t, port_type; allow foo_t foo_port_t:tcp_socket name_bind;
把这一行加入 $SELINUX_SRC/file_contexts 文件中. 这将为域 foo_t 保留端口4242
ifdef(`foo.te', `portcon tcp 4242 system_u:object_r:foo_port_t')