为什么Strace在Docker中不起作用

当我为《容器工作原理》杂志编辑容器功能页面时,我需要解释为什么Docker不起作用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_readvptrace通过的授权CAP_SYS_PTRACE看起来很合理。



证明可以strace在最新版本的Docker中工作



对于4.8及更高版本的内核,由于此提交,最终在Docker 19.03中允许系统调用ptrace除此以外,在我的笔记本电脑上,Docker仍为18.09.7版,并且显然缺少该提交。



就这样!



事实证明,处理这个问题很有趣,我认为这是一个很好的例子,说明了容器的平凡交互移动“填充”。



如果您喜欢这篇文章,您可能还会喜欢我的杂志How Containers Work,其中介绍了Linux内核的24页容器处理功能。在这里,您可以看到特权seccomp-bpf



All Articles