第3章 应用域隔离设计原则

SELinux 应用域隔离的核心在于通过 最小权限、类型划分 和 域边界定义 三个原则,将系统划分为相互隔离的运行时环境。每个应用进程运行在独立的域中,仅能访问为其显式授权的资源,从而限制攻击面并防止水平提权。本章从原理、实现流程、配置指令、调试方法到常见错误,系统讲解这三条原则如何在策略编写中落地。

知识前置:域(Domain)在 SELinux 中即进程的安全上下文(security context)首要字段“类型”(type),例如 httpd_t、mysqld_t。域隔离通过类型强制访问控制(TE)实现,每个域对应一组访问向量规则。
应用域隔离设计原则示意图 应用域A 类型:myapp_a_t 进程 a 文件: /var/a_data 端口: 8080 应用域B 类型:myapp_b_t 进程 b 文件: /var/b_data 应用域C 类型:myapp_c_t 进程 c 端口: 9090 ✘ ✘ 最小权限:仅开放进程所需资源 | 类型划分:每域独立类型 | 域边界:跨域访问默认拒绝

3.1 最小权限原则

原理:每个域只能获得完成其任务所必需的最小权限集,任何非必要文件、端口、IPC 对象均被拒绝。在 SELinux 策略中,这表现为精确的目标规则(targeted allow rules),而非通配符。

实现流程:

  1. 列举应用所有访问行为(读文件、写日志、连接端口、执行程序等)。
  2. 为每种行为编写 allow 规则,避免使用 self 或 * 代替具体客体类型。
  3. 使用 neverallow 断言确保未授权的访问不会被意外打开。

配置示例(策略片段 .te 文件):

type myapp_t;
type myapp_data_t;
files_type(myapp_data_t)

# 仅允许读取 /var/myapp 下的文件
allow myapp_t myapp_data_t:file { read getattr open };
# 禁止写入(除非显式添加)
neverallow myapp_t myapp_data_t:file write;

# 仅允许监听 9999 TCP 端口
allow myapp_t self:tcp_socket { create bind listen accept };
corenet_tcp_bind_all_nodes(myapp_t)
corenet_tcp_bind_generic_port(myapp_t)
gen_tunable(allow_myapp_extra_port, false)
调试命令: 使用 sesearch -A -s myapp_t 列出所有允许规则,检查是否出现意外的大范围权限。使用 seinfo -tmyapp_t -x 查看类型属性。

3.2 类型划分原则

原理:每个应用域拥有唯一的安全上下文类型(如 myapp_t),其管理的文件、进程、套接字也使用独立的类型标签(如 myapp_data_t、myapp_log_t)。类型划分是域隔离的基础,避免类型复用导致的权限泄露。

常用类型命名规范与划分策略:

资源类别类型命名示例说明
进程域myapp_t应用主进程类型
数据文件myapp_data_t应用私有数据,仅本域可读写
日志文件myapp_log_t日志输出,通常只写不读
临时文件myapp_tmp_t/tmp 下临时文件,使用 tmpfile 域
网络套接字myapp_port_t绑定的 TCP/UDP 端口

类型声明示例(.te 文件):

type myapp_t;
type myapp_data_t;
type myapp_log_t;
type myapp_tmp_t;
type myapp_port_t;

# 标记文件目录
fs_manage_files_pattern(myapp_t, myapp_data_t, myapp_data_t)
fs_manage_dirs_pattern(myapp_t, myapp_data_t, myapp_data_t)

3.3 域边界定义原则

原理:域边界通过 域转换(domain transition) 和 显式允许规则 严格定义。一个进程只能通过执行转换规则(type_transition 或 domain_auto_trans)进入其他域,且必须同时具备源域和目标域的 entrypoint 与 fdtrans 权限。跨域访问(如域A读取域B的文件)默认被拒绝,除非策略显式写入 allow 规则。

域边界定义的关键策略元素:

  • 域转换入口: domain_entry_file(myapp_t, myapp_exec_t) 声明可执行文件的类型作为入口。
  • 自动转换规则: domain_auto_trans(init_t, myapp_exec_t, myapp_t) 使 systemd 启动时自动切换域。
  • 类型边界(typebounds): 用于隔离子域,防止父域权限丢失。

实现流程:

  1. 定义入口文件类型 myapp_exec_t,并关联到可执行文件。
  2. 编写 domain_auto_trans 或显式 type_transition 规则。
  3. 使用 allow myapp_t myapp_exec_t:file { read getattr execute } 赋予执行权限。
  4. 使用 neverallow 禁止非预期的域转换(如 neverallow ~myapp_t myapp_t:process transition)。
type myapp_exec_t;
files_type(myapp_exec_t)

# 域自动转换:从 init_t 执行 /usr/bin/myapp 时自动进入 myapp_t
domain_auto_trans(init_t, myapp_exec_t, myapp_t)

# 确保只有本域才能进入
allow myapp_t myapp_exec_t:file entrypoint;
allow init_t myapp_exec_t:file { read getattr execute };

3.4 调试与验证方法

  • 查看域当前上下文: ps -eZ | grep myapp 确认进程运行在 myapp_t 域。
  • 检查类型边界: sesearch -d -s myapp_t -c process -p transition 列出域转换规则。
  • 模拟访问检查: sechecker -a /etc/selinux/targeted/policy/policy.* 2>&1 | grep myapp 扫描冲突。
  • AVC 审计日志: ausearch -ts recent -m avc -tsc all | grep myapp 查看拒绝事件。

3.5 常见错误与优化建议

错误场景原因解决方案
进程启动后仍为 init_t缺少域转换规则或入口文件类型错误确认 domain_auto_trans 与 entrypoint 权限
AVC 拒绝访问数据文件未授权 read 或 write添加 allow myapp_t myapp_data_t:file { read open }
跨域读文件被拒绝域边界原则禁止,需显式授权评估是否为合法需求,若必须则编写有限的跨域规则
性能问题:大量域转换频繁执行类型转换导致上下文切换合并子域或使用 typebounds 限制转换频次
稳定性要点:域边界定义不宜过细,避免产生数百个小型域导致策略加载缓慢。推荐每个独立应用至少一个域,共享库型进程可归入同一域。使用 typebounds 控制继承关系,确保父域权限不被子域破环。