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) 章节。