从搜索和源码探索 docker 如何处理 apparmor
由来
suse12sp5 上古系统记得之前接手还是 17.05 rpm 安装的,后续找了个 19.03.15 的 rpm 安装上的,之所以使用 rpm 是因为 docker static bin 安装起不来(忘记啥报错了来着),然后现在尝试了 24.0.5 的能起来,但是容器无法启动,发现和 apparmor 有关。apparmor、seccomp和 SELinux 都是用于增强 Linux 系统安全性的安全性模块。它们提供了对进程、应用程序和系统资源的额外访问控制层。
过程
报错信息
系统信息:
1 | suse12sp5:~ # cat /etc/os-release |
24.0.5 的 docker static bin 安装好后导入镜像,无法起容器,报错:
1 | Error response from daemon: AppArmor enabled on system but the docker-default profile could not be loaded: running `/sbin/apparmor_parser apparmor_parser -Kr /data/kube/docker/tmp/docker-default1881723382` failed with output: AppArmor parser error for /data/kube/docker/tmp/docker-default1881723382 in /data/kube/docker/tmp/docker-default1881723382 at line 16: syntax error, unexpected TOK_OPENPAREN, expecting TOK_MODE |
代码里搜报错 AppArmor enabled on system but the
搜到 ensureDefaultAppArmorProfile
方法里:
1 | // https://github.com/moby/moby/blob/v24.0.5/daemon/apparmor_default.go#L27 |
apparmor.HostSupports
是 os.Getenv("container") == ""
且 /sys/module/apparmor/parameters/enabled
里是 Y
且存在命令 /sbin/apparmor_parser
则满足aaprofile.IsLoaded
是等同于 grep 'docker-default ' /sys/kernel/security/apparmor/profiles
判断 apparmor 策略 docker-default
加载没,没加载则加载
docker 的 apparmor 生成和加载
然后就是 profiles/apparmor/apparmor.go 里的 generateDefault 和 InstallDefault
:
- docker daemon 进程读取
/proc/self/attr/current
为空则策略名为unconfined
- 创建
os.CreateTemp
临时文件,并用模板profiles/apparmor/template.go
生成到临时文件里 - 使用
/sbin/apparmor_parser -Kr <tmp_file>
加载策略 - 无论加载成功还是失败,最后会把这个临时策略文件删掉
解决过程
上面报错就是策略文件内容有问题,但是 docker daemon 会把策略文件删掉,最开始 clone 源码修改不删掉临时文件后 make 替换,但是后面发现了个简单粗暴套路:
1 | cp /sbin/apparmor_parser /sbin/apparmor_parser_REAL |
然后 docker start 后 /root/apparmor_parser_profile_log
就生成内容了:
1 |
|
执行 /sbin/apparmor_parser -Kr /root/apparmor_parser_profile_log
和上面一样的报错,尝试后发现去掉 signal
和 ptrace
行才可以,然后在 rpm 19.03 环境上面这样 hack 后拿到的策略文件也是没有 signal
和 ptrace
行的,删掉这俩类型后加载就只报警告:
1 | -KR 卸载 |
容器也能启动了,固化就把这个文件存放到 /etc/apparmor.d/docker-default
,这样提前加载后,docker daemon 判断加载了就不加载它的内置模板了。后续加了下下面几行:
1 | @{PROC}/sys/kernel/ r, |
apparmor 和一些其他信息
规则文件解释
#include
后面不是就绝对路径则是 /etc/apparmor.d/
内的,提供一些变量命名和目录权限profile <name|bin-path> flags=(xx,xxx,xxxx) {
名字(供外部 docker/k8s 使用该策略)或者二进制路径限制,flags 后面可以逗号或者空格分隔
- attach_disconnected
允许连接端口的进程
- mediate_deleted
允许进程访问已被删除的文件
- complain
默认是 enforce
模式,操作会被拒绝,complain 则会记录日志,kill 则 deny 的时候 kill 掉,查看进程模式可以通过 cat /proc/<pid>/attr/current
可以查看进程的策略名和模式,例如 dodcker-default enforce
还有其他属性就不介绍了,例如文件,网络,mount 啥的,自行搜下文档
开启打印后,可以系统日志里看到信息
1 | echo 0 > /proc/sys/kernel/printk_ratelimit |
docker 示例
搜相关 issue 的时候发现,之前的模板里有判断 apparmor_parser 的版本,后面又去掉了,是因为有人提交了
1 | $ ls -l contrib/apparmor/ |
看了下代码,可以生成参考的策略内容:
1 | $ go run contrib/apparmor/*.go 111 |
suse 的一些信息
一些故障
UOS 系统上容器无法启动:
1 | root@user-PC:~# cat /etc/os-release |
启动报错:
1 | $ docker run -d --name t1 2e72 |
搜了几个相关 issue,docker 和 runc 版本都足够新,机器上也有安装 /sbin/apparmor_parser
,之前有在 4.1x 内核上发现 apparmor 功能不完整, 关闭 apparmor 尝试下就好了:
1 | # GRUB_CMDLINE_LINUX 或者 GRUB_CMDLINE_LINUX_DEFAULT |
更新 grub
1 | systemctl disable --now apparmor |
重启后的:
1 | $ cat /proc/cmdline |