
简介面向Linux运维及系统安全学习者这份操作系统安全专题PPT聚焦SELinux安全增强Linux机制的原理与核心概念帮助读者理清传统DAC自主访问控制与SELinux所采用的MAC强制访问控制的差异理解为何即便以root权限运行进程仍需受策略约束。内容涵盖targeted、minimum、mls三大预设政策的定位与适用场景并通过主体、目标、政策及安全性上下文的身份、角色、类型字段的对比梳理进程与文件访问判定的完整流程同时兼顾文件安全上下文存放于iNode、进程存放于内存等关键细节。特别适合初次接触SELinux、准备系统安全认证或需要为团队做安全内训的读者PPT结构清晰、要点明确可作为课堂笔记或课前预习材料直接使用。压缩包共1个文件为pptx演示文稿大小422KB。目前已有227人学习。 要说系统安全里最容易被误伤又最值得搞懂的东西SELinux 肯定排得上号。很多人第一次接触它是在装数据库或者跑 Web 服务时明明权限都放开了服务却起不来一查审计日志全是avc: denied。这时候要么一脸懵要么图省事直接setenforce 0关掉。但说实话关掉 SELinux 相当于把 Linux 系统最基础的一道隔离层拆了尤其是在部署多用户、多服务的生产环境时风险远比你想象的大。这篇东西我会围绕 SELinux 的核心机制、实际会遇到的问题、TE 文件怎么写、以及修改后没生效的排查逻辑展开。它不是从《操作系统安全》PPT 里抄概念更多是我在折腾各种服务、踩过各种 SELinux 坑之后的经验梳理适合正在学系统安全、或者工作中被 SELinux 卡住过的同学希望你读完能少走点弯路。1. SELinux 到底在解决什么问题从能访问到该不该访问SELinux 全称是 Security-Enhanced Linux最早是国家安全局NSA贡献给 Linux 内核的强制访问控制机制。它解决的核心问题不是密码够不够强而是就算你拿到了某个进程的权限你能碰什么、不能碰什么这在传统 Linux 权限模型里是无解的。1.1 传统 DAC 权限模型的天生短板传统 Linux 权限模型叫 DAC自主访问控制典型体现就是rwx权限位和 root 账户的存在。DAC 的核心逻辑是只要进程的 UID 满足文件属主或属组的要求就能按对应的权限去读写执行。这个模型凑合能用但它有几个很致命的问题。最典型的就是 root 权限过大。任何以 root 身份运行的进程比如 Apache、MySQL、Nginx 的 master 进程都拥有至高无上的权限一旦被远程代码执行漏洞打穿攻击者可以直接读写/etc/shadow、读取数据库目录、往系统目录里写东西。再比如用户把/home/user目录权限设成了777原本只是为了共享文件但结果任何本地用户都能翻看别人的私人文档。DAC 世界里的规则是身份决定权限身份一旦被获取规则就形同虚设。1.2 MAC 强制访问控制给每个进程发一张权限清单SELinux 引入的是 MAC强制访问控制模型跟 DAC 最大的区别在于MAC 不是看你是谁而是看你的安全上下文是什么以及这个上下文被允许做什么。每个进程、每个文件、每个网络端口都被打上一个标签叫安全上下文Security Context格式大概长这样system_u:system_r:httpd_t:s0从左到右分别是 SELinux 用户、角色、类型Type、灵敏度级别。最核心的是第三段httpd_t它是用来做访问判断的类型字段。SELinux 有一大堆策略规则每一条大致表达了某个类型能不能对另一个类型做某个操作。比如默认的 httpd 策略允许httpd_t类型的进程去读取/var/www/html下类型为httpd_sys_content_t的文件。如果哪天你把站点目录挪到了/data/www而/data/www下的文件没有打上httpd_sys_content_t标签那 SELinux 就会阻止 Apache 读取它们。这不是因为文件权限不够而是因为类型对不上。DAC 回答的是你有没有这个权限MAC 回答的是你到底该不该有权限这就是两者本质的差异。2. Selinux 三种运行模式与切换策略生产环境怎么选型搞懂 SELinux 的机制还不够实际工作中你会发现80% 的问题出在模式配置和策略选择上。2.1 enforcing、permissive、disabled 到底怎么落地SELinux 有三种模式enforcing强制模式违反策略的操作直接拦截并且写审计日志。permissive宽容模式违反策略的操作不拦截但也记日志方便你观察会影响什么。disabled彻底关闭连标签都不加载。很多人以为生产环境就该 enforcing这个没错但如果是从零开始部署新服务我建议先开 permissive 观察一周。原因是如果服务架构比较复杂比如 Nginx 代理 PHP-FPM 又转发到 Java 应用你还用了非标准端口、自定义数据目录那么一点点标签不对都可能引发连锁故障。permissive 模式下服务能正常跑同时审计日志一直在告诉我如果这是 enforcing哪些操作会被拦这是最稳妥的适应方式。等确认日志里没有异常拦截就是只有符合预期的访问记录再切到 enforcing。很多人一上来就 enforcing结果被各种avc denied折腾到怀疑人生索性setenforce 0这其实有点因噎废食。2.2 临时切换与永久配置别把测试动作留到重启后命令层面临时切换是setenforce enforcing setenforce permissive这个命令不用重启立刻生效但它不持久化机器一重启就会恢复成/etc/selinux/config里的配置。所以正规做法是这样vim /etc/selinux/config关键配置SELINUXpermissive SELINUXTYPEtargetedSELINUXTYPEtargeted是默认策略意思是只对受控的进程比如 httpd、named、sshd 这些 domain进行强制访问控制其余进程不受约束。这个策略比较实用既保住了核心服务的隔离又不至于让整个系统被策略文件淹没。如果你的安全等级要求很高可以考虑mls多级安全策略但它配置复杂一般场景用不上。注意setenforce 0后如果忘了改配置文件重启后 SELinux 会以 enforcing 模式启动这可能导致某些依赖 permissive 状态的服务启动失败。反过来也一样如果你只是想临时测试关闭效果一定要记着改回来。3. 从用户能不能访问网络理解 SELinux 的布尔值开关热词里有一条selinux 禁用户程序 网络访问示例这个说法其实不够准确但场景很真实。SELinux 里和用户程序网络访问相关的机制主要是布尔值开关boolean和网络端口标签。3.1 SELinux boolean 是什么一种策略开关布尔值可以理解为 SELinux 策略里的可调节选项。SELinux 开发者发现有些访问控制规则在不同部署场景下的需求是相反的有的环境允许某个服务执行特定操作另一些环境则不允许。于是他们把这类规则做成了开关管理员不需要重写策略文件只需切换布尔值就能改变行为。查看所有布尔值getsebool -a拿和网络最相关的例子来说如果你跑了一个用户自定义程序比如自己编译的科学计算程序它需要监听某个端口对外提供数据SELinux 默认策略是不允许无标签进程随意绑定非标准端口的。你可以通过这样一条命令查看和网络相关的布尔值semanage boolean -l | grep httpd输出里会有一堆和 httpd 相关的开关比如httpd_can_network_connect默认通常是 off。它的含义是是否允许 httpd 进程发起对外网络连接比如代理访问外网。如果你的 Web 应用需要后端调用外部 API不把这个布尔值打开你会发现代码里一切正常但 curl 请求就是超时日志里全是name_connectdenied。3.2 用户自定义程序怎么放行以 tomcat 的 8081 端口为例再举个更贴近用户程序网络访问的例子。我有个朋友写过一个小型 Java 服务自己监听 8081 端口部署在 CentOS 7 上。SELinux 默认只允许特定服务比如 httpd绑定 80、8080 等标准端口一个普通用户进程监听 8081 是会失败的。虽然报错信息只说Permission denied但它的真实原因是 SELinux 端口标签里没有 8081。解决思路不是关 SELinux而是给 8081 端口打个标签semanage port -a -t http_port_t -p tcp 8081这样http_port_t类型的进程只要策略允许就能绑定 8081 了。如果你的服务不是 httpd 类型就需要更精细的 TE 规则下一节详细讲。这其实引出了 SELinux 的一大特点它不是一个简单的白名单程序控制而是把进程、端口、文件系统、网络都纳入了类型管控体系。理解这个之后你遇到权限够但连不上的怪问题脑子里就能多一根弦。4. TE 文件书写详解从示例到策略包的完整流程热词selinux中te文件的详细书写规则介绍是 SELinux 高级内容里最有门槛的一部分。TE 文件是 Type Enforcement 的缩写也是 SELinux 策略的核心表达方式。它的目标很纯粹定义一个类型domain可以访问哪些类型、执行哪些操作。4.1 一个最小可用的 TE 文件长什么样先看一个例子。假如我有一个程序/opt/myapp/bin/server我给它定义了一个专用域myapp_t给它配套的文件类型是myapp_exec_t。TE 文件内容大致是policy_module(myapp, 1.0.0) # 定义类型 type myapp_t; type myapp_exec_t; domain_type(myapp_t); domain_entry_file(myapp_t, myapp_exec_t); # 允许该域使用终端输出 allow myapp_t self:process { fork getpid getpgid }; allow myapp_t self:tcp_socket { create bind listen accept read write }; # 允许该域读取自己的配置文件 type myapp_config_t; files_type(myapp_config_t); allow myapp_t myapp_config_t:file { read open getattr };这里面有几个新手容易看懵的点我拆开解释policy_module(myapp, 1.0.0)声明模块名和版本号编译时会用到。type myapp_t;定义一个类型。类型本身没意义意义在于后续规则围绕它建立关系网络。domain_type(myapp_t);这是接口调用表示把 myapp_t 声明成一个域domain即可以被进程运行、可以拥有权限的主体。domain_entry_file(myapp_t, myapp_exec_t);这个接口告诉 SELinuxmyapp_exec_t类型的可执行文件被执行时产生的进程域应该是myapp_t。这就是入口文件的概念。allow myapp_t self:tcp_socket {...}允许 myapp_t 这个域自己创建和操作 TCP socket。注意self表示两个 label 相同且 object 类是tcp_socket。allow是 TE 里最基础的规则语法完整格式是allow source_type target_type:object_class { permissions };翻译成人话就是source_type类型的进程可以对target_type类型的资源执行permissions列出的操作。比如allow httpd_t httpd_log_t:file { append write read open getattr };意思就是 httpd 进程可以追加、写、读、打开 httpd_log_t 类型的文件。4.2 接口、宏、属性模块化让规则不再是一团乱麻实际策略里如果全靠一条条allow写那 SELinux 策略文件早就爆炸了。所以它有一大套接口interface和宏macro体系。比如你经常会看到这样的写法apache_content_template(myapp)这其实是一整套模板接口调用它会自动帮你生成myapp_content_t、myapp_public_content_t等一堆类型定义和对应规则。这也是为什么很多人查semodule -l时看到那么多已加载模块但他们自己写 TE 时却都很简练的原因——大部分通用逻辑都被封装进接口里了。理解 TE 内容不需要你背下所有宏但要能读懂常用的三个套路files_type()把某个类型标记为文件类型适合普通数据文件。domain_type()domain_entry_file()用来声明进程域和入口文件。allow ... self:process ...用于声明进程可以对自己做的操作比如创建子进程、发信号。4.3 编译、安装与调试TE 文件怎么跑起来写完 TE 文件之后不是直接扔进/etc/selinux/targeted/policy就完事需要经过一个标准的工具链流程。我用的是selinux-policy-devel包提供的工具示例文件叫myapp.te完整链路如下# 1. 永远先创建配套的 .fc 文件否则文件标签会乱 cat myapp.fc EOF /opt/myapp/bin/server -- gen_context(system_u:object_r:myapp_exec_t,s0) /opt/myapp/conf(/.*)? gen_context(system_u:object_r:myapp_config_t,s0) EOF # 2. 编译 .te 为 .pp 模块包 make -f /usr/share/selinux/devel/Makefile myapp.pp # 3. 安装模块 semodule -i myapp.pp # 4. 恢复目标文件的标签 restorecon -Rv /opt/myappmake -f /usr/share/selinux/devel/Makefile myapp.pp这步会把.te文件连同同名的.fc文件一起编译。.fc文件的全称是 File Context它决定了哪些路径的文件应该被打上什么标签。没有.fc你就算定义了myapp_exec_t类型文件系统里并不会真的出现这种标签的文件策略也就无从生效。编译完成后用semodule -i myapp.pp安装再用restorecon恢复文件标签。restorecon的作用是让文件系统上的标签重新匹配.fc规则。很多时候你写完策略发现没效果就是因为忘了 restorecon。经验开发期排查标签问题用ls -Z就能看到每个文件的上下文标签和策略里定义的类型一对基本 80% 的问题比如 label 不对、没有匹配文件当场就清楚了。5. 修改了 SELinux 权限却没被编译进去完整排查链路热词里有一条非常典型selinux权限修改以后 没被编译进去是为什么。我敢说凡是手搓过 TE 文件的人都遇到这个问题而且这个问题的坑通常不在权限规则本身而在流程某个环节被跳过了。5.1 从想当然到找到原因一批诡异的无效策略有一次我帮一个运维同事看问题。他自己写了一个 TE 模块内容是允许某个服务域去写/opt/appdata下的文件。他把allow语句写在.te文件里了执行了make -f /usr/share/selinux/devel/Makefile xxx.pp没有任何报错但服务运行的时候依然写不进去。我过去一看发现他犯了三个典型的错他写的是allow myapp_t etc_t:file { write };etc_t本身是系统通用类型不是他服务专属的文件类型。而且你直接引用系统已有的类型很容易造成过度授权更关键的是如果你没有通过模板接口生成对应的文件类型那文件系统里根本不存在myapp_config_t这样的标签这条 allow 实际作用范围很小。他编译后没有检查生成的.pp文件内容其实make可能使用了缓存的策略导致新规则没有被打进包。他装完模块后对目标文件执行的是chcon而不是restorecon。chcon虽然能临时改标签但一旦系统执行restorecon、或者重新标记文件系统所有chcon带来的修改都会被还原策略自然表现出没生效。5.2 排查链路一步步定位是编译还是加载还是标签的问题我后来总结了一个简单的排查顺序基本覆盖了权限改了没效果的绝大多数原因。第一步确认模块真的编译进去了semodule -l | grep myapp如果你安装的是myapp.pp这里能看到名字说明安装成功。如果根本没有这一行那就是编译或安装环节有问题。检查Makefile是不是用的系统 devel 包路径前面写过了再看看当前目录下有没有生成.pp后缀文件。第二步确认规则真的被加载了sesearch --allow -s myapp_t -t myapp_config_t -c file这个命令能直接搜出当前内核策略里有没有这条 allow 规则。如果输出为空说明规则没被加载如果有输出但服务还是不行那问题大概率不在策略本身而在于文件标签不对或者域压根没生效。第三步确认文件标签匹配ls -Z /opt/appdata/test.txt semanage fcontext -l | grep appdata看标签类型是不是策略里定义的类型。如果标签和你 think 的不一致执行restorecon -Rv /opt/appdata第四步看审计日志里到底怎么拒绝的ausearch -m AVC -ts recent这一步能得到最终答案。日志会明确指出是哪个源域、对哪个目标类型、什么操作被拦截了。日志里写的tcontext标签和策略里的 target_type 比对一下基本一针见血。5.3 最容易忽略的坑编译缓存和-I参数还有一个细节make -f /usr/share/selinux/devel/Makefile myapp.pp在有些版本上不会主动重新编译依赖的头文件。如果你改动了接口文件或者宏定义建议先清一下中间产物make -f /usr/share/selinux/devel/Makefile clean make -f /usr/share/selinux/devel/Makefile myapp.pp否则非常容易遇到规则改了但行为没变的情况。6. 关于 SELinux 策略开发的实操心得与学习建议最后聊聊我折腾 SELinux 策略之后的一些真实感受可能对纠结于这些细节的人有点帮助。规律比记忆重要TE 文件表面看很吓人语法满天飞但核心就一句话定义类型写 allow 规则用接口做模块化。把常见模板apache、postgres、custom app各敲一遍你就能举一反三。别怕 permissive尝试新策略时可以把整个域或某个服务域临时放到 permissive 域里用semanage permissive -a myapp_t然后在 enforcing 策略下观察和调试。这比全机关 permissive 精细得多也更安全。多用sesearch和audit2whyaudit2why会把审计日志里 denied 的原因翻译得比较直白有时候一条命令就直接告诉你缺少哪个布尔值或者哪条 allow。手动查策略是基本功但工具能帮你少花 80% 的时间。别把 SELinux 和防火墙混为一谈防火墙管的是网络层端口放行SELinux 管的是进程对资源和端口的访问权。两者叠加出现问题时先用ss -tlnp确认端口监听状态再用ausearch查 AVC 日志能精准锁定是谁在拦你。如果后续你还想深入了解建议从audit2allow -a -M mymodule开始玩起。它可以根据审计日志自动生成一个近似可用的 TE 模块虽然生成的规则可能过于宽泛但用来对比它生成的规则和我手写的规则理解得反而特别快。本文还有配套的精品资源点击获取