AppArmor 配置文件如何编写?

来源:安卓APP网作者:木下头衔:网络博主
导读:本期聚焦于木下创作的《AppArmor 配置文件如何编写?》,敬请观看详情。为什么用 systemd 启动的 Nginx 明明有读取 /data 的权限,却总是返回 Permission denied?如果你在 Ubuntu 或 SUSE 上排查过类似问题,最终很可能定位到 AppArmor 的配置文件。AppArmor 通过基于路径的强制访问控制限制进程能力,配置文件的语法和规则质量直接决定服务能否正常运行。本文从 profile 结构、权限字母、路径匹配、变量与抽象层、调试加载几个方面拆解如何编写一份可用的 AppArmor 配置。内容会介绍 include 指令、tunables 变量、deny 规则、owner 限制以及 apparmor_parser 与 aa-logprof 的实际用法,帮助读者把进程权限收敛到最小可用范围,同时避免因规则书写错误导致服务启动失败或安全策略失效。

AppArmor 是 Linux 内核提供的强制访问控制框架,它不关心进程属于哪个用户,而是根据可执行文件路径为进程划出一块可以访问的边界。Ubuntu、Debian、SUSE 等发行版默认启用它,很多服务启动后看似权限正确却仍然报 Permission denied,通常就是 AppArmor 配置没有放行对应路径。配置文件默认存放在 /etc/apparmor.d/ 目录,文件名一般将二进制的绝对路径中的 / 替换为 .,例如 /usr/sbin/nginx 对应 usr.sbin.nginx。要编写一份可靠配置,需要理解 profile 结构、权限字母、路径匹配、变量与抽象层,以及调试加载流程。

AppArmor 配置文件如何编写?

一、AppArmor 配置文件的基本结构

每个 AppArmor 配置文件的核心是一个 profile 块,块头写可执行文件的绝对路径,块内写入具体规则。文件头部通常会通过 include 指令引入全局可调参数,例如 #include <tunables/global>,这样可以在统一位置定义变量。下面是一个最小化的 Nginx 配置示例,它允许绑定网络端口、写日志和 PID 文件,同时显式拒绝读取 /etc/shadow。

#include <tunables/global>

/usr/sbin/nginx {
  #include <abstractions/base>
  capability net_bind_service,
  /var/log/nginx/*.log w,
  /run/nginx.pid w,
  deny /etc/shadow r,
}

profile 名称后面可以追加一对括号写入标志,例如 flags=(complain)。在 complain 模式中,AppArmor 只记录越权行为而不真正阻止,适合新配置上线前的观察期;enforce 模式是默认策略,会立即阻断未授权访问。多个标志用逗号分隔,例如 flags=(complain,attach_disconnected)。

如果同一个可执行文件需要不同启动参数或不同运行环境,可以在一个文件中定义多个 profile,并用 profile 关键字加名称区分。每个 profile 独立加载、独立切换 enforce 或 complain 模式,便于灰度测试和回滚。

二、权限规则与路径匹配

权限规则是 profile 的主体内容,每条规则通常由路径和权限字母组成,并以逗号结束。r 表示读取,w 表示写入,m 表示允许将文件映射到内存,k 表示允许锁定文件,l 表示允许创建硬链接,x 表示执行。权限字母可以组合出现,例如 rw 表示读写。

执行权限需要单独说明,因为 AppArmor 对执行进行了细分。ix 表示继承当前 profile,适合执行同一应用内部的脚本;px 表示切换到目标文件的独立 profile;Px 在切换前清理环境变量;ux 表示以不受 AppArmor 限制的方式执行;Ux 在 ux 基础上同样清理环境。如果希望执行一个不被 AppArmor 管理的程序,使用 ux 或 Ux,但安全风险较高。

/opt/app/bin/worker px,
/usr/bin/python3 ix,
/opt/app/tools/backup.sh Ux,

路径匹配支持多种通配符。* 匹配当前目录下的任意字符但不会跨目录,** 匹配任意层目录,? 匹配单个字符,[abc] 匹配字符类,{a,b,c} 表示花括号展开。例如 /var/www/** r 可以覆盖 /var/www 下所有子目录和文件;/home/*/.ssh/authorized_keys r 则只匹配一级用户目录下的 SSH 公钥文件。

除了允许规则,deny 用于显式拒绝。当 allow 规则较宽时,deny 可以精确屏蔽敏感文件。例如允许 /etc/** r 但拒绝 /etc/shadow r。AppArmor 在处理规则时更具体的路径优先,但明确 deny 会覆盖宽泛允许,因此把 deny 规则放在文件开头或靠近相关 allow 规则位置都是常见做法。owner 关键字可以进一步限制只有进程属主与目标文件属主一致时才匹配,适合多用户环境中的家目录访问。

deny /etc/shadow r,
owner /home/*/tmp/** rw,
/var/www/** r,

三、变量、别名与抽象层

当多个规则反复使用同一路径前缀时,可以用变量减少重复。变量定义使用 @{变量名}=值 的形式,引用时写成 @{变量名}。变量可以定义在当前 profile 文件内,也可以放在 /etc/apparmor.d/tunables/global 中统一维护。典型场景是应用根目录或用户家目录。

@{APP_HOME}=/srv/myapp
@{APP_HOME}/** rw,
@{APP_HOME}/tmp/** rw,

include 指令用于引入可复用的规则片段。系统自带的抽象文件位于 /etc/apparmor.d/abstractions/ 目录,例如 base 抽象中包含读取 /etc/localtime、/proc/meminfo 等常见权限,nameservice 抽象允许 DNS 解析所需的库和套接字访问。自定义抽象也可以放在同一目录,通过 #include <abstractions/自定义名称> 引用。

模块化组织的好处是降低维护成本。一个复杂服务可能会依赖数据库、缓存、日志目录,把这些权限抽象成独立文件后,多个 profile 可以复用同一组规则。例如创建 /etc/apparmor.d/abstractions/myapp-base,写入日志和 PID 文件权限,然后在主 profile 中直接 include。

#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/myapp-base>

四、编写与调试流程

写完配置文件后,需要先用 apparmor_parser 检查语法。常用的 -Q 选项表示静默模式,语法正确时不输出任何内容;-r 用于替换已加载的 profile;-R 用于移除。首次加载一个不存在的 profile 时,使用 -a 添加,但在 Debian/Ubuntu 中直接 -r 也能完成替换并加载。

sudo apparmor_parser -Q /etc/apparmor.d/usr.sbin.nginx
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx

如果语法错误,apparmor_parser 会输出带行号的提示,常见错误包括规则末尾缺少逗号、变量未定义、通配符位置非法等。修复后重新加载,不需要重启系统。查看当前加载状态可以使用 aa-status,它会列出处于 enforce 和 complain 模式的 profile 数量。

规则不够用时,最好的方法不是手工猜测,而是通过 aa-logprof 读取系统日志中被 AppArmor 拒绝的操作,并交互式生成规则。日志通常位于 /var/log/syslog 或 /var/log/audit/audit.log,关键字段包括 operation、profile、name 和 requested_mask。例如下面的日志表示 /usr/sbin/nginx 想要打开 /data/www/index.html 但被拒绝。

apparmor="DENIED" operation="open" profile="/usr/sbin/nginx" name="/data/www/index.html" pid=1234 comm="nginx" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

运行 sudo aa-logprof -f /path/to/logfile 后,工具会给出建议规则,确认后自动更新对应 profile 文件。需要注意的是,仅在 complain 模式下产生的日志会更完整,因此新配置可以先切换到 complain,运行一段时间业务后再切换回 enforce。

五、常见错误与最佳实践

第一个常见错误是规则漏掉逗号。AppArmor 解析器对行尾是否空格并不敏感,但逗号是规则结束符,缺失会导致下一行被误解或解析失败。开发时可以用编辑器插件辅助定位,保存后立即执行 apparmor_parser -Q 验证。

第二个常见错误是路径通配符的层级理解错误。* 不跨目录,** 跨目录,很多人希望用 * 匹配所有子目录下的文件,结果只匹配到了当前目录。例如 /var/log/* r 只能读取 /var/log 下的直接文件,不会放行 /var/log/nginx/error.log,应写成 /var/log/** r。

第三个常见错误是把服务临时迁就规则,使用 ux 或 Ux 执行外部程序,甚至直接为 profile 设置为 unconfined。这样虽然能解决 Permission denied,却完全失去了强制访问控制的意义。正确做法是持续使用 aa-logprof 收集需要的最小权限,必要时为被调用的外部程序创建独立 profile。

最佳实践上,建议从最小权限开始,初始只放行必要目录和 capability,然后通过 complain 模式观察日志补齐权限。对于敏感文件如 /etc/shadow、/root/.ssh 等,即使有较宽允许也要显式 deny。使用 include 抽象减少重复,并定期用 aa-status 检查是否有 profile 意外处于 complain 模式。生产环境变更后,应把配置纳入版本管理,便于审计和回滚。

AppArmor配置文件Linux安全修改时间:2026-10-02 16:35:06

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1002/64743.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。