SELinux 安全策略配置与故障排查实战

一、SELinux 概述

1.1 什么是 SELinux

SELinux(Security-Enhanced Linux)是由 NSA 开发的 Linux 安全模块,提供强制访问控制(MAC)机制。与传统的自主访问控制(DAC)不同,SELinux 基于策略规则,对进程和文件进行细粒度访问控制。

1.2 SELinux 工作模式

模式 说明 适用场景
Enforcing 强制模式,阻止违规操作并记录 生产环境
Permissive 宽容模式,仅记录不阻止 调试/测试
Disabled 完全禁用 不推荐

1.3 查看 SELinux 状态

1
2
3
4
5
6
7
8
# 查看 SELinux 状态
getenforce

# 查看详细状态
sestatus

# 查看内核启动参数
grep selinux /proc/cmdline

sestatus 输出示例:

1
2
3
4
5
6
7
8
9
10
SELinux status:                 enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Memory protection checking: actual (secure)
Max kernel policy version: 33

二、SELinux 核心概念

2.1 安全上下文(Security Context)

每个进程和文件都有安全上下文,格式为:

1
user:role:type:level

查看安全上下文:

1
2
3
4
5
6
7
8
# 查看文件上下文
ls -Z /path/to/file

# 查看进程上下文
ps -eZ | grep process_name

# 查看端口上下文
semanage port -l | grep http

示例输出:

1
2
3
4
5
# 文件上下文
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html

# 进程上下文
system_u:system_r:httpd_t:s0 1234 ? 00:00:01 httpd

2.2 类型(Type)- 最重要的概念

SELinux 主要使用 类型强制(Type Enforcement) 控制访问:

  • 进程类型:如 httpd_tmysqld_tsshd_t
  • 文件类型:如 httpd_sys_content_tmysqld_db_tssh_home_t

规则:进程类型只能访问允许的文件类型。

2.3 布尔值(Booleans)

布尔值是 SELinux 策略的可调节开关,用于启用/禁用特定功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看所有布尔值
getsebool -a

# 搜索特定服务的布尔值
getsebool -a | grep httpd

# 查看布尔值状态
getsebool httpd_can_network_connect

# 临时启用(重启失效)
setsebool httpd_can_network_connect on

# 永久启用(-P 参数)
setsebool -P httpd_can_network_connect on

# 批量启用
setsebool -P httpd_can_network_connect=1 httpd_enable_homedirs=1

三、SELinux 配置管理

3.1 配置文件

主配置文件:/etc/selinux/config

1
2
3
# /etc/selinux/config
SELINUX=enforcing # enforcing|permissive|disabled
SELINUXTYPE=targeted # targeted|minimum|mls

修改后需重启生效。

3.2 模式切换

1
2
3
4
5
6
7
# 临时切换(重启失效)
setenforce 0 # 切换到 Permissive
setenforce 1 # 切换到 Enforcing

# 永久切换(修改配置文件后重启)
vi /etc/selinux/config
# SELINUX=permissive

3.3 策略管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看已安装策略
semodule -l

# 查看策略详情
semodule -l | grep targeted

# 安装策略模块
semodule -i custom_module.pp

# 移除策略模块
semodule -r custom_module

# 重新编译策略
semodule -B

四、故障排查流程

4.1 典型故障现象

  • 服务启动失败
  • 文件访问被拒绝(Permission denied)
  • 网络连接被阻止
  • 端口绑定失败

4.2 排查步骤

步骤 1:确认是否为 SELinux 问题

1
2
3
4
5
6
7
8
# 临时切换到 Permissive 模式
setenforce 0

# 重试操作
# 如果成功,说明是 SELinux 问题

# 恢复 Enforcing 模式
setenforce 1

步骤 2:查看审计日志

1
2
3
4
5
6
7
8
9
10
11
# 查看 SELinux 拒绝日志
ausearch -m avc -ts recent

# 或使用 audit2why 分析
ausearch -m avc -ts today | audit2why

# 查看完整日志
tail -f /var/log/audit/audit.log | grep denied

# 使用 sealert 分析(需安装 setroubleshoot)
sealert -a /var/log/audit/audit.log

日志示例:

1
2
3
4
5
type=AVC msg=audit(1711180800.123:456): avc:  denied  { read } for  
pid=1234 comm="httpd" name="index.html" dev="sda1" ino=123456
scontext=system_u:system_r:httpd_t:s0
tcontext=system_u:object_r:user_home_t:s0
tclass=file permissive=0

日志解读:

  • denied { read }: 拒绝的操作
  • comm="httpd": 进程名称
  • scontext: 源上下文(进程)
  • tcontext: 目标上下文(文件)
  • tclass=file: 对象类别

步骤 3:分析问题原因

1
2
3
4
5
6
7
# 使用 audit2why 分析原因
ausearch -m avc -ts today | audit2why

# 输出示例:
# type=AVC ... denied { read } ...
# Was caused by:
# Missing type enforcement (TE) allow rule.

步骤 4:生成并应用策略

方法 1:使用 audit2allow 自动生成策略

1
2
3
4
5
6
7
8
9
10
11
# 从日志生成策略模块
ausearch -m avc -ts today | audit2allow -M mypol

# 查看生成的策略
cat mypol.te

# 安装策略模块
semodule -i mypol.pp

# 验证
getenforce

方法 2:手动修复文件上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看文件当前上下文
ls -Z /path/to/file

# 查看应该的上下文
semanage fcontext -l | grep "/path"

# 添加或修改上下文规则
semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?"

# 应用上下文(递归)
restorecon -Rv /var/www/html

# 验证
ls -Z /var/www/html

方法 3:启用相关布尔值

1
2
3
4
5
6
7
8
9
10
11
# 查看相关布尔值
getsebool -a | grep httpd

# 启用网络访问
setsebool -P httpd_can_network_connect on

# 启用家目录访问
setsebool -P httpd_enable_homedirs on

# 启用数据库连接
setsebool -P httpd_can_connect_mysql on

4.3 常见故障案例

案例 1:Nginx 无法访问自定义目录

现象: Nginx 返回 403 Forbidden

排查:

1
2
3
4
# 查看日志
ausearch -m avc -ts recent | grep nginx

# 输出:denied { read } for ... tcontext=...:var_t ...

解决:

1
2
3
4
5
6
# 方法 1:修改文件上下文
semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?"
restorecon -Rv /data/www

# 方法 2:启用布尔值(如果适用)
setsebool -P httpd_read_user_content on

案例 2:MySQL 无法在非标准目录创建数据

现象: MySQL 启动失败,报错 Permission denied

排查:

1
2
ausearch -m avc -ts recent | grep mysqld
# 输出:denied { write } for ... tcontext=...:var_t ...

解决:

1
2
3
4
5
6
# 设置正确的上下文
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
restorecon -Rv /data/mysql

# 重启 MySQL
systemctl restart mysqld

案例 3:应用无法绑定非标准端口

现象: 应用启动失败,无法绑定端口 8080

排查:

1
2
3
4
5
# 查看端口上下文
semanage port -l | grep http

# 查看拒绝日志
ausearch -m avc -ts recent | grep name=8080

解决:

1
2
3
4
5
6
7
8
# 添加端口到 http_port_t 类型
semanage port -a -t http_port_t -p tcp 8080

# 验证
semanage port -l | grep 8080

# 重启应用
systemctl restart myapp

案例 4:Docker 容器无法访问宿主机目录

现象: 容器内无法读取挂载的宿主机目录

解决:

1
2
3
4
5
6
7
8
9
# 方法 1:设置容器共享上下文
chcon -Rt svirt_sandbox_file_t /data/docker/volumes

# 方法 2:永久设置
semanage fcontext -a -t svirt_sandbox_file_t "/data/docker/volumes(/.*)?"
restorecon -Rv /data/docker/volumes

# 方法 3:启用布尔值
setsebool -P container_manage_cgroup on

五、最佳实践

5.1 生产环境配置建议

1
2
3
4
5
6
7
8
9
10
11
# 1. 保持 Enforcing 模式
getenforce # 应返回 Enforcing

# 2. 最小化布尔值启用
getsebool -a | grep on # 只启用必要的

# 3. 定期审计日志
ausearch -m avc -ts weekly | audit2why

# 4. 自定义策略模块化
semodule -l | grep custom # 使用自定义模块

5.2 开发/测试环境建议

1
2
3
4
5
# 使用 Permissive 模式便于调试
setenforce 0

# 但仍记录所有拒绝操作
# 便于上线前修复策略

5.3 策略定制流程

  1. 在 Permissive 模式下测试应用
  2. 收集所有 AVC 拒绝日志
  3. 使用 audit2allow 生成策略
  4. 审查生成的策略(重要!)
  5. 切换回 Enforcing 模式
  6. 安装自定义策略模块
  7. 验证应用正常运行

5.4 常用布尔值参考

Web 服务:

1
2
3
4
setsebool -P httpd_can_network_connect on      # 允许外连
setsebool -P httpd_enable_homedirs on # 访问家目录
setsebool -P httpd_can_connect_mysql on # 连接 MySQL
setsebool -P httpd_can_connect_ldap on # 连接 LDAP

数据库服务:

1
2
setsebool -P mysql_connect_any on              # MySQL 任意连接
setsebool -P postgresql_can_network_connect on # PostgreSQL 外连

容器服务:

1
2
setsebool -P container_manage_cgroup on        # 管理 cgroup
setsebool -P docker_daemon_systemctl on # systemctl 控制

文件共享:

1
2
3
setsebool -P nfs_export_all_rw on              # NFS 读写
setsebool -P samba_enable_home_dirs on # Samba 家目录
setsebool -P ftpd_full_access on # FTP 完全访问

六、监控与告警

6.1 日志监控

1
2
3
4
5
6
7
8
# 实时监控 SELinux 拒绝
tail -f /var/log/audit/audit.log | grep denied

# 统计拒绝次数
ausearch -m avc -ts today | wc -l

# 按类型统计
ausearch -m avc -ts today | audit2why | sort | uniq -c

6.2 Prometheus 监控

1
2
# 使用 node_exporter 收集 SELinux 指标
# 指标:node_selinux_current_mode, node_selinux_enabled

6.3 告警规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# prometheus/alerts/selinux.yml
groups:
- name: selinux
rules:
- alert: SELinuxDisabled
expr: node_selinux_enabled == 0
for: 5m
labels:
severity: warning
annotations:
summary: "SELinux 已禁用"

- alert: SELinuxPermissive
expr: node_selinux_current_mode == 1
for: 30m
labels:
severity: warning
annotations:
summary: "SELinux 处于 Permissive 模式"

- alert: SELinuxDenialsHigh
expr: rate(node_selinux_denials_total[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "SELinux 拒绝操作过多"

七、故障排查命令速查表

目的 命令
查看状态 getenforce, sestatus
切换模式 setenforce 0/1
查看文件上下文 ls -Z file
查看进程上下文 ps -eZ
查看端口上下文 semanage port -l
查看布尔值 getsebool -a
设置布尔值 setsebool -P name on/off
查看拒绝日志 ausearch -m avc -ts recent
分析拒绝原因 audit2why
生成策略 audit2allow -M mypol
安装策略 semodule -i mypol.pp
设置文件上下文 semanage fcontext -a -t type path
应用上下文 restorecon -Rv path
添加端口类型 semanage port -a -t type -p tcp port

八、总结

SELinux 故障排查的核心思路:

  1. 确认问题:切换到 Permissive 模式验证是否为 SELinux 问题
  2. 查看日志:使用 ausearchaudit2why 分析拒绝原因
  3. 选择方案
    • 文件上下文错误 → 使用 semanage fcontext + restorecon
    • 缺少策略规则 → 使用 audit2allow 生成策略
    • 功能被布尔值限制 → 启用相关布尔值
  4. 验证修复:切换回 Enforcing 模式,验证应用正常
  5. 文档记录:记录策略变更,便于后续维护

重要原则:

  • 生产环境保持 Enforcing 模式
  • 不要随意禁用 SELinux
  • 自定义策略要审查后再应用
  • 定期审计和清理无用策略

参考文档: