Linux SELinux 管理
大约 11 分钟约 3345 字
Linux SELinux 管理
简介
SELinux(Security-Enhanced Linux)是 Linux 的强制访问控制(MAC)安全模块,由美国国家安全局(NSA)开发,通过安全上下文和策略限制进程的访问权限。理解 SELinux 的模式、上下文和策略管理,有助于在保证安全的同时正确运行服务。
SELinux 与传统的 Linux 权限模型(DAC,自主访问控制)不同,它提供了一层额外的安全保护。在 DAC 模型下,root 用户可以访问系统上的任何文件;但在 SELinux 的 MAC 模型下,即使 root 用户运行的进程也受到 SELinux 策略的限制。这意味着即使攻击者获得了某个服务的 root 权限,SELinux 也可以限制该进程只能访问被策略允许的资源。
SELinux 与传统权限的关系
访问控制决策流程:
进程请求访问文件
↓
DAC 检查(传统权限:rwx、uid/gid)
↓ 通过
MAC 检查(SELinux 策略:安全上下文)
↓ 通过
允许访问SELinux 不是替代传统权限,而是在传统权限之上增加一层额外的检查。两者都通过才能访问资源。
SELinux 三种模式
Enforcing(强制模式)
├── 所有策略规则都被强制执行
├── 违反策略的操作会被拒绝并记录日志
└── 生产环境推荐模式
Permissive(宽容模式)
├── 策略规则检查但不强制执行
├── 违反策略的操作只记录日志,不拒绝
└── 用于调试和策略开发
Disabled(禁用模式)
├── 完全关闭 SELinux
├── 不进行任何安全检查
└── 不推荐(降低系统安全性)特点
实现
模式与上下文管理
# 查看当前模式
getenforce # Enforcing/Permissive/Disabled
sestatus # 详细状态
# sestatus 输出示例:
# 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
# 切换模式(临时)
setenforce 0 # 切换到 Permissive
setenforce 1 # 切换到 Enforcing
# 永久修改(重启生效)
# /etc/selinux/config
# SELINUX=enforcing
# 或使用 sed 命令修改
sudo sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
# 查看文件安全上下文
ls -Z /var/www/html/
# system_u:object_r:httpd_sys_content_t:s0 index.html
# 查看进程安全上下文
ps -eZ | grep nginx
ps -eZ | grep httpd
# 查看所有进程的 SELinux 上下文
ps -eZ | head -20安全上下文详解
安全上下文格式:user:role:type:level
user: 用户标识(system_u、user_u、unconfined_u)
role: 角色标识(object_r、system_r)
type: 类型标识(最关键的部分,策略规则基于 type 编写)
level: 安全级别(MLS/MCS 多级安全)
示例:
system_u:object_r:httpd_sys_content_t:s0
│ │ │ │
│ │ │ └── 安全级别(MLS)
│ │ └── 类型(文件类型)
│ └── 角色(object 表示被动对象)
└── 用户(system 表示系统进程)修改文件上下文
# 临时修改文件上下文(重启后失效)
chcon -t httpd_sys_content_t /var/www/html/index.html
chcon -R -t httpd_sys_content_t /var/www/html/
# 恢复默认上下文
restorecon -Rv /var/www/html/
# 永久设置上下文规则(推荐)
semanage fcontext -a -t httpd_sys_content_t "/data/web(/.*)?"
restorecon -Rv /data/web
# 查看文件的默认上下文规则
semanage fcontext -l | grep /var/www
# 查看当前目录的上下文
matchpathcon /var/www/html/index.html
# 复制文件时保留上下文
cp --preserve=context source dest
# 移动文件时保留上下文
mv source dest # mv 会保留原上下文,可能不匹配新位置布尔值管理
SELinux 布尔值(Boolean)是一种快速调整策略的开关机制,无需修改或重编译策略文件。
# 查看所有布尔值
getsebool -a
# 查看特定服务的布尔值
getsebool -a | grep httpd
# httpd_can_network_connect --> off
# httpd_can_network_connect_db --> off
# httpd_can_network_relay --> off
# httpd_enable_homedirs --> off
# httpd_unified --> off
# httpd_read_user_content --> off
# 查看布尔值的详细说明
semanage boolean -l | grep httpd_can_network_connect
# 设置布尔值(临时,重启失效)
setsebool httpd_can_network_connect on
# 设置布尔值(永久)
setsebool -P httpd_can_network_connect on # 允许 HTTP 连接网络
setsebool -P httpd_can_network_connect_db on # 允许连接数据库
setsebool -P httpd_enable_homedirs on # 允许访问家目录
setsebool -P nis_enabled on # NIS 支持
setsebool -P use_nfs_home_dirs on # NFS 家目录
setsebool -P samba_enable_home_dirs on # Samba 家目录
setsebool -P ftp_home_dir on # FTP 访问家目录
setsebool -P ssh_keysign on # SSH 密钥签名常用布尔值速查
# Web 服务器相关
setsebool -P httpd_can_network_connect on # Nginx/Apache 反向代理
setsebool -P httpd_can_network_connect_db on # 连接 MySQL/PostgreSQL
setsebool -P httpd_can_network_relay on # 代理转发
setsebool -P httpd_enable_homedirs on # 访问用户家目录
setsebool -P httpd_read_user_content on # 读取用户文件
setsebool -P httpd_unified on # Apache 统一策略
# 数据库相关
setsebool -P selinuxuser_postgresql_connect_enabled on
# 文件共享相关
setsebool -P samba_enable_home_dirs on # Samba 访问家目录
setsebool -P use_nfs_home_dirs on # NFS 挂载家目录
setsebool -P virt_use_nfs on # 虚拟化使用 NFS
# Docker 相关
setsebool -P container_use_cephfs on # Docker 使用 CephFS故障排查
# 1. 查看 SELinux 拒绝日志
ausearch -m AVC,USER_AVC,SELINUX_ERR -ts recent
# AVC = Access Vector Cache,记录了被拒绝的访问
ausearch -m AVC -ts recent | audit2why
# audit2why 会给出被拒绝的原因和建议的修复方法
# 2. 快速定位问题
grep denied /var/log/audit/audit.log | tail -10
# 3. 使用 sealert 分析(需要 setroubleshoot-server)
sealert -a /var/log/audit/audit.log
# sealert 给出更友好的中文提示和修复建议
# 4. 生成允许规则
grep "apache" /var/log/audit/audit.log | audit2allow -M myapache
# 生成 myapache.pp 和 myapache.te 文件
semodule -i myapache.pp
# 安装自定义策略模块
# 5. 查看 SELinux 审计日志(journalctl 方式)
journalctl -t setroubleshoot | tail -20audit2allow 工作流程
# 步骤 1:先切到 Permissive 模式收集日志
setenforce 0
# 步骤 2:运行服务,触发被拒绝的操作
systemctl restart nginx
curl http://localhost
# 步骤 3:从审计日志生成策略模块
grep nginx /var/log/audit/audit.log | audit2allow -M nginx_custom
# 生成 nginx_custom.te(策略源码)和 nginx_custom.pp(编译后的策略)
# 步骤 4:查看生成的策略(确认安全)
cat nginx_custom.te
# 步骤 5:安装策略模块
semodule -i nginx_custom.pp
# 步骤 6:切回 Enforcing 模式
setenforce 1
# 步骤 7:验证服务正常
systemctl restart nginx
curl http://localhost
# 查看已安装的自定义策略模块
semodule -l | grep nginx_custom
# 删除自定义策略模块
semodule -r nginx_custom常见服务 SELinux 配置
# Nginx 代理后端服务
setsebool -P httpd_can_network_connect on
setsebool -P httpd_can_network_relay on
# Nginx 自定义网站目录
semanage fcontext -a -t httpd_sys_content_t "/data/web(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/data/web/uploads(/.*)?"
restorecon -Rv /data/web
# MySQL 自定义数据目录
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
restorecon -Rv /data/mysql
# PostgreSQL 自定义数据目录
semanage fcontext -a -t postgresql_db_t "/data/postgresql(/.*)?"
restorecon -Rv /data/postgresql
# Redis 自定义目录
semanage fcontext -a -t redis_var_lib_t "/data/redis(/.*)?"
restorecon -Rv /data/redis
# Docker 与 SELinux
# Docker 挂载卷添加 :z 或 :Z 后缀
# :z = 多容器共享标签(共享卷)
# :Z = 私有标签(单容器使用)
# docker run -v /data:/data:Z myimage
# docker-compose:
# volumes:
# - ./data:/app/data:z
# 自定义 Web 应用目录
semanage fcontext -a -t httpd_sys_content_t "/opt/myapp/static(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/opt/myapp/uploads(/.*)?"
restorecon -Rv /opt/myappSELinux 策略模块编写
# 自定义策略模块(.te 文件)示例
# 允许 Nginx 访问自定义端口和目录
# mynginx.te
module mynginx 1.0;
require {
type httpd_t;
type httpd_sys_content_t;
type httpd_log_t;
type port_t;
type etc_t;
class tcp_socket { name_bind name_connect };
class dir { read search open getattr };
class file { read open getattr };
}
# 允许 httpd_t 类型绑定到非标准端口
allow httpd_t port_t:tcp_socket name_bind;
# 允许 httpd_t 读取自定义目录
allow httpd_t httpd_sys_content_t:dir { read search open getattr };
allow httpd_t httpd_sys_content_t:file { read open getattr };
# 编译和安装自定义模块
# 1. 检查策略语法
checkmodule -M -m -o mynginx.mod mynginx.te
# 2. 创建策略包
semodule_package -o mynginx.pp -m mynginx.mod
# 3. 安装策略模块
semodule -i mynginx.pp
# 4. 验证安装
semodule -l | grep mynginx
# 5. 卸载模块
semodule -r mynginxSELinux 与 Docker 深入配置
# Docker 容器的 SELinux 上下文
# 查看容器进程的 SELinux 标签
docker exec mycontainer cat /proc/self/attr/current
# 输出:system_u:system_r:container_t:s0:c452,c823
# Docker 守护进程的 SELinux 类型
ps -eZ | grep dockerd
# system_u:system_r:docker_t:s0
# 挂载卷时的 SELinux 处理
# :z — 多容器共享标签(多个容器可以读写同一个卷)
docker run -v /data/shared:/data:z myimage
# :Z — 私有标签(仅当前容器可以访问)
docker run -v /data/private:/data:Z myimage
# 不使用后缀时,SELinux 可能阻止容器访问挂载目录
# 这就是为什么很多 Docker 挂载问题都与 SELinux 有关
# 查看 Docker 相关的布尔值
getsebool -a | grep container
# container_use_cephfs --> off
# container_use_fusefs --> off
# virt_sandbox_use_audit --> on
# virt_use_nfs --> off
# 允许容器使用 NFS
setsebool -P virt_use_nfs on
# 允许容器使用 CephFS
setsebool -P container_use_cephfs onSELinux 日志分析与监控
# 安装 setroubleshoot 工具(提供友好错误提示)
yum install -y setroubleshoot-server
# 启动 auditd 服务(记录 SELinux 审计日志)
systemctl enable auditd
systemctl start auditd
# 查看 SELinux 拒绝日志(详细格式)
ausearch -m AVC -ts recent -i
# 使用 audit2why 分析拒绝原因
ausearch -m AVC -ts recent | audit2why
# 使用 sealert 获取可读的错误分析
sealert -a /var/log/audit/audit.log
# 示例输出:
# SELinux is preventing /usr/sbin/nginx from read access on the file /data/web/index.html
#
# ***** Plugin catchall (100. confidence) suggests **************************
#
# If you believe that nginx 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 allow this access for now by executing:
# # grep nginx /var/log/audit/audit.log | audit2allow -M mypol
# # semodule -i mypol.pp
# 统计 SELinux 拒绝次数
ausearch -m AVC -ts today | grep denied | wc -l
# 按服务统计 SELinux 拒绝
ausearch -m AVC -ts today | grep denied | \
awk '{for(i=1;i<=NF;i++) if($i ~ /scontext/) print $i}' | \
sort | uniq -c | sort -rn
# 设置 SELinux 审计日志大小
cat >> /etc/audit/auditd.conf << 'EOF'
max_log_file = 50
max_log_file_action = ROTATE
num_logs = 5
EOF
systemctl restart auditdSELinux 安全加固
# 1. 确保 SELinux 处于 Enforcing 模式
getenforce
# 如果不是 Enforcing,修改 /etc/selinux/config
# SELINUX=enforcing
# 2. 安装安全策略工具
yum install -y policycoreutils-python-utils setools-console
# 3. 检查系统文件上下文是否被篡改
rpm -qf /etc/passwd
# 查看 RPM 包管理的文件的 SELinux 上下文
rpm -ql selinux-policy-targeted | head -5
# 4. 重新标记整个文件系统(谨慎使用,耗时长)
touch /.autorelabel
reboot
# 系统重启时会自动重新标记所有文件
# 5. 检查是否有进程运行在 unconfined 域(安全风险)
ps -eZ | grep unconfined
# unconfined 域的进程不受 SELinux 限制
# 6. 查看哪些文件被修改了非默认上下文
semanage fcontext -l | wc -l # 查看所有上下文规则数量
# 7. 禁止特定用户登录
# 通过 SELinux 限制用户切换
semanage login -a -s user_u restricted_user
# restricted_user 只能运行在 user_u 域中
# 8. 配置 SSH 使用特定的 SELinux 域
# 默认 sshd 运行在 sshd_t 域
ps -eZ | grep sshdSELinux 与常见中间件
# MySQL/MariaDB
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
restorecon -Rv /data/mysql
setsebool -P selinuxuser_mysql_connect_enabled on
# PostgreSQL
semanage fcontext -a -t postgresql_db_t "/data/postgresql(/.*)?"
restorecon -Rv /data/postgresql
setsebool -P selinuxuser_postgresql_connect_enabled on
# Tomcat
semanage fcontext -a -t tomcat_log_t "/opt/tomcat/logs(/.*)?"
semanage fcontext -a -t tomcat_var_lib_t "/opt/tomcat/data(/.*)?"
restorecon -Rv /opt/tomcat
# PHP-FPM
setsebool -P httpd_can_network_connect_db on
setsebool -P httpd_execmem on
# Elasticsearch
semanage fcontext -a -t elasticsearch_log_t "/var/log/elasticsearch(/.*)?"
semanage fcontext -a -t elasticsearch_data_t "/data/elasticsearch(/.*)?"
restorecon -Rv /var/log/elasticsearch /data/elasticsearch
# MongoDB
semanage fcontext -a -t mongod_var_lib_t "/data/mongodb(/.*)?"
semanage fcontext -a -t mongod_log_t "/var/log/mongodb(/.*)?"
restorecon -Rv /data/mongodb /var/log/mongodb
# MinIO
setsebool -P httpd_can_network_connect on
semanage fcontext -a -t httpd_sys_content_t "/data/minio(/.*)?"
restorecon -Rv /data/minio优点
缺点
总结
SELinux 通过强制访问控制提升系统安全性。Enforcing 模式强制执行策略,Permissive 模式仅记录不拒绝。遇到问题时先排查 SELinux 拒绝日志,用 audit2allow 生成策略而非直接关闭。生产环境应保持 Enforcing 模式,通过合理配置上下文和布尔值来满足业务需求。
关键知识点
- SELinux 上下文格式:user:role:type:level
- restorecon 恢复默认上下文,chcon 临时修改
- 布尔值(setsebool)是调整策略的快捷方式
- Permissive 模式用于调试,记录所有违反但不拒绝
- audit2allow 可以根据审计日志自动生成策略模块
- Docker 挂载卷需要使用 😒 或 :Z 后缀处理 SELinux 标签
项目落地视角
- 生产环境保持 Enforcing 模式
- 使用 sealert 分析拒绝日志找到根本原因
- 为自定义路径设置永久上下文规则
- 新服务部署时先 Permissive 收集日志,再生成策略模块
- 将 SELinux 上下文配置纳入部署流程
常见误区
- 遇到问题直接 setenforce 0 关闭 SELinux
- 用 chcon 修改上下文不持久(应用 restorecon)
- 忽略 SELinux 拒绝日志导致安全漏洞
- 不理解 type 字段是策略匹配的关键
- Docker 挂载卷不使用 😒/:Z 后缀导致 SELinux 阻止访问
进阶路线
- 学习 SELinux 策略语言编写自定义模块
- 研究 MLS(多级安全)策略
- 了解容器运行时的 SELinux 支持
- 学习 SELinux 与 AppArmor 的区别和适用场景
适用场景
- Web 服务器安全加固
- 数据库文件目录权限控制
- 多租户环境的安全隔离
- 合规性要求(等保、PCI-DSS 等要求启用 SELinux)
落地建议
- 保持 SELinux Enforcing 模式
- 用 sealert 分析问题而非关闭
- 为自定义路径设置 semanage fcontext
- 将 SELinux 配置纳入自动化部署脚本
排错清单
- 检查 getenforce 模式
- 用 sealert 分析 audit.log
- 确认文件上下文是否正确(ls -Z)
- 检查布尔值是否已开启(getsebool)
- 确认 auditd 服务是否运行(systemctl status auditd)
复盘问题
- SELinux 拒绝了多少次非法访问?
- 关闭 SELinux 的安全风险有多大?
- 如何在不降低安全性的前提下简化运维?
- 当前服务的 SELinux 策略模块是否经过审计?
