当前位置: 首页 > 科技观察

为什么strace在Docker中不起作用?

时间:2023-03-17 21:53:45 科技观察

在编辑“容器如何工作”粉丝杂志的功能页面时,我想尝试解释为什么strace在Docker容器中不起作用。这就是问题所在——如果我在笔记本电脑上的Docker容器中运行strace,就会发生这种情况:$dockerrun-itubuntu:18.04/bin/bash$#...installstrace...[emailprotected]:/#stracelsstrace:ptrace(PTRACE_TRACEME,...):Operationnotpermittedstrace是通过ptrace系统调用来工作的,所以如果不允许ptrace,那一定不行!这个问题很容易解决-在我的机器上,它是这样解决的:dockerrun--cap-add=SYS_PTRACE-itubuntu:18.04/bin/bash但是我对如何修复它不感兴趣,我想知道为什么有时候是这样的。为什么strace不行,为什么--cap-add=SYS_PTRACE可以解决这个问题?假设1:容器进程缺少CAP_SYS_PTRACE能力。我一直认为原因是Docker容器进程默认没有CAP_SYS_PTRACE能力。这与--cap-add=SYS_PTRACE可以修复的问题是一样的,对吧?但这实际上是不合理的,原因有二。原因1:在实验中,作为普通用户,我可以跟踪用户运行的任何进程。但是如果我检查我当前进程是否有CAP_SYS_PTRACE能力,没有:$getpcaps$$Capabilitiesfor`11589':=原因2:capabilitiesmanpage中CAP_SYS_PTRACE的介绍是:CAP_SYS_PTRACE*Tracearbitraryprocessesusingptrace(2);所以,CAP_SYS_PTRACE的功能是让你像root一样,ptrace任何用户拥有的任何进程。您不需要使用它来跟踪您的用户所拥有的正常进程。我用第三种方法测试了(LCTT译注:这里的原文可能有误)——我用dockerrun--cap-add=SYS_PTRACE-itubuntu:18.04/bin/bash运行一个Docker容器,去掉CAP_SYS_PTRACE能力,但我仍然可以跟踪流程,尽管我不再具备这种能力。什么?为什么?!假设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]'因为用户命名的空间ID(4026531837)是一样的,因此容器中的root用户和主机上的root用户是完全相同的用户。所以,绝对没有理由不能跟踪它创建的进程!这个假设实际上没有意义,但我(之前)没有意识到Docker容器中的root用户与主机上的root用户相同,所以我认为这很有趣。假设三:ptrace系统调用被seccomp-bpf规则阻塞我也知道Docker使用seccomp-bpf来防止容器进程运行很多系统调用。并且ptrace在被Docker的默认seccomp配置文件阻止的系统调用列表中!(实际上,允许的系统调用列表是一个白名单,所以只是ptrace不在默认白名单中。但是结果是一样的。)这很容易解释为什么strace在Docker容器中不起作用——如果ptrace系统call被完全屏蔽了,那么你当然不能调用它,strace就会失败。让我们来检验这个假设——如果我们禁用所有seccomp规则,strace是否会在Docker容器中工作?$dockerrun--security-optseccomp=unconfined-itubuntu:18.04/bin/bash$stracelsexecve("/bin/ls",["ls"],0x7ffc69a65580/*8vars*/)=0...itworksfine...是的,效果很好!非常好。谜底解开了,除了.....为什么--cap-add=SYS_PTRACE解决了问题?我们还没有解释的是:为什么--cap-add=SYS_PTRACE可以解决问题?dockerrun的手册页是这样解释的——cap-add参数。--cap-add=[]AddLinuxcapabilities这与seccomp规则无关!这是怎么回事?让我们看看Docker源代码当文档没有帮助时,唯一要做的就是查看源代码。Go语言的好处在于,因为依赖项通常位于Go存储库中,所以您可以grep找出执行某项操作的代码在哪里。所以我克隆了github.com/moby/moby,然后用grepped寻找rgCAP_SYS_PTRACE之类的东西。我想是这样。在containerd的seccomp实现中,在contrib/seccomp/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-如果需要,一些额外的系统调用。这就说得通了!如果您有像--CAP_SYS_PTRACE这样的功能,可以让您使用process_vm_readv系统调用,但该系统调用被seccomp配置文件阻止,那对您没有帮助!因此,当您为容器提供CAP_SYS_PTRACE能力时,允许使用process_vm_readv和ptrace系统调用似乎是一个合理的选择。就是这样!这是一件有趣的小事,我认为这是一个很好的例子,说明容器是如何由许多活动部件组成的,这些部件以不那么明显的方式协同工作。