SELinux 安全机制

SELinux (Security-Enhanced Linux) 是一种强制访问控制(MAC)安全机制,为 Linux 提供了额外的安全层。

SELinux 工作原理

访问控制模型对比

传统 DAC (Discretionary Access Control):
- 基于用户、组、其他
- 文件所有者决定访问权限
- root 拥有完全权限

SELinux MAC (Mandatory Access Control):
- 基于安全策略
- 管理员定义策略规则
- root 也受策略限制

SELinux 三要素

主体(Subject)
  ↓ 访问请求
客体(Object)
  ↓ 策略查询
安全策略(Policy)
  ↓ 决定
允许 / 拒绝

SELinux 模式

三种运行模式

# 查看当前模式
getenforce

# 三种模式:
# 1. Enforcing - 强制模式(拒绝违规操作并记录)
# 2. Permissive - 宽容模式(允许但记录违规)
# 3. Disabled - 禁用模式

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

# 永久修改(需重启)
# /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted

策略类型

# targeted - 针对性策略(默认)
# 只保护特定的系统服务
# 用户进程通常不受限制

# mls - 多级安全策略
# 军事级安全分类

# minimum - 最小策略
# 只保护选定的进程

安全上下文

上下文格式

user:role:type:level
  │    │    │     │
  │    │    │     └─ MLS/MCS 安全级别
  │    │    └─ 类型(Type Enforcement)
  │    └─ 角色
  └─ SELinux 用户

示例:
system_u:object_r:httpd_sys_content_t:s0
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

查看和修改上下文

# 查看文件上下文
ls -Z /var/www/html/
# -rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html

# 查看进程上下文
ps -eZ | grep httpd
# system_u:system_r:httpd_t:s0 1234 ? httpd

# 查看用户上下文
id -Z
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

# 修改文件上下文(临时)
chcon -t httpd_sys_content_t /var/www/html/test.html

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

# 恢复默认上下文
restorecon -v /var/www/html/test.html

布尔值配置

查看和设置布尔值

# 列出所有布尔值
getsebool -a

# 查看特定布尔值
getsebool httpd_can_network_connect

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

# 临时设置
setsebool httpd_can_network_connect on

# 永久设置
setsebool -P httpd_can_network_connect on

# 常用布尔值
setsebool -P httpd_can_network_connect_db on  # 允许 httpd 连接数据库
setsebool -P httpd_can_sendmail on            # 允许 httpd 发送邮件
setsebool -P httpd_enable_homedirs on         # 允许访问用户主目录
setsebool -P ftpd_full_access on              # FTP 完全访问

端口管理

# 查看端口上下文
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 8081

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

# 查看所有自定义端口
semanage port -l -C

故障排查

查看 AVC 日志

# 查看审计日志
ausearch -m avc -ts recent

# 查看特定服务的拒绝日志
ausearch -m avc -c httpd

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

# 使用 sealert
sealert -a /var/log/audit/audit.log

# 实时监控
tail -f /var/log/audit/audit.log | grep AVC

日志示例

type=AVC msg=audit(1234567890.123:456): avc: denied { write } 
  for pid=1234 comm="httpd" name="test.txt" 
  dev="dm-0" ino=67890 
  scontext=system_u:system_r:httpd_t:s0 
  tcontext=unconfined_u:object_r:user_home_t:s0 
  tclass=file permissive=0

解读:
- httpd 进程尝试写入文件
- 文件上下文是 user_home_t
- SELinux 拒绝了此操作

生成策略模块

# 使用 audit2allow 生成策略
ausearch -m avc -ts recent | audit2allow -M mypolicy

# 会生成两个文件:
# mypolicy.te - 策略源文件
# mypolicy.pp - 编译后的策略模块

# 查看策略内容
cat mypolicy.te

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

# 列出已加载的模块
semodule -l

# 删除模块
semodule -r mypolicy

实战案例

案例1:配置 Web 服务器

#!/bin/bash
# setup-web-selinux.sh

# 1. 创建 Web 目录
mkdir -p /web/html
mkdir -p /web/cgi-bin

# 2. 设置文件上下文
semanage fcontext -a -t httpd_sys_content_t "/web/html(/.*)?"
semanage fcontext -a -t httpd_sys_script_exec_t "/web/cgi-bin(/.*)?"

# 3. 应用上下文
restorecon -Rv /web

# 4. 配置端口
semanage port -a -t http_port_t -p tcp 8080

# 5. 设置布尔值
setsebool -P httpd_can_network_connect on
setsebool -P httpd_can_network_connect_db on

# 6. 验证配置
ls -Zd /web/html
semanage port -l | grep 8080
getsebool httpd_can_network_connect

案例2:允许自定义应用

# 假设有一个自定义应用 /opt/myapp/bin/myapp

# 1. 查看当前上下文
ls -Z /opt/myapp/bin/myapp

# 2. 运行应用并检查日志
/opt/myapp/bin/myapp &
ausearch -m avc -ts recent | audit2why

# 3. 如果有拒绝,生成策略
ausearch -m avc -ts recent | audit2allow -M myapp_policy

# 4. 检查生成的策略
cat myapp_policy.te

# 5. 加载策略
semodule -i myapp_policy.pp

# 6. 设置正确的上下文
chcon -t bin_t /opt/myapp/bin/myapp
# 或创建自定义类型
semanage fcontext -a -t myapp_exec_t "/opt/myapp/bin/myapp"
restorecon -v /opt/myapp/bin/myapp

案例3:调试应用权限问题

#!/bin/bash
# debug-selinux.sh

APP_NAME="myapp"
LOG_FILE="/tmp/selinux-debug.log"

echo "=== SELinux 调试报告 ===" > $LOG_FILE
echo "时间: $(date)" >> $LOG_FILE
echo "" >> $LOG_FILE

# 1. 检查 SELinux 状态
echo "--- SELinux 状态 ---" >> $LOG_FILE
getenforce >> $LOG_FILE
echo "" >> $LOG_FILE

# 2. 检查进程上下文
echo "--- 进程上下文 ---" >> $LOG_FILE
ps -eZ | grep $APP_NAME >> $LOG_FILE
echo "" >> $LOG_FILE

# 3. 最近的 AVC 拒绝
echo "--- 最近的 AVC 拒绝 ---" >> $LOG_FILE
ausearch -m avc -ts recent -c $APP_NAME >> $LOG_FILE 2>&1
echo "" >> $LOG_FILE

# 4. 建议的解决方案
echo "--- 建议的解决方案 ---" >> $LOG_FILE
ausearch -m avc -ts recent -c $APP_NAME | audit2why >> $LOG_FILE 2>&1
echo "" >> $LOG_FILE

# 5. 生成策略(如果需要)
echo "--- 自动生成的策略 ---" >> $LOG_FILE
ausearch -m avc -ts recent -c $APP_NAME | audit2allow >> $LOG_FILE 2>&1

cat $LOG_FILE

策略开发

创建自定义策略

# 1. 创建策略源文件 myapp.te
cat > myapp.te << 'EOF'
policy_module(myapp, 1.0.0)

# 声明类型
type myapp_t;
type myapp_exec_t;

# 设置为域
domain_type(myapp_t)
init_daemon_domain(myapp_t, myapp_exec_t)

# 允许规则
allow myapp_t self:process { fork signal };
allow myapp_t self:tcp_socket create_stream_socket_perms;

# 文件访问
files_read_etc_files(myapp_t)
logging_send_syslog_msg(myapp_t)

# 网络访问
corenet_tcp_bind_generic_node(myapp_t)
corenet_tcp_connect_http_port(myapp_t)
EOF

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

# 3. 加载策略
semodule -i myapp.pp

# 4. 设置文件上下文
semanage fcontext -a -t myapp_exec_t "/opt/myapp/bin/myapp"
restorecon -v /opt/myapp/bin/myapp

策略语法

# 基本语法
allow source_type target_type:class { permissions };

# 示例
allow httpd_t httpd_sys_content_t:file { read open getattr };

# 类型转换
type_transition source_type target_type:class new_type;

# 角色转换
role_transition source_role target_type new_role;

# 布尔值
if (httpd_can_network_connect) {
    allow httpd_t port_type:tcp_socket name_connect;
}

# 宏
files_read_etc_files(domain)
# 展开为多个 allow 规则

性能优化

# 禁用不需要的审计
auditctl -l  # 列出规则
auditctl -D  # 删除所有规则

# 减少日志量
# /etc/audit/rules.d/audit.rules
# 只记录拒绝的操作
-a always,exclude -F msgtype=AVC -F success=no

# 清理旧日志
logrotate -f /etc/logrotate.d/audit

# 监控性能影响
perf stat -e syscalls:sys_enter_read,syscalls:sys_enter_write ./app

最佳实践

开发环境

# 1. 初始使用 Permissive 模式
setenforce 0

# 2. 运行应用,收集日志
./run_tests.sh

# 3. 分析并生成策略
ausearch -m avc -ts recent | audit2allow -M myapp
semodule -i myapp.pp

# 4. 切换到 Enforcing 测试
setenforce 1
./run_tests.sh

# 5. 重复 2-4 直到无错误

生产环境

# 1. 始终使用 Enforcing 模式
# 2. 定期审查审计日志
# 3. 最小权限原则
# 4. 使用标准策略类型
# 5. 谨慎使用 audit2allow(避免过度许可)
# 6. 文档化所有自定义策略

安全检查清单

#!/bin/bash
# selinux-security-check.sh

echo "=== SELinux 安全检查 ==="

# 1. 检查模式
echo "1. SELinux 模式: $(getenforce)"

# 2. 检查是否有 unconfined 进程
echo "2. Unconfined 进程:"
ps -eZ | grep unconfined_t | wc -l

# 3. 检查 permissive 域
echo "3. Permissive 域:"
semanage permissive -l

# 4. 检查最近的拒绝
echo "4. 最近 24 小时的拒绝次数:"
ausearch -m avc -ts today | wc -l

# 5. 检查自定义策略模块
echo "5. 自定义策略模块:"
semodule -l | grep -v "^selinux_" | wc -l

# 6. 检查布尔值变更
echo "6. 非默认布尔值:"
semanage boolean -l | grep "State.*Default" | grep -v "State: on.*Default: on" | wc -l

下一步:学习 防火墙配置(iptables/nftables) 章节。