☰
SELinux与AppArmor入门:强制访问控制配置与排错
2026/10/23 2:57:18 网站建设 项目流程

「服务改了配置就是起不来,日志也没报错」「明明给了 777 权限还是拒绝访问」——很多人被这类诡异问题坑过,最后发现是 SELinux 或 AppArmor 在「多管闲事」。它们是 Linux 的**强制访问控制(MAC)**机制,比传统的文件权限更底层、更严格。这一篇讲清 SELinux 和 AppArmor 的原理、配置和排错方法,让你不再把「关掉它」当成唯一解法。

一、什么是强制访问控制

传统的 Linux 权限(rwx、owner/group/other)叫DAC(自主访问控制):文件属主可以随意决定谁能访问。问题在于——如果某个进程以 root 运行,它就能访问任何文件,一旦被攻破,危害巨大。

MAC(强制访问控制)在 DAC 之上再加一层:由系统策略统一决定,进程能访问哪些资源,连 root 也要遵守。它就是「纵深防御」里的第 4 层(见第 79 篇)。

维度DAC(传统权限)MAC(SELinux/AppArmor)
决定者文件属主系统策略
root 能否绕过能不能
粒度用户/组/其他进程/路径/标签
目的基本的访问控制限制进程能力,防提权

二、两大流派

MAC 实现 ├─ SELinux(红帽系:RHEL/CentOS/Rocky/Fedora)—— 基于「标签」 └─ AppArmor(Debian 系:Ubuntu/Debian/SUSE)—— 基于「路径」
维度SELinuxAppArmor
发行版RHEL/CentOS/RockyUbuntu/Debian
模型标签(label/context)路径(path)
配置复杂,策略语言简单,可读文本
学习曲线陡缓

先确认你的系统用哪个:

# 看 SELinuxgetenforce# 看 AppArmoraa-status

三、SELinux 基础

三种模式

getenforce
模式行为
Enforcing强制执行策略,违规被拒绝
Permissive只记录不阻止(排错时用)
Disabled完全关闭

临时切换:

sudosetenforce0# 改 Permissivesudosetenforce1# 改 Enforcing

配置文件/etc/selinux/config(永久,需重启):

SELINUX=enforcing SELINUXTYPE=targeted

⚠️ 排错黄金流程

服务报「Permission denied」时,先别急着关 SELinux,改用 Permissive 模式测试:

# 1. 切换 Permissivesudosetenforce0# 2. 重启服务,如果好了 → 是 SELinux 问题sudosystemctl restart myservice# 3. 切回 Enforcing,查看拒绝日志找真因sudosetenforce1sudoausearch-mavc-tsrecent

记住:Permissive 是「排错工具」,不是「解决方案」。找到真因后要么调上下文、要么开布尔值、要么写策略,而不是永久禁用。

查看拒绝日志

# 看 AVC 拒绝记录sudoausearch-mavc-tsrecentsudojournalctl-taudit|grepAVC# 用 sealert 得到人类可读的建议sudodnfinstallsetroubleshoot-server-ysudosealert-a/var/log/audit/audit.log

AVC denied就是「SELinux 拒绝了某操作」,日志里会写清楚「哪个进程、对哪个资源、做了什么操作」以及修复建议。

四、SELinux 上下文与布尔值

SELinux 的核心是标签(context)。每个进程和文件都带一个安全上下文,格式:

user:role:type:level system_u:object_r:httpd_sys_content_t:s0

排障最关注的是type字段(第三个)。

查看上下文

# 文件上下文ls-Z/var/www/html/index.html# system_u:object_r:httpd_sys_content_t:s0 ...# 进程上下文ps-eZ|grepnginx# system_u:system_r:httpd_t:s0 nginx

规则:进程的 type(如httpd_t)必须能访问文件的 type(如httpd_sys_content_t),否则被拒。

修改文件上下文

场景:把网站文件放到/web/而不是默认的/var/www/html/,nginx 访问被拒。

# 查看应该用什么上下文semanage fcontext-l|grephttpd_sys_content# 把 /web 目录标记为 web 内容类型sudosemanage fcontext-a-thttpd_sys_content_t"/web(/.*)?"# 应用规则到实际文件sudorestorecon-Rv/web

关键点:semanage fcontext是持久规则,restorecon把规则应用到现有文件。只做前者不生效,只做后者重启可能丢,两者都要。

布尔值(boolean):开关式策略

SELinux 有一些「即开即用」的开关,对应常见需求:

# 列出所有布尔值getsebool-a# 查看 web 相关getsebool-a|grephttpd# 开启「允许 httpd 连接网络」sudosetsebool-Phttpd_can_network_connect on

-P表示持久化(重启后仍生效)。

典型场景:

需求布尔值
web 服务连数据库httpd_can_network_connect_db on
web 服务访问外网httpd_can_network_connect on
ftp 读写家目录ftp_home_dir on
允许 http 写目录httpd_unified on

先查布尔值,再改上下文,最后才考虑写策略——这是 SELinux 排错的优先级。

五、SELinux 常见排错

1. 服务起不来(端口问题)

场景:给 nginx 配了 8080 端口,起不来。

nginx: [emerg] bind() to 0.0.0.0:8080 failed (13: Permission denied)

原因:SELinux 只允许 httpd 绑定特定端口。

# 查看 httpd 允许的端口semanage port-l|grephttp_port_t# 把 8080 加入 httpd 允许端口sudosemanage port-a-thttp_port_t-ptcp8080

2. 文件访问被拒

场景:网站文件放自定义目录,403。

# 查看 AVC 日志确认sudoausearch-mavc-tsrecent|grepnginx# 修正上下文(见上文)sudosemanage fcontext-a-thttpd_sys_content_t"/web(/.*)?"sudorestorecon-Rv/web

3. 服务重启后上下文丢失

cp/mv/tar有时不带上下文,或文件系统不保留。用restorecon重建:

sudorestorecon-Rv/path

4. 实在要用「文档化」的关闭

如果确认某策略不适用于业务,且不想关全局,可以:

# 只对特定服务设白名单(推荐)semanage permissive-ahttpd_t# 完全禁用(不推荐,仅测试)setenforce0

permissive只让httpd_t域宽容,其他仍受保护,比全局关闭安全得多。

六、AppArmor 基础

Ubuntu/Debian 默认用 AppArmor,它基于路径而不是标签,profile 是纯文本,易懂。

查看状态

sudoaa-status
apparmor module is loaded. 42 profiles are loaded. 38 profiles are in enforce mode. /usr/sbin/nginx /usr/bin/man ... 4 profiles are in complain mode.

两种模式

模式行为
enforce强制执行,违规拒绝
complain只记录不阻止(排错用)

切换模式

# 改某 profile 为 complain(排错)sudoaa-complain /usr/sbin/nginx# 改回 enforcesudoaa-enforce /usr/sbin/nginx# 禁用某 profilesudoln-s/etc/apparmor.d/usr.sbin.nginx /etc/apparmor.d/disable/sudoapparmor_parser-R/etc/apparmor.d/usr.sbin.nginx

排错

# 查看拒绝日志sudodmesg|grep-iapparmorsudojournalctl|grepapparmorsudodmesg|grep"apparmor=\"DENIED\""

生成/调整 profile

# 让 AppArmor 学习一个程序的行为sudoaa-genprof /usr/bin/myapp# 从日志自动生成 profilesudoaa-logprof

七、MAC 加固的正确心态

做法评价
出问题就Disabled/disable❌ 等于拆掉一层防线
保持 Enforcing,用 Permissive 定位✅ 正确姿势
先调上下文/布尔值✅ 优先方案
为特定服务设 permissive✅ 折中
写自定义策略✅ 终极方案(复杂)

SELinux/AppArmor 的初衷是「即使服务被攻破,也限制它能造成的破坏」。关掉它,等于放弃了这层保护。正确的做法是理解它为什么拒绝,然后精准地放行必要操作。

八、⚠️ 常见错误

  1. 遇到问题直接setenforce 0。这是把安全防线拆了,应该先定位再精准放行。
  2. 改了semanage fcontext忘了restorecon。规则是规则,还得应用到实际文件。
  3. getenforce显示 Enforcing 就慌。Enforcing 是正常状态,说明策略在保护你。
  4. 复制文件后上下文丢失。cp/mv可能不保留上下文,用restorecon修复。
  5. chmod 777也解决不了。因为瓶颈在 MAC 而非 DAC,权限再宽也没用。
  6. 不知道系统用哪个 MAC。先getenforce/aa-status确认,别对着 AppArmor 找 SELinux 的错。
  7. 重装系统后策略没迁移。自定义的 SELinux 上下文、AppArmor profile 需要备份和迁移。
  8. 在生产上直接改配置验证。应在测试环境验证策略改动,避免影响生产。

九、原理探究:安全上下文与策略匹配

SELinux 的标签模型

SELinux 给每个主体(进程)和每个客体(文件、端口、socket)都打一个安全上下文。访问时,内核做一件事:

主体(进程上下文)的操作 → 查策略 → 客体(文件/端口上下文)

决策不看你「是谁(root 还是普通用户)」,而看「进程带什么标签、文件带什么标签、策略允不允许这对标签交互」。

举例:

nginx 进程 上下文:system_u:system_r:httpd_t 网站文件 上下文:system_u:object_r:httpd_sys_content_t 策略:允许 httpd_t 读 httpd_sys_content_t → 通过 ✓ /etc/shadow 上下文:system_u:object_r:shadow_t 策略:不允许 httpd_t 读 shadow_t → 拒绝 ✗

这就是为什么 nginx(哪怕以 root 运行)也读不了/etc/shadow——不是权限问题,是 SELinux 策略层面根本不允许。这就是「强制访问控制」的威力:连 root 都要服从策略。

为什么按「端口」也要管

不光是文件,端口也有标签。http_port_t是允许 httpd 绑定的端口集合。这样即使攻击者让 nginx 绑定到别的端口,也绑不上——限制的不是「谁能绑」,而是「这个进程能绑哪些」。

AppArmor 的路径模型

AppArmor 更直观,profile 里直接写路径规则:

/usr/sbin/nginx { /etc/nginx/** r, /var/www/** r, /var/log/nginx/** w, network tcp, }

意思是「nginx 只能读/etc/nginx、/var/www,写/var/log/nginx,允许 TCP」。基于路径的好处是可读性好,缺点是路径变动(如软链接、mount)容易绕过。

两者对比的本质

  • SELinux 基于标签:资源带标签,进程带标签,策略管「标签对标签」。强势但复杂。
  • AppArmor 基于路径:策略管「进程对路径」。简单但不够细。

无论哪种,核心思想都是一样的:给进程划定一个「只能碰这些」的笼子,即使它被攻破,也翻不出这个笼子。这就是「沙箱(sandbox)」和「最小权限」在网络访问控制层的落地。

和容器安全的关系

Docker/Kubernetes 的容器隔离,也会用到 SELinux/AppArmor:

# docker 指定 SELinux 标签dockerrun --security-optlabel=type:container_t...# docker 指定 AppArmor profiledockerrun --security-optapparmor=docker-default...

K8s 的 Pod Security Context 也能指定seLinuxOptions。理解 MAC,也是理解容器安全的基础。

十、SELinux/AppArmor 速查表

# ===== SELinux =====getenforce# 看模式setenforce0/1# 临时切 Permissive/Enforcingls-Zfile# 看文件上下文ps-eZ|grepproc# 看进程上下文ausearch-mavc-tsrecent# 看拒绝日志semanage fcontext-a-tTYPE"路径"# 加上下文规则restorecon-Rv/path# 应用上下文semanage port-a-thttp_port_t-ptcp8080# 放开端口getsebool-a|grepxxx# 看布尔值setsebool-Pxxx on# 开布尔值semanage permissive-adomain_t# 单域宽容# ===== AppArmor =====aa-status# 看状态aa-complain /path# 切 complainaa-enforce /path# 切 enforcedmesg|grep-iapparmor# 看日志aa-genprof /usr/bin/app# 生成 profile

一句话收尾:SELinux 用「标签」、AppArmor 用「路径」,都是给进程划定活动范围,连 root 也管。遇到「权限给了还被拒」,先想它俩;排错用 Permissive/complain 定位,再用semanage/布尔值精准放行——别把关闭当解法,那等于自断一层防线。


至此,第八篇「网络配置、远程与安全篇」10 篇全部完成。下一篇进入第九篇「存储、磁盘、备份与恢复篇」,从磁盘分区开始,把存储这块硬骨头啃下来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询