strace
。这是strace
在笔记本电脑上的Docker容器中运行时发生的情况:
$ docker run -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace
它可以通过系统调用进行操作ptrace
,因此未经许可ptrace
将无法使用!但这很容易解决,在我的笔记本电脑上,我做了以下所有事情:
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
但是我有兴趣不是要解决问题,而是要弄清楚为什么会出现这种情况。那么,为什么
strace
它不起作用,却--cap-add=SYS_PTRACE
纠正了所有问题?
假设1:容器进程没有自己的特权 CAP_SYS_PTRACE
由于问题一直通过解决
--cap-add=SYS_PTRACE
,因此在我看来,Docker容器进程从定义上看并没有自己的特权CAP_SYS_PTRACE
,但是由于两个原因,这里没有加总。
原因1:作为一个实验,以常规用户身份登录,我可以轻松启动
strace
任何进程,但是检查我当前的进程是否具有特权CAP_SYS_PTRACE
并没有发现任何东西:
$ getpcaps $$
Capabilities for `11589': =
原因2:在
man capabilities
特权CAP_SYS_PTRACE
如下:
CAP_SYS_PTRACE
* Trace arbitrary processes using ptrace(2);
关键
CAP_SYS_PTRACE
是要使我们类似于root,可以控制任何用户的任意进程。对于ptrace
您的用户,此特权不需要常规过程。
此外,我还进行了另一项检查:我通过启动了Docker容器
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
,然后撤销了特权CAP_SYS_PTRACE
- strace
即使没有特权也可以继续正常工作。为什么?!
假设2:用户名称空间中的大小写?
我的下一个假设(听起来没有那么根据)听起来像是“嗯,也许该过程位于不同的用户名称空间中,
strace
并且行不通……只是因为?” 它看起来像一组不太一致的陈述,但是我仍然尝试从这一方面看问题。
那么,该过程是否在其他用户定义的命名空间中?这是它在容器中的外观:
root@e27f594da870:/# ls /proc/$$/ns/user -l
... /proc/1/ns/user -> 'user:[4026531837]'
这就是它在主机上的外观:
bork@kiwi:~$ ls /proc/$$/ns/user -l
... /proc/12177/ns/user -> 'user:[4026531837]'
容器中的根与主机上的根是同一用户,因为它们在用户名称空间中具有公共标识符(4026531837),因此不应有任何干扰工作的
strace
原因。如您所见,这个假设原来是这样,但是后来我仍然没有意识到容器和主机上的用户是匹配的,这种方法对我来说似乎很有趣。
假设3:系统调用被ptrace
规则阻止seccomp-bpf
我已经知道Docker中有一条规则来限制由Docker中的容器处理器运行的大量系统调用
seccomp-bpf
,事实证明,在其定义中有并且在其调用列表中ptrace
!(实际上,调用列表是一个例外列表,ptrace
只是没有进入异常列表,但是结果没有改变。)
现在很清楚为什么容器不能在Docker容器中工作
strace
,因为很明显,完全阻塞的ptrace
调用将不起作用。
让我们测试一下这个假设,看看如果
strace
禁用所有seccomp规则,是否可以在Docker中使用该容器:
$ docker run --security-opt seccomp=unconfined -it ubuntu:18.04 /bin/bash
$ strace ls
execve("/bin/ls", ["ls"], 0x7ffc69a65580 /* 8 vars */) = 0
... it works fine ...
精细!一切正常,秘密揭晓!那只是...
为什么--cap-add=SYS_PTRACE
可以解决问题?
我们仍然没有解释为什么它
--cap-add=SYS_PTRACE
可以解决新出现的挑战问题。主页docker run
解释该参数的操作,如下所示--cap-add
:
--cap-add=[]
Add Linux capabilities
所有这些都与seccomp的规则无关!怎么了?
让我们看一下Docker的源代码。
如果文档还没有帮助,那么我们剩下的就是投入源代码。
关于Go的一件好事是,通过在Go存储库中提供依赖项,您
grep
可以遍历整个存储库并找到您感兴趣的代码。因此我github.com/moby/moby
克隆并搜寻了他的表情rg CAP_SYS_PTRACE
。
在我看来,这就是这里发生的情况:在容器中seccomp的实现中,在contrib / seccomp / seccomp_default.go部分中,有很多代码通过seccomp规则检查具有特权的进程是否有权根据此特权使用系统调用。
case "CAP_SYS_PTRACE":
s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{
Names: []string{
"kcmp",
"process_vm_readv",
"process_vm_writev",
"ptrace",
},
Action: specs.ActAllow,
Args: []specs.LinuxSeccompArg{},
})
那里也有代码,在moby中,对于profile / seccomp / seccomp.go而言,对于seccomp概要,根据定义,它们执行类似的操作,因此我们很可能找到了答案!
Docker --cap-add
可以做的不仅仅是说
结果,它似乎
--cap-add
并没有完全完成主页上写的内容,而是看起来像--cap-add-and-also-whitelist-some-extra-system-calls-if-required
。看起来确实是这样:如果您具有spirit的特权,该特权CAP_SYS_PTRACE
使您可以使用系统调用process_vm_readv
,但是该调用被阻止的Seccomp配置文件对您没有太大帮助,因此使用系统调用process_vm_readv
和ptrace
通过的授权CAP_SYS_PTRACE
看起来很合理。
证明可以strace
在最新版本的Docker中工作
对于4.8及更高版本的内核,由于此提交,最终在Docker 19.03中允许系统调用
ptrace
。除此以外,在我的笔记本电脑上,Docker仍为18.09.7版,并且显然缺少该提交。
就这样!
事实证明,处理这个问题很有趣,我认为这是一个很好的例子,说明了容器的平凡交互移动“填充”。
如果您喜欢这篇文章,您可能还会喜欢我的杂志How Containers Work,其中介绍了Linux内核的24页容器处理功能。在这里,您可以看到特权和seccomp-bpf。