SELinux 策略文件编写

一. 核心概念理解

在开始编写 .te 文件之前,必须理解几个核心概念:

  • 主体 (Subject): 通常是进程。在 SELinux 中,进程是在某个域 (Domain) 中运行的。例如,httpd_t 是 Apache Web 服务器的域
  • 客体 (Object): 被进程访问的资源,例如文件、目录、端口、套接字等
  • 类型 (Type): SELinux 为所有客体(有时也包括主体)分配的一个标签(Label)。策略规则基于这些类型来定义访问控制
    ○ 文件的类型通常称为 类型 (Type)
    ○ 进程的域本质上也是一种特殊的类型
  • 策略规则 (Policy Rule): 定义了"哪个域(主体)可以对哪个类型(客体)执行哪些操作"。规则的基本结构是:
    allow <domain> <type> : <class> <permission>;
    domain : 进程的域(如 my_app_t
    type : 客体的类型(如 my_app_log_t
    class : 客体的类别(如 file, dir, tcp_socket
    permission : 允许的操作(如 read, write, create, listen
  • .te 文件 (Type Enforcement File): 这是定义策略模块的主要文件,其中包含了类型声明、域转换规则以及主要的 allow 规则

二. 编写 .te 文件的基本步骤

假设我们有一个自定义的应用程序 my_app,它需要:

  1. 在自己的目录(/opt/my_app/)中读写文件
  2. 绑定到 TCP 端口 9999

我们的目标是创建一个策略模块来允许这些行为

步骤 1:创建 .te 文件

首先,为模块创建一个文件,通常命名为 <module_name>.te。模块名应具有描述性,例如 my_app.te

步骤 2:定义策略模块和所需类型

my_app.te 文件中,我们首先声明模块并定义新的类型

bash 复制代码
# 声明策略模块名称
policy_module(my_app, 1.0)

# 定义进程域类型
# 你的应用程序进程将运行在这个域中
type my_app_t;
# 初始域:当系统启动时,由 init 启动的进程的初始上下文
init_daemon_domain(my_app_t)

# 定义用于 /opt/my_app 目录及其文件的类型
type my_app_etc_t;
# 定义用于日志文件的类型
type my_app_log_t;

说明:

  • policy_module(my_app, 1.0):声明模块名为 my_app,版本为 1.0
  • type my_app_t;:定义一个新类型 my_app_t,用于我们的应用程序进程域
  • init_daemon_domain(my_app_t):这是一个宏,它做了很多事情:
    ○ 它告诉 SELinux,由 init 系统启动的、类型为 my_app_exec_t 的可执行文件,应该转换到 my_app_t 域运行
    ○ 它自动为 my_app_t 域设置了允许与终端、消息队列等基本资源交互的规则
  • type my_app_etc_t;type my_app_log_t;:定义用于配置文件和日志文件的新类型
步骤 3:定义文件上下文(File Contexts)

我们需要告诉 SELinux 哪些路径应该用我们新定义的类型来标记。这通常在另一个文件 my_app.fc 中完成,但为了教程连贯性,我们先在 .te 文件中声明依赖

.te 文件需要知道这些类型将应用于文件系统对象。我们稍后会创建 .fc 文件

步骤 4:编写访问规则 (Allow Rules)

这是策略的核心,我们明确授予必要的权限

bash 复制代码
# 1. 允许 my_app_t 域对 my_app_etc_t 类型的文件和目录进行基本读写
files_search_etc(my_app_t) # 宏:允许搜索 /etc 目录
allow my_app_t my_app_etc_t : dir { read search open getattr };
allow my_app_t my_app_etc_t : file { read open getattr };

# 2. 允许 my_app_t 域对 my_app_log_t 类型的文件进行读写和创建
logging_log_file(my_app_t) # 宏:允许使用日志相关的权限
allow my_app_t my_app_log_t : file { create read write open append getattr setattr };
allow my_app_t my_app_log_t : dir { create read write open add_name remove_name getattr setattr };

# 3. 允许 my_app_t 绑定到 TCP 端口 9999
# 首先,需要定义我们自己的端口类型
type my_app_port_t;
# 然后,声明该类型用于 tcp 套接字,并且端口号是 9999
# (注意:这行通常也在另一个 .if 文件中,但可以写在这里)
corenet_port(my_app_port_t, tcp, 9999)
# 最后,允许我们的域绑定到该类型的端口
allow my_app_t my_app_port_t : tcp_socket { name_bind };

说明:

  • files_search_etc, logging_log_file:这些是 ,它们展开后是一组常用的、复杂的 allow 规则。强烈建议优先使用宏而不是手动编写 allow 规则 ,因为它们更安全、更简洁。你可以通过 sepolicy generate 命令或查阅现有策略来找到可用的宏
  • allow ... : dir { read search ... };:详细列出了对目录 (dir 类) 允许的操作
  • corenet_port(my_app_port_t, tcp, 9999):这是一个宏,它:
    ○ 定义规则允许 my_app_port_t 类型与 TCP 端口 9999 关联
    ○ 为其他域(如内核 netif_tnode_t)访问该端口提供必要的规则
步骤 5:处理其他依赖

你的应用可能需要其他权限,例如访问 /proc 文件系统、与 Unix 套接字通信等。一个很好的起点是使用 audit2allow 工具

  1. 将 SELinux 设置为 permissive 模式:setenforce Permissive

  2. 运行你的应用程序,执行所有操作

  3. 查看 /var/log/audit/audit.log 或使用 ausearch 来收集 AVC(Access Vector Cache)拒绝消息

  4. 使用 audit2allow -aaudit2allow -w -a 来生成建议的规则
    -w 选项会告诉你哪些规则是因为哪个拒绝消息而需要的
    不要盲目添加所有 audit2allow 建议的规则! 只添加与你应用程序功能相关的、你理解的那些规则。audit2allow 可能会生成过于宽松的规则

三. 完整的示例文件

my_app.te

bash 复制代码
policy_module(my_app, 1.0)

###########################################
# Type declarations
###########################################
type my_app_t;
type my_app_etc_t;
type my_app_log_t;
type my_app_port_t;

###########################################
# Role and user domain transition
# init_daemon_domain 宏已经处理了大部分内容
###########################################
init_daemon_domain(my_app_t)

###########################################
# File context rules (referenced from .fc)
# 这里只是声明类型,实际路径映射在 .fc 文件
###########################################

###########################################
# Access Vector Rules
###########################################

# Allow basic directory operations for etc files
files_search_etc(my_app_t)
allow my_app_t my_app_etc_t : dir { read search open getattr };
allow my_app_t my_app_etc_t : file { read open getattr };

# Allow read/write/create for log files
logging_log_file(my_app_t)
allow my_app_t my_app_log_t : file { create read write open append getattr setattr };
allow my_app_t my_app_log_t : dir { create read write open add_name remove_name getattr setattr };

# Allow binding to TCP port 9999
corenet_port(my_app_port_t, tcp, 9999)
allow my_app_t my_app_port_t : tcp_socket { name_bind };

# Additional rules based on audit2allow analysis
# Example: if your app needs to read /proc files
# allow my_app_t self : proc { read };

my_app.fc

bash 复制代码
# 这个文件将类型映射到文件系统路径
# Format: <path_regex>    <<type>

# Configuration files in /opt/my_app/etc
/opt/my_app/etc(/.*)?    gen_context(system_u:object_r:my_app_etc_t, s0)

# Log files in /var/log/my_app
/var/log/my_app(/.*)?    gen_context(system_u:object_r:my_app_log_t, s0)

# The application binary itself
/opt/my_app/bin/my_app    gen_context(system_u:object_r:my_app_exec_t, s0)

注意: .fc 文件中的 my_app_exec_t 类型是由 init_daemon_domain(my_app_t) 宏自动创建和使用的,它用于可执行文件,以实现域转换

四. 编译、安装和启用策略模块

  1. 所需软件包: 确保已安装 selinux-policy-develpolicycoreutils 等包
    ○ (RHEL/CentOS/Fedora): sudo dnf install selinux-policy-devel policycoreutils

  2. 编译和安装:

    bash 复制代码
    # 1. 将 .te 和 .fc 文件放在一个目录下
    # 2. 运行 make -f /usr/share/selinux/devel/Makefile
    # 这会将 .te 文件编译成 .pp 策略模块包文件
    sudo make -f /usr/share/selinux/devel/Makefile my_app.pp
    
    # 3. 安装编译好的模块
    sudo semodule -i my_app.pp
  3. 应用文件上下文:

    bash 复制代码
    # 在创建文件之前,先应用文件上下文规则
    sudo restorecon -Rv /opt/my_app/ /var/log/my_app/
    # 如果文件已经存在,同样使用此命令重新标记它们
  4. 重启应用程序: 重启你的应用程序,以便它在新策略下以正确的域 (my_app_t) 启动

  5. 验证:
    ○ 检查进程域:ps -efZ | grep my_app
    ○ 检查文件上下文:ls -lZ /opt/my_app/ /var/log/my_app/

五. 调试技巧

  • permissive 模式是你的朋友: 始终先在 permissive 模式下开发和测试你的策略。在此模式下,SELinux 会记录拒绝访问的日志但不会真正阻止操作。这让你可以收集所有必要的 AVC 消息,而不会使应用程序崩溃
  • 使用 audit2why: audit2why -a 可以将 AVC 消息翻译成更易读的英文,解释为什么访问被拒绝
  • sepolicy generate: 这是一个非常强大的工具。你可以尝试:
    sepolicy generate --init /path/to/my_app/binary
    它会尝试为你生成一个初步的 .te.fc 文件,这是一个极好的起点。你可以在其基础上进行修改和细化

总结

步骤 关键动作 常用命令/宏
1. 分析 在 Permissive 模式下运行应用,收集 AVC 拒绝消息 setenforce 0, ausearch -m avc, audit2allow -a
2. 编写 创建 .te.fc 文件,定义类型,使用宏和规则 type, init_daemon_domain(), allow, files_search_etc(), corenet_port()
3. 编译 .te 文件编译成 .pp 模块 make -f /usr/share/selinux/devel/Makefile
4. 安装 将模块加载到内核 semodule -i
5. 标记 将安全上下文应用到文件系统 restorecon -Rv
6. 测试 在 Enforcing 模式下重启应用并验证 setenforce 1, ps -efZ, ls -lZ
相关推荐
.豆鲨包2 小时前
【Android】Viewpager2实现无限轮播图
android·java
xqlily2 小时前
Linux操作系统之Ubuntu
linux·运维·ubuntu
xiangxiongfly9152 小时前
Android CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout实现折叠置顶效果
android·折叠
阿部多瑞 ABU2 小时前
《基于国产Linux的机房终端安全重构方案》
linux·安全
柿蒂2 小时前
从if-else和switch,聊聊“八股“的作用
android·java·kotlin
倔强的石头1062 小时前
【Linux指南】Makefile入门:从概念到基础语法
linux·运维·服务器
ajassi20002 小时前
linux C 语言开发 (七) 文件 IO 和标准 IO
linux·运维·服务器
程序猿编码2 小时前
基于 Linux 内核模块的字符设备 FIFO 驱动设计与实现解析(C/C++代码实现)
linux·c语言·c++·内核模块·fifo·字符设备
一只游鱼3 小时前
Zookeeper介绍与部署(Linux)
linux·运维·服务器·zookeeper