SELinux介绍
SELinux介绍
booleans(8), setsebool(8), sepolicy(8), system-config-selinux(8), togglesebool(8), restorecon(8), fixfiles(8), setfiles(8), semanage(8), sepolicy(8)
SELinux(Security-Enhanced Linux)是一套比传统用户权限更强、更细粒度、更灵活的权限管控机制。由于不受传统用户权限限制,因此SELinux也可以限制root用户的权限。
SELinux的权限访问控制模型
SELinux 主要作用就是最大限度地减小系统中服务进程可访问的资源(最小权限原则)。
访问控制模型
访问控制模型包括三个要素,即:
- 主体(Subject):主动对其它实体施加动作的实体【即进程】
- 客体(Object):被动接受其他实体访问的实体【即文件】
- 控制策略(Policy):主体对客体的操作行为和约束条件【即SELinux策略】
DAC权限模型
DAC权限模型(Discretionary Access Control)下,决定一个资源是否能被访问的因素是:某个资源是否拥有对应用户的权限(读、写、执行)。即是以用户账号为权限管控的主体。
MAC权限模型
MAC权限模型(Mandatory Access Control)下,决定一个资源是否能被访问的因素除了DAC模型之外,还需要判断每一类进程是否拥有对某一类资源的访问权限。
这样一来,即使进程是以 root 身份运行的,也需要判断这个进程的类型以及允许访问的资源类型才能决定是否允许访问某个资源。进程的活动空间也可以被压缩到最小。
而 MAC 又细分为了两种方式,一种叫类别安全(MCS)模式,另一种叫多级安全(MLS)模式。
标签/安全上下文
任何文件、目录、设备、端口(一切皆文件)都有一个对应的SELinux上下文(context),也叫做标签(label),存储在文件的扩展属性(extended attributes)中。
安全上下文我自己把它分为「进程安全上下文」和「文件安全上下文」。一个「进程安全上下文」一般对应多个「文件安全上下文」。只有两者的安全上下文对应上了,进程才能访问文件(主客体的上下文要对应)。它们的对应关系由policy中的规则决定。
文件安全上下文由文件创建的位置和创建文件的进程所决定。而且系统有一套默认值,用户也可以对默认值进行设定。
需要注意的是,单纯的移动文件操作并不会改变文件的安全上下文。
ls -Z /var/www/html/index.html

上图 httpd_sys_content_t 就是该文件的安全上下文。
策略
SELinux的权限由策略(policy)指定。同一时刻只会有一个policy被激活执行。
策略规定了:哪种类型的进程,能访问哪种标签的对象,以什么方式访问(读、写、执行等)。
比如:
- 类型为
httpd_t的进程(例如 nginx、httpd) - 可以读取类型为
httpd_sys_content_t的文件 - 不可以写这些文件,除非文件被标记为
httpd_sys_rw_content_t
只要行为不符合规则,哪怕用户权限没问题,访问照样被拒绝!
查看所有的策略:
sudo semanage fcontext -l
工作模式
| 模式 | 描述 |
|---|---|
| Enforcing | 强制执行安全策略,违反策略的操作会被拒绝并记录。 |
| Permissive | 仅记录违反策略的操作,但不阻止。一般用于调试。 |
| Disabled | 完全关闭 SELinux。 |
临时切换模式(重启后失效):
setenforce 0 # 切换到 Permissive
setenforce 1 # 切换回 Enforcing
永久修改需编辑配置文件 /etc/selinux/config:重启才生效
工作过程
-
进程发起访问请求
用户空间中的进程(例如httpd)尝试访问某个受保护的资源,如读取文件/var/www/html/index.html。 -
内核拦截并触发 SELinux 检查
Linux 内核在处理该系统调用(如open()或read())时,检测到启用了 SELinux 强制访问控制(MAC),于是将访问请求交由 SELinux 安全模块进行策略评估。 -
获取主体与客体的安全上下文
SELinux 从安全上下文(security context)中提取:- 主体(Subject):发起访问的进程的安全上下文,其类型(type)为
httpd_t; - 客体(Object):目标文件的安全上下文,其类型为
httpd_sys_content_t。
安全上下文通常格式为user:role:type:level,其中类型(type)是类型强制(Type Enforcement, TE)策略判断的核心依据。
- 主体(Subject):发起访问的进程的安全上下文,其类型(type)为
-
查询访问向量缓存(AVC)
SELinux 首先在 AVC(Access Vector Cache)中查找是否已有针对该(主体类型, 客体类型, 权限)组合的授权决策缓存记录:- 若命中缓存:直接返回“允许”或“拒绝”的结果;
- 若未命中:继续向 SELinux 策略引擎(Policy Decision Module)发起查询。
-
策略引擎进行访问决策
SELinux 策略子系统根据加载的策略规则(通常是编译后的二进制策略文件/etc/selinux/targeted/policy/policy.*)判断是否允许该操作。
例如,检查是否存在如下形式的 allow 规则:allow httpd_t httpd_sys_content_t : file { read getattr open };若规则存在且匹配所需权限(如
read),则授权;否则拒绝。 -
返回决策结果给内核
SELinux 将策略决策结果(允许/拒绝)返回给内核的 LSM(Linux Security Modules)钩子函数。 -
内核执行或拒绝操作
内核根据 SELinux 的返回值决定是否继续执行原始系统调用:- 若允许,则完成文件读取等操作;
- 若拒绝,则向用户空间返回错误(如
Permission denied,对应 errnoEACCES)。
-
记录拒绝事件(如适用)
如果访问被拒绝,SELinux 会生成一条 AVC 拒绝日志条目,并通过审计子系统(auditd)写入日志文件(通常为/var/log/audit/audit.log),也可能同时出现在/var/log/messages或通过ausearch、sealert等工具查看。
日志内容包含主体/客体上下文、请求权限、时间戳等信息,用于故障排查或策略调优。
使用
启用 SELinux 的前提是:当前运行的内核必须在编译时启用了 SELinux 支持(CONFIG_SECURITY_SELINUX)。
这是必要条件,没有它,SELinux 无法工作。在此基础上,还需安装用户空间组件并正确配置。
安装SELinux
由于SELinux在大多数发行版中是直接编译进内核而不是作为内核模块加载,所以要激活SELinux的机制,要生效则必须重启系统。这里以Debian为例。
1. 禁用AppArmor
Debian系的Linux发行版预装的是AppArmor作为默认的Linux Security Module (LSM)。但是Linux同一时刻只能有一个LSM,所以必须先禁用AppArmor。
补充:Disabling AppArmor removes its protection from confined applications. If you decide to revert to AppArmor later, you will need to re-enable and reboot. Consider backing up any custom AppArmor profiles before proceeding.
sudo systemctl status apparmor
sudo systemctl disable apparmor --now # 相当于先stop再disable。从而apparmor不会在下次重启时自动启动
2. 安装和激活SELinux
sudo apt install policycoreutils selinux-utils selinux-basics selinux-policy-default auditd policycoreutils-python-utils
修改GRUB来激活SELinux:
sudo selinux-activate # 这个脚本会在/etc/default/grub的GRUB_CMDLINE_LINUX变量中去除selinux=0,并设置security=selinux
# 会产生如下输出:
# Activating SE Linux
# Generating grub configuration file ...
# Found linux image: /boot/vmlinuz-6.x.x-amd64
# Found initrd image: /boot/initrd.img-6.x.x-amd64
# done
# SE Linux is activated. You may need to reboot now.
重启系统。激活SELinux后的第一次启动会很慢。因为需要给系统中的每一个文件都打上SELinux的标签(安全上下文)。
系统启动后,验证SELinux已经激活:
sestatus
# 会看到以下输出:
# SELinux status: enabled
# SELinuxfs mount: /sys/fs/selinux
# SELinux root directory: /etc/selinux
# Loaded policy name: default
# Current mode: permissive
# Mode from config file: permissive
# Policy MLS status: enabled
# Policy deny_unknown status: allowed
# Memory protection checking: actual (secure)
# Max kernel policy version: 33
可以看到,激活后的SELinux默认是permissive模式。
3. 移除SELinux
如果想要移除SELinux,要非常小心,否则会造成无法启动系统。
# 设置为permissive模式
sudo setenforce 0
修改/etc/selinux/config中为disable:SELINUX=disabled
# 移除GRUB中的selinux相关参数
sudo selinux-activate disable
# 卸载用户态工具
sudo apt remove --purge selinux-basics selinux-policy-default policycoreutils policycoreutils-python-utils selinux-utils auditd
sudo apt autoremove
# 如果要重新启用Apparmor
sudo systemctl enable apparmor
重启系统。
# 检查SELinux是否被关闭了
sestatus # 应该输出disabled
常用命令
| 命令 | 用途 |
|---|---|
| sestatus | 查看当前 SELinux 状态 |
| getenforce | 查看当前 SELinux 模式 |
| setenforce | 临时切换模式 |
| ls -Z | 查看文件或目录的标签 |
| ps -eZ | 查看进程的标签 |
| chcon | 临时修改标签(重启失效) |
| restorecon | 恢复默认标签(结合 semanage 使用) |
| semanage | 配置永久标签、端口、布尔值等 |
配置SELinux
需要修改 /etc/selinux/config 配置,重启后才生效。
/etc/selinux/config :SELinux 的主配置文件,用于在系统启动时设置 SELinux 的全局行为。它只被 SELinux 初始化脚本(如 selinuxenabled、init 或 systemd 相关服务)读取,用来决定:
- SELinux 是否启用(
SELINUX=enforcing|permissive|disabled) - 使用哪个策略类型(
SELINUXTYPE=default/targeted|minimum|mls)- targeted:对大部分网络服务进程进行管制。这是系统默认使用的策略。
- minimum:以 targeted 为基础,仅对选定的网络服务进程进行管制。一般不用。
- mls:多级安全保护。对所有的进程进行管制。这是最严格的政策,配置难度非常大。一般不用,除非对安全性有极高的要求。
# 更改SELinux的工作模式
SELINUX=enforcing
# 可选值:enforcing、permissive、disabled
# 更改要运行的Policy
SELINUXTYPE=default
# 可选值:default、minimum、mls
注意:
-
这里设置
SELINUX=disabled的方式已经被废弃了,因此在高版本的内核上可能并没有完全关闭SELinux(可能仍有SELinux hooks被执行)。现在推荐的做法是设置内核启动变量selinux=0来彻底关闭,此时会忽略/etc/selinux/config中设置的内容。参见:2.6. 在引导时更改 SELinux 模式 | 使用 SELinux | Red Hat Enterprise Linux | 8 | Red Hat Documentation -
在Debian系上的 "default" policy 和在Red Hat系上的 "targeted" 是同一个东西,但是名字不要混用。
-
policy必须被安装到
/etc/selinux/{SELINUXTYPE}目录下。
对于policy,还可以通过SELinux提供的boolean来开关特定policy的特性:
-
列出HTTP相关的booleans:
sudo getsebool -a | grep httpd # 会看到类似输出: # httpd_can_network_connect --> off # httpd_can_network_connect_db --> off # httpd_can_sendmail --> off # httpd_enable_cgi --> on -
更改特定的booleans的值:
# 临时开启(重启后失效) sudo setsebool httpd_can_network_connect on # 永久开启(会写入策略配置) sudo setsebool -P httpd_can_network_connect on
给具体应用配置SELinux权限
以Nginx为例。
-
检查有哪些端口放行了HTTP流量:
sudo semanage port -l | grep http_port_t # http_port_t tcp 80, 443, 488, 8008, 8009, 8443, 8448可以看到有一些端口已经放行了HTTP流量。
-
给自己的端口8080放行HTTP流量:
sudo semanage port -a -t http_port_t -p tcp 8080注意,SELinux默认已经将8080端口授予了
http_cache_port_t标签。在Debian13以前,执行上述命令会看到“Port tcp/8080 already defined.”的错误,因此需要将-a(add)选项更改为-m(modify)。而Debian13则会自动帮你在这种情况下把-a改为-m。 -
授予存放静态页面文件的文件夹以
httpd_sys_content_t标签。(默认情况下,SELinux 只信任标准 Web 目录(如/var/www/html)的内容。如果你把网站放在非标准位置(如/srv/www),SELinux 会阻止 httpd 访问,导致 403 Forbidden 错误)sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"然后给这个
/srv/www下的所有现存文件都打上httpd_sys_content_t标签:sudo restorecon -Rv /srv/www效果:
root@debian-13:~# ls -Z /srv/www/index.html system_u:object_r:var_t:s0 /srv/www/index.html root@debian-13:~# sudo restorecon -Rv /srv/www Relabeled /srv/www from system_u:object_r:var_t:s0 to system_u:object_r:httpd_sys_content_t:s0 Relabeled /srv/www/index.html from system_u:object_r:var_t:s0 to system_u:object_r:httpd_sys_content_t:s0
常见错误排查
首先排查确实是SELinux限制权限导致的问题。
-
先切到permssive模式
sudo setenforce 0 # 验证确实切换成功了 getenforce # 应该输出Permissive -
测试自己的程序,看看是否还有问题。如果没问题了,说明就是SELinux限制权限导致的问题。此时可以查看
/var/log/audit/audit.log审计日志来看下是什么行为导致违例。记得确保 auditd 服务事先是开启的:sudo systemctl status auditd。查看日志时可以使用辅助工具:# 查看原始 audit 日志 sudo grep 'denied' /var/log/audit/audit.log | tail -20 # 实时跟踪 sudo tail -f /var/log/audit/audit.log # 使用专用工具(更易读) sudo ausearch -m avc # 查找所有 AVC 拒绝 sudo ausearch -ts recent # 查看最近的 audit 事件 sudo aureport --avc # 生成 AVC 拒绝摘要报告 # 将 audit.log 中的拒绝信息转为人可读建议 sudo ausearch -m avc -ts recent | audit2why一个示例的denied日志:
type=AVC msg=audit(1234567890.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:var_t:s0 tclass=file permissive=0 -
解决完问题后,记得恢复之前的SELinux工作模式。
常见问题解决
文件/进程缺少标签:用 restorecon 自动打上所需标签即可。
启用booleans:用 setsebool 开启对应的booleans即可。不太推荐,因为不符合最小权限原则,可能给其他进程放行了。
最推荐的方法是:为被拒绝的操作生成一个自定义 SELinux 策略模块(policy module),然后加载它。
-
为denied日志生成对应的policy。这里称作mycustommodule:
# 取出最近的denied日志 sudo grep 'denied' /var/log/audit/audit.log > denied.log # 生成policy模块 sudo audit2allow -i denied.log -M mycustommodule -
加载这个策略模块:
# 加载策略模块 sudo semodule -i mycustommodule.pp # 检查SELinux是否加载了这个policy sudo semodule -l | grep mycustommodule
只要配合 ausearch + audit2allow + semodule 工具链,就能高效、可控地扩展 SELinux 策略。

浙公网安备 33010602011771号