在编辑“容器如何工作”杂志的功能页面时,我发现自己在试图解释为什么strace在Docker容器中不能工作。
这里的问题是-如果在Docker容器中运行strace,则会发生以下情况:
$docker run-it ubuntu:18.04/bin/bash$#.。安装strace.。[电子邮件受保护]:/#strace lsstrace:ptrace(ptrace_TRACEME,.):不允许操作。
strace使用ptrace系统调用工作,所以如果不允许使用ptrace,它肯定不会工作!这个很容易修复-在我的机器上,这个可以修复它:
但我对修复它不感兴趣,我想知道为什么会发生这种情况。那么,为什么strace不工作,为什么--cap-add=SYS_ptrace可以修复它呢?
我一直认为原因是Docker容器进程by Default没有CAP_SYS_PTRACE功能。这与通过--cap-add=SYS_ptrace进行修复是一致的,对吗?
原因1:实验上,作为一个普通用户,我可以依赖于myuser运行的任何进程。但是,如果我检查当前进程是否具有CAP_SYS_PTRACE功能,则不会:
因此,CAP_SYS_PTRACE的目的是让您ptrace任何用户拥有的任意进程,就像root用户通常可以做到的那样。您不应该仅仅为了ptrace您的用户拥有的常规进程而需要它。
我用第三种方法测试了这一点-我用dockerrun--cap-add=SYS_ptrace-it ubuntu:18.04/bin/bash运行了一个Docker容器,删除了CAP_SYS_ptrace功能,即使我不再拥有该功能,我仍然可以加速进程。什么?为什么?
我的下一个假设(没有那么充分的依据)大致是这样的:“嗯,可能进程在不同的用户命名空间中,并且因为…,所以策略不起作用。原因是什么?“。这并不是真正连贯的,但当我调查它的时候就发生了这样的事情。
因为用户命名空间ID(4026531837)相同,所以容器中的根用户与主机上的根用户完全相同。因此,它绝对没有理由不能阻止它创建的进程!
这个假设没有多大意义,但我没有意识到Docker容器中的根用户与主机上的根用户相同,所以我认为这很有趣。
我还知道Docker使用seccomp-bpf来阻止容器进程运行大量系统调用。并且ptrace在被Docker的默认seccompprofile阻止的系统调用列表中!(实际上,允许的系统调用的列表是一个白名单,所以只是ptrace不在默认的白名单中。但结果却是一样的。)。
这很容易解释为什么strace在Docker容器中不能工作-如果ptrace系统调用被完全阻塞,那么当然您根本无法调用它,strace就会失败。
让我们验证这个假设-如果我们禁用所有seccomp规则,是否可以在Docker容器中进行westrace?
$docker run--security-opt seccomp=unconfined-it ubuntu:18.04/bin/bash$strace lexecve(";/bin/ls";,[";ls";],0x7ffc69a65580/*8 vars*/)=0.。它工作得很好..。
这和Seccomp规则没有任何关系!到底怎么回事?
当文档没有帮助时,唯一能做的就是查看源代码。
Go的好处是,因为依赖项通常在AGO存储库中提供,所以您只需对存储库执行grep,就可以找出执行某项操作的代码在哪里。所以我克隆了github.com/moby/moby和greppedfor一些内容,比如rgCAP_SYS_ptrace。
这就是我认为正在发生的事情。在tainerd的seccomp实现中,在contrib/seccomp/seccomp_default.go中,有一组代码确保如果一个进程具有可访问性,那么它也被授予访问权限(通过seccomp规则)来使用与该功能相关的系统调用。
案例";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.Linuxx。
在profile/seccomp/seccomp.goin Moby和默认的seccompprofile中,还有一些其他代码似乎做了一些非常相似的事情,所以有可能就是这样做的。
结果似乎是--cap-add并不完全像手册页中所说的那样,它更像是--cap-add-and-also-whitelist-some-extra-system-calls-if-required.。这让我感觉到了!如果您有CAP_SYS_PTRACE这样的功能,该功能理应允许您使用PROCESS_VM_READV系统调用,但该系统调用被seccomp配置文件阻止,这对您没有太大帮助!
因此,当您给容器CAP_SYS_PTRACE时,允许PROCESS_VM_READV和ptrace系统调用似乎是一个合理的选择。
在这次提交时(docker 19.03),Docker实际上允许ptrace系统调用比4.8更新的内核版本。
这是一件有趣的小事,我认为这是一个很好的例子,说明了容器是如何由许多移动的部件组成的,这些部件以不完全明显的方式协同工作。