SELinux

目录

  1. 概述:什么是 SELinux
  2. 历史背景与发展
  3. SELinux 安全模型详解
  4. 核心架构与工作原理
  5. 安全上下文(Security Context)
  6. SELinux 策略语言详解
  7. SELinux 的三种模式
  8. 实战命令大全
  9. SELinux 排错实战
  10. 编写自定义 SELinux 策略
  11. SELinux 与容器安全
  12. SELinux vs AppArmor:对比与选型
  13. 安全加固最佳实践
  14. 常见问题 FAQ
  15. 总结

1. 概述:什么是 SELinux

SELinux(Security-Enhanced Linux,安全增强型 Linux)是由美国国家安全局(NSA)主导开发的 强制访问控制(MAC,Mandatory Access Control) 安全系统。它内置于 Linux 内核中,为系统提供细粒度的安全策略强制执行。

适用系统:RHEL / CentOS / Fedora / Rocky Linux / AlmaLinux(内核 2.6+)

核心特性

特性 说明
强制访问控制(MAC) 超越传统 Unix 权限(DAC),即使 root 进程也受 SELinux 策略约束
最小权限原则 每个进程只能访问完成任务所必需的资源
类型强制(TE) 核心访问控制机制,基于主体和客体的安全类型
RBAC 支持 基于角色的访问控制,控制用户能执行哪些类型的进程
MLS/MCS 支持 多层安全(军事级)和多范畴安全(容器隔离)
内核级执行 策略在内核中强制执行,用户空间无法绕过
细粒度控制 可控制到每个系统调用、每个文件、每个网络端口

SELinux 在整个 Linux 安全体系中的位置

用户进程发起操作
        │
        ▼
┌───────────────────────┐
│  1. DAC 检查          │  ← 传统 Unix 权限 (rwx)
│     (自主访问控制)     │
└───────────┬───────────┘
            │ 通过
            ▼
┌───────────────────────┐
│  2. SELinux MAC 检查  │  ← 强制访问控制
│     (LSM Hook)        │     TE + RBAC + MLS
└───────────┬───────────┘
            │ 通过
            ▼
┌───────────────────────┐
│  3. Capabilities 检查 │  ← 特权操作控制
└───────────┬───────────┘
            │ 通过
            ▼
        执行操作

关键原则:如果 DAC(传统权限)拒绝了操作,SELinux 不会介入。SELinux 是在 DAC 之后附加的额外安全检查层。


2. 历史背景与发展

时间 里程碑
1992-1999 NSA 和 SCC(Secure Computing Corporation)启动 Flask 安全架构研究
2000 NSA 发布 SELinux 的第一个公开版本(基于 Linux 2.2 内核补丁)
2001 Linux 2.4 时代的 SELinux,由 NSA、NAI Labs、MITRE 等合作开发
2003 Linux 2.6.0:SELinux 通过 LSM 框架合入主线内核
2005 Red Hat 开始在 RHEL 4 中默认提供 SELinux(Targeted Policy)
2008 SELinux Reference Policy(refpolicy)项目启动,统一策略开发
2014 Android 5.0 开始强制启用 SELinux(Enforcing 模式)
2020+ CIL(Common Intermediate Language)策略语言成熟,替代旧式策略语法
2024+ RHEL 9/10 中 SELinux 持续增强,容器策略精细化

SELinux 的设计目标

NSA 原始设计目标(Flask 架构):
  ├─ 最小权限:每个主体仅被授予完成任务所需的最小权限
  ├─ 强制访问控制:策略强制执行,不可绕过
  ├─ 策略灵活性:策略与执行逻辑分离,可独立更新
  ├─ 透明性:对合规应用透明,不需修改源代码
  └─ 防篡改:即使 root 被攻破,也无法绕过 SELinux

3. SELinux 安全模型详解

SELinux 实现了多层安全模型的组合,在 Red Hat 系统中提供 TE、RBAC 和 MLS 三种模型。

3.1 类型强制(Type Enforcement, TE)

TE 是 SELinux 最核心、最常用的访问控制机制。

基本原理

  • 每个主体(进程)被分配一个域类型(Domain Type)
  • 每个客体(文件、目录、端口、套接字等)被分配一个类型(Type)
  • 策略中的 allow 规则定义哪个域可以访问哪个类型、进行什么操作
┌──────────────────────────────┐
│  进程:/usr/sbin/httpd       │
│  Domain: httpd_t             │
│                              │
│  allow httpd_t httpd_sys_content_t:file { read getattr open };
│                              │
│          │ 允许读取          │
│          ▼                  │
│  文件:/var/www/index.html  │
│  Type: httpd_sys_content_t   │
└──────────────────────────────┘

┌──────────────────────────────┐
│  进程:/usr/sbin/httpd       │
│  Domain: httpd_t             │
│                              │
│  (无 allow 规则)             │
│                              │
│          │ 拒绝访问          │
│          ▼                  │
│  文件:/etc/shadow           │
│  Type: shadow_t              │
└──────────────────────────────┘

TE 的核心概念

概念 说明 示例
Domain 进程的类型 httpd_tsshd_tinit_t
Type 文件/资源的类型 httpd_sys_content_tetc_tuser_home_t
Attribute 类型的集合(标签) domainfile_typeexec_type
Class 客体类别 filedirtcp_socketprocess
Permission 操作权限 readwriteexecuteopen

3.2 基于角色的访问控制(RBAC)

RBAC 在 TE 之上增加了一层用户-角色-域的控制关系:

SELinux 用户 ──► 角色 ──► 域(Domain)
    │              │           │
  user_u        user_r      user_t
  staff_u       staff_r     staff_t
  sysadm_u      sysadm_r    sysadm_t
  unconfined_u  unconfined_r unconfined_t

规则:

  • 每个 SELinux 用户被授权扮演特定角色
  • 每个角色被授权进入特定的域
  • 用户只能通过被授权的角色,进入被授权的域
# 查看 SELinux 用户映射
semanage user -l

# 查看角色与域的关联
semanage user -l | grep sysadm_u
# sysadm_u   sysadm_r   sysadm_t   s0-s0:c0.c1023

3.3 多层安全(Multi-Level Security, MLS)

MLS 是面向政府和军事环境的安全模型,基于 Bell-LaPadula(BLP)模型

BLP 原则 说明
不向上读(No Read Up) 低级别主体不能读取高级别数据
不向下写(No Write Down) 高级别主体不能向低级别写入(防止泄密)
安全级别定义:
  s0(非机密)→ s1(秘密)→ s2(机密)→ s3(绝密)

进程级别: s1(秘密)
  ├─ 可以读:s0(非机密)s1(秘密)    ← 同级及以下
  ├─ 不能读:s2(机密)s3(绝密)       ← 高于自身
  ├─ 可以写:s1(秘密)s2(机密)s3(绝顶) ← 同级及以上
  └─ 不能写:s0(非机密)               ← 低于自身(防止泄密)

注意:MLS 在标准 RHEL 中不是默认启用的。默认策略是 Targeted Policy,使用 MCS 代替 MLS。


3.4 多范畴安全(Multi-Category Security, MCS)

MCS 是 MLS 的简化版本,在 Targeted Policy 中默认启用。它使用 范畴(Category) 而非敏感度级别:

MCS 标签格式:s0:c0,c2  (敏感度 s0,范畴 c0 和 c2)

用途:
  ├─ 虚拟机隔离:每个 VM 分配不同范畴
  ├─ 容器隔离:每个容器分配不同范畴
  └─ 用户数据隔离:不同用户的文件使用不同范畴
# 查看进程的 MCS 标签
ps -eZ | head -5
# system_u:system_r:init_t:s0           ← 无范畴
# system_u:system_r:virtd_t:s0:c1,c2    ← 有范畴隔离

4. 核心架构与工作原理

4.1 LSM 框架集成

SELinux 通过 Linux Security Module(LSM) 框架在内核关键路径上放置钩子函数:

用户进程 → 系统调用
              │
              ▼
        ┌─────────────┐
        │  DAC 检查   │  ← 传统权限 (rwx)
        └──────┬──────┘
               │ 通过
               ▼
        ┌─────────────────────────┐
        │  LSM Hook (security_*)  │
        │         │               │
        │    ┌────▼────┐          │
        │    │ SELinux │          │
        │    │  模块   │          │
        │    └────┬────┘          │
        │         │               │
        │  ┌──────▼──────┐        │
        │  │ 策略引擎     │        │
        │  │ (Policy      │        │
        │  │  Engine)     │        │
        │  └──────┬──────┘        │
        │         │               │
        │  ┌──────▼──────┐        │
        │  │   AVC       │        │
        │  │ (访问向量    │        │
        │  │  缓存)       │        │
        │  └──────┬──────┘        │
        └─────────┼───────────────┘
                  │
          ┌───────┴───────┐
          │  ALLOW / DENY │
          └───────────────┘

4.2 访问决策流程与 AVC

AVC(Access Vector Cache) 是 SELinux 的性能优化关键组件:

访问请求
    │
    ▼
┌─────────┐
│ AVC 查询 │── 命中 ──► 返回缓存结果
└────┬────┘
     │ 未命中
     ▼
┌─────────────┐
│ 安全服务器    │
│ (策略引擎)    │── 遍历策略规则
│              │── 计算访问决策
└──────┬──────┘
       │
       ▼
  决策 + 写入 AVC 缓存 + 审计日志

查看 AVC 统计:

cat /sys/fs/selinux/avc/cache_stats
# 输出示例:
# lookups hits misses allocations reclaims frees
# 452341 451892 449 449 0 0
# 命中率:451892/452341 = 99.9%

4.3 SELinux 与 DAC 的关系

DAC 先于 SELinux 执行:

  请求访问 /etc/shadow
       │
       ▼
  DAC: 检查 UID/GID/权限位
       │
       ├─ DAC 拒绝 → 返回 EACCES(SELinux 不参与)
       │
       └─ DAC 允许 → 继续
              │
              ▼
         SELinux: 检查 TE 规则
              │
              ├─ 策略拒绝 → 返回 EACCES + AVC 拒绝日志
              │
              └─ 策略允许 → 操作执行

重要:即使你是 root,如果 SELinux 策略拒绝,操作仍然会失败。这是 SELinux 最强大的特性之一。


5. 安全上下文(Security Context)

5.1 格式与组成

所有 SELinux 控制的对象(进程、文件、目录、端口、套接字等)都有一个安全上下文(Security Context)

SELinux_User : Role : Type : Level

示例:
system_u:object_r:httpd_sys_content_t:s0
  │         │         │               │
  │         │         │               └─ MLS/MCS 级别
  │         │         └─ 类型(TE 核心)
  │         └─ 角色
  └─ SELinux 用户
字段 后缀 含义 示例
SELinux User _u SELinux 用户标识 system_u, unconfined_u, user_u
Role _r 角色(客体通常为 object_r object_r, system_r, unconfined_r
Type _t 最核心:域或类型 httpd_t, etc_t, shadow_t
Level s0 敏感度 + 范畴 s0, s0:c0,c1

5.2 查看与修改安全上下文

# ========== 查看安全上下文 ==========

# 查看文件的安全上下文
ls -Z /etc/passwd
# -rw-r--r--. root root system_u:object_r:passwd_file_t:s0 /etc/passwd

# 查看进程的安全上下文
ps -eZ | grep nginx
# system_u:system_r:httpd_t:s0    12345 ?  00:00:00 nginx

# 查看当前用户的安全上下文
id -Z
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

# 查看端口的安全上下文
semanage port -l | grep 80
# http_port_t  tcp  80, 81, 443, ...

# ========== 修改安全上下文 ==========

# 临时修改(文件系统 relabel 后会恢复)
chcon -t httpd_sys_content_t /var/www/custom/index.html
chcon -R -t httpd_sys_content_t /var/www/custom/

# 永久修改(写入策略,不受 relabel 影响)
semanage fcontext -a -t httpd_sys_content_t "/var/www/custom(/.*)?"
restorecon -Rv /var/www/custom/

5.3 文件上下文管理

# 查看文件上下文规则
semanage fcontext -l

# 查看特定路径的默认上下文
semanage fcontext -l | grep /var/www

# 添加自定义文件上下文规则
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"

# 修改已有规则
semanage fcontext -m -t httpd_sys_rw_content_t "/web(/.*)?"

# 删除规则
semanage fcontext -d -t httpd_sys_content_t "/web(/.*)?"

# 应用文件上下文(根据策略重置标签)
restorecon -Rv /web/

# 全系统 relabel(谨慎操作!)
touch /.autorelabel && reboot

6. SELinux 策略语言详解

SELinux 策略定义了系统上所有的访问控制规则。主流策略语言有两种:

  • 内核策略语言(Kernel Policy Language):传统语法,.te/.fc/.if 文件
  • CIL(Common Intermediate Language):现代语法,.cil 文件,更简洁

6.1 类型与属性定义

# 定义类型
type httpd_t;
type httpd_sys_content_t;
type httpd_exec_t;

# 定义属性(Attribute)—— 类型的集合
attribute domain;        # 进程域
attribute file_type;     # 文件类型

# 为类型添加属性
typeattribute httpd_t domain;
typeattribute httpd_sys_content_t file_type;

# 定义类型时同时添加属性
type httpd_t, domain;

6.2 客体类别与权限

# 定义客体类别(Class)和权限
class file {
    ioctl read write create getattr setattr
    lock relabelfrom relabelto append map
    unlink link rename execute swapon quotaon
    mounton audit_access open execmod watch watch_mount
    watch_sb watch_with_perm watch_reads
}

class process {
    fork transition sigchld sigkill sigstop
    signull signal ptrace getsched setsched
    getsession getpgid setpgid getcap setcap
    share getattr setexec setfscreate noatsecure
    siginh setrlimit rlimitinh dyntransition
    setcurrent execmem execstack execheap setkeycreate
    setsockcreate getrlimit
}

class tcp_socket {
    bind connect listen accept getopt setopt
    shutdown recvfrom sendto name_bind
    node_bind name_connect
}

6.3 访问向量规则(AV Rules)

这是 SELinux 策略中最核心的规则类型:

# ========== allow:授予权限 ==========
allow httpd_t httpd_sys_content_t : file { read getattr open };
# 允许 httpd_t 域读取 httpd_sys_content_t 类型的文件

allow httpd_t httpd_log_t : file { read write append open };
# 允许 httpd_t 域读写 httpd_log_t 类型的日志文件

allow httpd_t self : process { fork signal };
# 允许 httpd_t 进程 fork 和发信号给自己

# ========== dontaudit:不记录拒绝 ==========
dontaudit httpd_t default_t : dir { getattr search };
# 访问被拒绝时,不写入审计日志(减少日志噪音)

# ========== auditallow:强制记录 ==========
auditallow staff_t shadow_t : file read;
# 即使访问被允许,也强制记录审计日志(监控敏感访问)

# ========== neverallow:编译时检查 ==========
neverallow domain shadow_t : file { read write };
# 如果任何 allow 规则违反此约束,策略编译报错
# 这是确保安全底线的"硬规则"

allow 规则完整格式:

allow <源域> <目标类型> : <客体类别> { <权限集合> };
allow <source_domain> <target_type> : <object_class> { <permissions> };

6.4 类型转换规则(Type Transition)

当进程执行新程序或创建新文件时,需要改变类型:

# ========== 进程域转换 ==========

# 定义转换规则:a_t 执行 b_exec_t 后变成 b_t
type_transition a_t b_exec_t : process b_t;

# 需要的三条 allow 规则:
# 1. 允许从 a_t 转换到 b_t
allow a_t b_t : process transition;

# 2. 允许 a_t 执行 b_exec_t 文件
allow a_t b_exec_t : file { execute read getattr open };

# 3. b_exec_t 必须是 b_t 域的入口点
allow b_t b_exec_t : file entrypoint;

# ========== 文件创建时的类型转换 ==========

# 在 /var/log 目录下创建文件时,自动设为 httpd_log_t
type_transition httpd_t var_log_t : file httpd_log_t;
allow httpd_t var_log_t : dir { write add_name };
allow httpd_t httpd_log_t : file { create write open };

域转换流程图:

init_t 执行 /usr/sbin/httpd (httpd_exec_t)
    │
    │ type_transition init_t httpd_exec_t:process httpd_t;
    │
    ▼
httpd_t(域转换完成)
    │
    │ 读取 /var/www/html/index.html (httpd_sys_content_t)
    │ allow httpd_t httpd_sys_content_t:file { read getattr open };
    │
    ▼
返回文件内容给客户端

6.5 文件系统标记

# ========== fs_use_xattr ==========
# 用于支持扩展属性的文件系统(ext4/xfs/btrfs)
# 文件标签从自身的 security.selinux 扩展属性读取
fs_use_xattr ext4 system_u:object_r:fs_t:s0;
fs_use_xattr xfs system_u:object_r:fs_t:s0;

# ========== fs_use_task ==========
# 用于伪文件系统(pipefs/sockfs)
# 文件标签由创建进程决定
fs_use_task pipefs system_u:object_r:fs_t:s0;

# ========== fs_use_trans ==========
# 基于类型转换规则动态决定标签
fs_use_trans devpts system_u:object_r:devpts_t:s0;

# ========== genfscon ==========
# 为不支持 xattr 的文件系统指定路径的标签
genfscon proc /kmsg system_u:object_r:proc_kmsg_t:s0;
genfscon proc /sys/kernel/shm system_u:object_r:proc_rw_t:s0;
genfscon sysfs /devices/system/cpu system_u:object_r:sysfs_devices_system_cpu_t:s0;

6.6 CIL 策略语言简介

CIL(Common Intermediate Language)是现代 SELinux 策略开发推荐使用的语言:

;; CIL 格式的策略文件示例

;; 定义类型
(type httpd_t)
(type httpd_exec_t)
(type httpd_sys_content_t)

;; 添加属性
(typeattributeset domain (httpd_t))
(typeattributeset file_type (httpd_exec_t httpd_sys_content_t))

;; 定义角色
(role system_r)
(roletype system_r httpd_t)

;; 定义用户
(user system_u)
(userrole system_u system_r)

;; 定义客体类别
(class file (read write execute getattr open))

;; allow 规则
(allow httpd_t httpd_sys_content_t (file (read getattr open)))

;; 类型转换
(typetransition init_t httpd_exec_t process httpd_t)

;; 文件上下文
(filecon "/usr/sbin/httpd" file httpd_exec_t)
(filecon "/var/www/html(/.*)?" file httpd_sys_content_t)

7. SELinux 的三种模式

模式 行为 日志 适用场景
Enforcing 强制执行策略,违规操作被阻止 记录拒绝日志 生产环境
Permissive 不阻止违规操作,仅记录 记录所有违规日志 调试、策略开发
Disabled 完全关闭 SELinux 不记录 不推荐
# 查看当前模式
getenforce

# 查看配置文件中的模式
cat /etc/selinux/config
# SELINUX=enforcing

# 临时切换模式
setenforce 0   # 切换到 Permissive
setenforce 1   # 切换到 Enforcing

# 永久修改模式
vim /etc/selinux/config
# SELINUX=enforcing|permissive|disabled

单个域的 Permissive 模式(推荐)

不必将整个系统设为 Permissive,可以只对特定域放开:

# 将 httpd_t 域设为 permissive(不阻止,但记录)
semanage permissive -a httpd_t

# 查看所有 permissive 域
semanage permissive -l

# 移除 permissive
semanage permissive -d httpd_t

8. 实战命令大全

8.1 状态与模式管理

# 查看 SELinux 状态
sestatus
getenforce

# 切换模式
setenforce 0  # Permissive
setenforce 1  # Enforcing

# 查看 SELinux 策略版本
sestatus | grep "Policy from config file"

# 查看已加载的策略模块
semodule -l

8.2 安全上下文管理

# 查看安全上下文
ls -Z /var/www/html/
ps -eZ | grep httpd
id -Z
netstat -Z | grep :80

# 临时修改上下文(重启/relabel 后会恢复)
chcon -t httpd_sys_content_t /var/www/custom/index.html
chcon -R -t httpd_sys_content_t /var/www/custom/
chcon -R --reference=/var/www/html /var/www/custom

# 永久修改上下文
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/web/uploads(/.*)?"

# 应用文件上下文
restorecon -Rv /web/
restorecon -Rv /var/www/

8.3 布尔值(Boolean)管理

布尔值允许在不修改策略的情况下动态调整 SELinux 行为:

# 查看所有布尔值
getsebool -a

# 查看特定布尔值
getsebool httpd_enable_cgi
getsebool httpd_can_network_connect

# 设置布尔值(临时)
setsebool httpd_enable_cgi on

# 设置布尔值(永久)
setsebool -P httpd_can_network_connect on

# 查看布尔值说明
semanage boolean -l | grep httpd

常用布尔值速查:

布尔值 用途
httpd_can_network_connect 允许 Apache 连接外部网络
httpd_enable_cgi 允许 Apache 运行 CGI 脚本
httpd_read_user_content 允许 Apache 读取用户家目录内容
httpd_can_sendmail 允许 Apache 发送邮件
named_write_master_zones 允许 BIND 写入主区域文件
nfs_export_all_ro NFS 只读导出
ssh_sysadm_login 允许 sysadm 角色 SSH 登录

8.4 端口上下文管理

# 查看端口上下文
semanage port -l | grep http
# http_port_t  tcp  80, 81, 443, 488, 8008, 8009, 8443, 9000

# 添加自定义端口
semanage port -a -t http_port_t -p tcp 8080

# 修改端口类型
semanage port -m -t http_port_t -p tcp 8443

# 删除端口规则
semanage port -d -t http_port_t -p tcp 8080

# 查看特定端口
semanage port -l | grep 3306
# mysqld_port_t  tcp  3306

8.5 策略模块管理

# 列出已安装模块
semodule -l

# 安装模块
semodule -i myapp.pp

# 卸载模块
semodule -r myapp

# 升级模块
semodule -u myapp.pp

# 查看模块详情
semodule -l | grep myapp

# 禁用模块(不卸载)
semodule -d myapp

# 启用模块
semodule -e myapp

9. SELinux 排错实战

9.1 排错工作流

发现问题(应用报错 Permission Denied)
           │
           ▼
┌──────────────────────┐
│ 1. 确认是 SELinux 问题 │
│    getenforce          │
│    ausearch -m avc     │
└──────────┬───────────┘
           │ 确认是 SELinux
           ▼
┌──────────────────────┐
│ 2. 收集拒绝日志        │
│    sealert -a /var/log/│
│    audit/audit.log     │
└──────────┬───────────┘
           │
           ▼
┌──────────────────────┐
│ 3. 分析问题原因        │
│    - 文件上下文错误?   │
│    - 布尔值未开启?     │
│    - 端口标签不对?     │
│    - 缺少策略规则?     │
└──────────┬───────────┘
           │
           ▼
┌──────────────────────┐
│ 4. 选择修复方案        │
│    ├─ restorecon       │
│    ├─ setsebool        │
│    ├─ semanage port    │
│    └─ audit2allow      │
└──────────┬───────────┘
           │
           ▼
┌──────────────────────┐
│ 5. 验证修复            │
│    重新测试应用        │
└──────────────────────┘

9.2 审计日志分析

# 查看最近的 SELinux 拒绝
ausearch -m avc -ts recent

# 查看特定进程的拒绝
ausearch -m avc -c httpd

# 查看特定文件的拒绝
ausearch -m avc -f /var/www/html

# 实时监控拒绝事件
ausearch -m avc -i --follow

# 更可读的格式
ausearch -m avc -ts today --raw | aureport --summary

AVC 拒绝日志解读:

type=AVC msg=audit(1623456789.123:456):
  avc:  denied  { read } for
  pid=12345 comm="httpd"
  name="index.html" dev="sda1" ino=78901
  scontext=system_u:system_r:httpd_t:s0
  tcontext=unconfined_u:object_r:user_home_t:s0
  tclass=file permissive=0
字段 含义
denied { read } 被拒绝的操作:读取
pid=12345 comm="httpd" 进程 PID 和命令名
name="index.html" 目标文件名
scontext=...httpd_t:s0 安全上下文(进程)
tcontext=...user_home_t:s0 目标安全上下文(文件)
tclass=file 目标类别:文件
permissive=0 0=Enforcing 模式被拒绝

9.3 使用 audit2allow 生成策略

audit2allow 是最常用的排错工具,它从拒绝日志中生成允许规则:

# 分析所有拒绝事件
audit2allow -a

# 分析最近的拒绝
ausearch -m avc -ts recent | audit2allow

# 生成策略模块
ausearch -m avc -ts today -c httpd | \
    audit2allow -M httpd_custom

# 查看生成的规则
cat httpd_custom.te

# 编译并安装策略模块
semodule -i httpd_custom.pp

audit2allow 输出示例:

# ausearch -m avc -c httpd | audit2allow

#============= httpd_t ==============
allow httpd_t user_home_t:file { read getattr open };
allow httpd_t http_port_t:tcp_socket name_bind;

# 编译为可加载模块
# ausearch -m avc -c httpd | audit2allow -M httpd_fix
# semodule -i httpd_fix.pp

⚠️ 安全警告audit2allow 生成的规则可能过于宽松。务必审查 .te 文件内容,确保只允许必要的权限,而不是盲目加载。

9.4 使用 sealert 分析问题

sealert 提供更友好的问题分析和修复建议:

# 分析审计日志
sealert -a /var/log/audit/audit.log

# 分析特定事件 ID
sealert -l <event_id>

# 浏览器模式(GUI)
sealert -b

sealert 输出示例:

SELinux is preventing /usr/sbin/httpd from read access on the file
/var/www/html/index.html.

***** Plugin restorecon (99.5 confidence) suggests *****

If you want to fix the label.
/var/www/html/index.html default label should be httpd_sys_content_t.
Then you can run restorecon.
Do
# /sbin/restorecon -v /var/www/html/index.html

***** Plugin catchall (1.49 confidence) suggests *****

If you believe that httpd should be allowed read access on the
index.html file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
# ausearch -c 'httpd' --raw | audit2allow -M my-httpd
# semodule -i my-httpd.pp

9.5 Permissive 域调试

# 将特定域设为 permissive(推荐,不影响其他进程)
semanage permissive -a httpd_t

# 运行应用,收集所有 AVC 拒绝
# ... 测试应用所有功能 ...

# 从日志生成策略
ausearch -m avc -c httpd | audit2allow -M httpd_policy

# 检查生成的规则
cat httpd_policy.te

# 加载策略
semodule -i httpd_policy.pp

# 恢复 enforce
semanage permissive -d httpd_t

10. 编写自定义 SELinux 策略

10.1 sepolicy generate 快速生成

RHEL/CentOS 提供 sepolicy generate 命令快速创建自定义策略骨架:

# 安装开发工具
yum install -y policycoreutils-devel setools-console

# 为 /opt/myapp/bin/server 生成策略
sepolicy generate --init /opt/myapp/bin/server

# 生成的文件:
# myapp.te      - Type Enforcement 规则
# myapp.fc      - File Context 定义
# myapp.if      - 接口文件
# myapp_selinux.spec - RPM 打包规范

# 查看生成的规则
cat myapp.te
# 生成的 myapp.te 示例:
policy_module(myapp, 1.0.0)

type myapp_t;
type myapp_exec_t;
init_daemon_domain(myapp_t, myapp_exec_t)

# 网络权限
allow myapp_t self:tcp_socket create_stream_socket_perms;

# 日志权限
allow myapp_t var_log_t:file { append create open };

10.2 手动编写策略模块

# myapp.te
policy_module(myapp, 1.0.0)

# 声明类型
type myapp_t;
type myapp_exec_t;
type myapp_conf_t;
type myapp_log_t;
type myapp_var_t;

# 标记为域
init_daemon_domain(myapp_t, myapp_exec_t)

# 文件访问
allow myapp_t myapp_conf_t:file { read getattr open };
allow myapp_t myapp_log_t:file { append create open write };
allow myapp_t myapp_var_t:dir { add_name create read write search };

# 网络访问
allow myapp_t self:tcp_socket { create bind listen accept };
corenet_tcp_bind_generic_node(myapp_t)
corenet_tcp_bind_http_port(myapp_t)

# 能力
allow myapp_t self:capability { net_bind_service setuid setgid };

# 日志
allow myapp_t var_log_t:dir { add_name write search };

# 基础系统访问
files_read_etc_files(myapp_t)
libs_use_ld_so(myapp_t)
libs_use_shared_libs(myapp_t)
sysnet_dns_name_resolve(myapp_t)
# myapp.fc
/opt/myapp/bin/server     --  gen_context(system_u:object_r:myapp_exec_t,s0)
/opt/myapp/etc(/.*)?      --  gen_context(system_u:object_r:myapp_conf_t,s0)
/opt/myapp/logs(/.*)?     --  gen_context(system_u:object_r:myapp_log_t,s0)
/opt/myapp/data(/.*)?     --  gen_context(system_u:object_r:myapp_var_t,s0)

10.3 编译与加载自定义模块

# 编译策略模块
make -f /usr/share/selinux/devel/Makefile myapp.pp

# 或使用 checkmodule/semodule_package
checkmodule -M -m -o myapp.mod myapp.te
semodule_package -o myapp.pp -m myapp.mod -f myapp.fc

# 加载策略模块
semodule -i myapp.pp

# 应用文件上下文
restorecon -Rv /opt/myapp/

# 验证
ps -eZ | grep myapp
ls -Z /opt/myapp/

11. SELinux 与容器安全

SELinux 为容器提供了强大的隔离保护:

# Docker 中使用 SELinux
docker run -d \
  --security-opt label=type:svirt_apache_t \
  --security-opt label=level:s0:c100,c200 \
  nginx

# 查看容器进程的 SELinux 上下文
ps -eZ | grep docker
# system_u:system_r:container_t:s0:c100,c200

# Podman 原生 SELinux 集成
podman run -d --name web nginx
# 自动分配唯一的 MCS 范畴,容器间完全隔离

容器 SELinux 隔离原理:

容器 A:system_u:system_r:container_t:s0:c100,c200
容器 B:system_u:system_r:container_t:s0:c300,c400

  即使两个容器都是 container_t 类型,
  MCS 范畴不同(c100,c200 vs c300,c400),
  它们无法互相访问对方的文件。

Kubernetes 中的 SELinux:

apiVersion: v1
kind: Pod
metadata:
  name: selinux-pod
spec:
  securityContext:
    seLinuxOptions:
      level: "s0:c100,c200"
      role: "system_r"
      type: "container_t"
      user: "system_u"
  containers:
  - name: app
    image: nginx

12. SELinux vs AppArmor:对比与选型

对比维度 SELinux AppArmor
设计理念 基于标签,全局策略 基于路径,程序为中心
默认发行版 RHEL, CentOS, Fedora Ubuntu, SUSE, Debian
安全模型 TE + RBAC + MLS/MCS 路径+Capability+网络
标识方式 安全标签(inode) 文件路径
默认行为 未配置 = 全部拒绝 未配置 = 不受限
策略语言 内核语言 / CIL AppArmor 专用语法
学习曲线 陡峭(1-3 周) 平缓(1-3 天)
MLS 支持 ✅ 完整支持 ❌ 不支持
硬链接安全 ✅ inode 标签不受影响 ⚠️ 硬链接可能绕过路径
容器生态 Podman/OpenShift 原生 Docker 默认
策略调试 permissive 域 + audit2allow complain 模式 + aa-logprof
Android ✅ 强制执行(5.0+) ❌ 不支持
适用场景 企业/政府/军事/Android 中小部署/Ubuntu 生态

选型建议

使用 RHEL/CentOS/Fedora?
  └─ 是 → SELinux(系统原生支持,无需额外配置)

使用 Ubuntu/Debian/SUSE?
  └─ 是 → AppArmor(系统原生支持)

需要多级安全(MLS)?
  └─ 是 → SELinux(唯一选择)

需要 Android 安全?
  └─ 是 → SELinux(Android 强制要求)

团队安全经验有限?
  └─ 是 → AppArmor(上手更快)

需要最细粒度的安全控制?
  └─ 是 → SELinux(TE 模型粒度最细)

13. 安全加固最佳实践

基本原则

# ✅ 永远不要禁用 SELinux
# ❌ setenforce 0 不是解决方案

# ✅ 使用 permissive 域调试(而非全局 permissive)
semanage permissive -a myapp_t

# ✅ 使用 audit2allow 生成策略(但审查后再加载)
ausearch -m avc -c myapp | audit2allow -M myapp_fix
cat myapp_fix.te  # ← 审查!
semodule -i myapp_fix.pp

# ✅ 使用 restorecon 修复文件标签
restorecon -Rv /path/to/files

生产环境 Checklist

项目 状态 说明
SELinux 为 Enforcing 模式 getenforce 确认
配置文件为 enforcing /etc/selinux/config
定期审计 AVC 拒绝 ausearch -m avc 每周检查
自定义策略使用版本控制 .te/.fc 文件纳入 Git
文件标签正确 restorecon -Rnv / 预检查
布尔值最小化开放 getsebool -a 审查
容器使用 MCS 隔离 ps -eZ 检查范畴唯一性

14. 常见问题 FAQ

Q1:如何快速判断问题是 SELinux 引起的?

# 方法 1:查看审计日志
ausearch -m avc -ts recent | tail -5

# 方法 2:临时切换到 permissive 测试
setenforce 0
# 重新运行出问题的操作
# 如果成功,则是 SELinux 问题
setenforce 1

# 方法 3:查看 sealert
sealert -a /var/log/audit/audit.log | tail -20

Q2:文件标签被改乱了怎么办?

# 查看当前标签
ls -Z /var/www/

# 恢复默认标签
restorecon -Rv /var/www/

# 如果是自定义路径,先定义规则
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
restorecon -Rv /web/

Q3:移动文件后标签变了?

# mv 会保留原始标签,cp 会继承目标目录标签
# 使用 cp 后需要恢复标签
cp /tmp/file /var/www/html/
restorecon -v /var/www/html/file

# 或使用 install 命令(自动设置正确标签)
install -m 644 /tmp/file /var/www/html/

Q4:如何导出和导入 SELinux 策略?

# 导出所有自定义模块
semodule -l | grep -v "^permissive" > custom_modules.txt

# 备份自定义文件上下文
semanage fcontext -l -C > custom_fcontext.txt

# 备份布尔值
getsebool -a > custom_booleans.txt

Q5:SELinux 占用多少性能?

# 查看 AVC 缓存命中率
cat /sys/fs/selinux/avc/cache_stats
# 典型命中率 >99%,性能影响约 1-3%

# 禁用 SELinux 的性能收益远小于安全风险

15. 总结

核心要点

要点 说明
MAC 强制访问控制 SELinux 超越传统 DAC,即使 root 也受策略约束
类型强制(TE) 核心访问控制机制,基于进程域和文件类型
安全上下文 user:role:type:level 四元组标识每个对象
三种模式 Enforcing(生产)/ Permissive(调试)/ Disabled(不推荐)
策略与执行分离 策略可独立更新,不影响执行逻辑
AVC 缓存 高性能访问决策缓存,命中率 >99%
工具链完善 audit2allow, sealert, semanage, restorecon 等
容器集成 Podman/OpenShift 深度集成 MCS 隔离

推荐资源


免责声明:本文内容基于 Linux 内核文档、Red Hat 官方文档和 SELinux 社区资料编写,适用于 Linux 2.6+ 内核系列。具体 API 行为和发行版配置可能有所差异,请以实际运行环境为准。