云计算百科
云计算领域专业知识百科平台

SELinux与AppArmor入门:强制访问控制配置与排错

「服务改了配置就是起不来,日志也没报错」「明明给了 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/Rocky Ubuntu/Debian
模型 标签(label/context) 路径(path)
配置 复杂,策略语言 简单,可读文本
学习曲线 陡 缓

先确认你的系统用哪个:

# 看 SELinux
getenforce
# 看 AppArmor
aa-status

三、SELinux 基础

三种模式

getenforce

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

临时切换:

sudo setenforce 0 # 改 Permissive
sudo setenforce 1 # 改 Enforcing

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

SELINUX=enforcing
SELINUXTYPE=targeted

⚠️ 排错黄金流程

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

# 1. 切换 Permissive
sudo setenforce 0

# 2. 重启服务,如果好了 → 是 SELinux 问题
sudo systemctl restart myservice

# 3. 切回 Enforcing,查看拒绝日志找真因
sudo setenforce 1
sudo ausearch -m avc -ts recent

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

查看拒绝日志

# 看 AVC 拒绝记录
sudo ausearch -m avc -ts recent
sudo journalctl -t audit | grep AVC

# 用 sealert 得到人类可读的建议
sudo dnf install setroubleshoot-server -y
sudo sealert -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 | grep nginx
# system_u:system_r:httpd_t:s0 nginx

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

修改文件上下文

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

# 查看应该用什么上下文
semanage fcontext -l | grep httpd_sys_content

# 把 /web 目录标记为 web 内容类型
sudo semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
# 应用规则到实际文件
sudo restorecon -Rv /web

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

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

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

# 列出所有布尔值
getsebool -a

# 查看 web 相关
getsebool -a | grep httpd

# 开启「允许 httpd 连接网络」
sudo setsebool -P httpd_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 | grep http_port_t

# 把 8080 加入 httpd 允许端口
sudo semanage port -a -t http_port_t -p tcp 8080

2. 文件访问被拒

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

# 查看 AVC 日志确认
sudo ausearch -m avc -ts recent | grep nginx

# 修正上下文(见上文)
sudo semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
sudo restorecon -Rv /web

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

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

sudo restorecon -Rv /path

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

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

# 只对特定服务设白名单(推荐)
semanage permissive -a httpd_t

# 完全禁用(不推荐,仅测试)
setenforce 0

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

六、AppArmor 基础

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

查看状态

sudo aa-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(排错)
sudo aa-complain /usr/sbin/nginx

# 改回 enforce
sudo aa-enforce /usr/sbin/nginx

# 禁用某 profile
sudo ln -s /etc/apparmor.d/usr.sbin.nginx /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx

排错

# 查看拒绝日志
sudo dmesg | grep -i apparmor
sudo journalctl | grep apparmor
sudo dmesg | grep "apparmor=\\"DENIED\\""

生成/调整 profile

# 让 AppArmor 学习一个程序的行为
sudo aa-genprof /usr/bin/myapp

# 从日志自动生成 profile
sudo aa-logprof

七、MAC 加固的正确心态

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

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

八、⚠️ 常见错误

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

    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 标签
    docker run –security-opt label=type:container_t ...

    # docker 指定 AppArmor profile
    docker run –security-opt apparmor=docker-default ...

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

    十、SELinux/AppArmor 速查表

    # ===== SELinux =====
    getenforce # 看模式
    setenforce 0 / 1 # 临时切 Permissive/Enforcing
    ls -Z file # 看文件上下文
    ps -eZ | grep proc # 看进程上下文
    ausearch -m avc -ts recent # 看拒绝日志
    semanage fcontext -a -t TYPE "路径" # 加上下文规则
    restorecon -Rv /path # 应用上下文
    semanage port -a -t http_port_t -p tcp 8080 # 放开端口
    getsebool -a | grep xxx # 看布尔值
    setsebool -P xxx on # 开布尔值
    semanage permissive -a domain_t # 单域宽容

    # ===== AppArmor =====
    aa-status # 看状态
    aa-complain /path # 切 complain
    aa-enforce /path # 切 enforce
    dmesg | grep -i apparmor # 看日志
    aa-genprof /usr/bin/app # 生成 profile

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


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

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » SELinux与AppArmor入门:强制访问控制配置与排错
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!