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

开发 前端
在编辑“容器如何工作”爱好者杂志的能力页面时,我想试着解释一下为什么 strace 在 Docker 容器中无法工作。这里的问题是 —— 如果我在笔记本上的 Docker 容器中运行 strace,就会出现这种情况......

在编辑“容器如何工作”爱好者杂志的能力页面时,我想试着解释一下为什么 strace 在 Docker 容器中无法工作。

这里的问题是 —— 如果我在笔记本上的 Docker 容器中运行 strace,就会出现这种情况:

  1. $ docker run  -it ubuntu:18.04 /bin/bash 
  2. $ # ... install strace ... 
  3. [email protected]:/# strace ls 
  4. strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted 

strace 通过 ptrace 系统调用起作用,所以如果不允许使用 ptrace,它肯定是不能工作的! 这个问题很容易解决 —— 在我的机器上,是这样解决的:

  1. docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash 

但我对如何修复它不感兴趣,我想知道为什么会出现这种情况。为什么 strace 不能工作,为什么--cap-add=SYS_PTRACE 可以解决这个问题?

假设 1:容器进程缺少 CAP_SYS_PTRACE 能力。

我一直以为原因是 Docker 容器进程默认不具备 CAP_SYS_PTRACE 能力。这和它可以被 --cap-add=SYS_PTRACE 修复是一回事,是吧?

但这实际上是不合理的,原因有两个。

原因 1:在实验中,作为一个普通用户,我可以对我的用户运行的任何进程进行 strace。但如果我检查我的当前进程是否有 CAP_SYS_PTRACE 能力,则没有:

  1. $ getpcaps $$ 
  2. Capabilities for `11589': = 

原因 2:capabilities 的手册页对 CAP_SYS_PTRACE 的介绍是:

  1. CAP_SYS_PTRACE 
  2.        * Trace arbitrary processes using ptrace(2); 

所以,CAP_SYS_PTRACE 的作用是让你像 root 一样,可以对任何用户拥有的任意进程进行 ptrace。你不需要用它来对一个只是由你的用户拥有的普通进程进行 ptrace 。

我用第三种方法测试了一下(LCTT 译注:此处可能原文有误) —— 我用 docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash 运行了一个 Docker 容器,去掉了 CAP_SYS_PTRACE 能力,但我仍然可以跟踪进程,虽然我已经没有这个能力了。什么?为什么?!

假设 2:关于用户命名空间的事情?

我的下一个(没有那么充分的依据的)假设是“嗯,也许这个过程是在不同的用户命名空间里,而 strace 不能工作,因为某种原因而行不通?”这个问题其实并不相关,但这是我观察时想到的。

容器进程是否在不同的用户命名空间中?嗯,在容器中:

  1. root@e27f594da870:/# ls /proc/$$/ns/user -l 
  2. ... /proc/1/ns/user -> 'user:[4026531837]' 

在宿主机:

  1. bork@kiwi:~$ ls /proc/$$/ns/user -l 
  2. ... /proc/12177/ns/user -> 'user:[4026531837]' 

因为用户命名空间 ID(4026531837)是相同的,所以容器中的 root 用户和主机上的 root 用户是完全相同的用户。所以,绝对没有理由不能够对它创建的进程进行 strace!

这个假设并没有什么意义,但我(之前)没有意识到 Docker 容器中的 root 用户和主机上的 root 用户同一个,所以我觉得这很有意思。

假设 3:ptrace 系统的调用被 seccomp-bpf 规则阻止了

我也知道 Docker 使用 seccomp-bpf 来阻止容器进程运行许多系统调用。而 ptrace 在被 Docker 默认的 seccomp 配置文件阻止的系统调用列表中!(实际上,允许的系统调用列表是一个白名单,所以只是ptrace 不在默认的白名单中。但得出的结果是一样的。)

这很容易解释为什么 strace 在 Docker 容器中不能工作 —— 如果 ptrace 系统调用完全被屏蔽了,那么你当然不能调用它,strace 就会失败。

让我们来验证一下这个假设 —— 如果我们禁用了所有的 seccomp 规则,strace 能在 Docker 容器中工作吗?

  1. $ docker run --security-opt seccomp=unconfined -it ubuntu:18.04  /bin/bash 
  2. $ strace ls 
  3. execve("/bin/ls", ["ls"], 0x7ffc69a65580 /* 8 vars */) = 0 
  4. ... it works fine ... 

是的,很好用!很好。谜底解开了,除了…..

为什么 --cap-add=SYS_PTRACE 能解决问题?

我们还没有解释的是:为什么 --cap-add=SYS_PTRACE 可以解决这个问题?

docker run 的手册页是这样解释 --cap-add 参数的。

  1. --cap-add=[] 
  2.    Add Linux capabilities 

这跟 seccomp 规则没有任何关系! 怎么回事?

我们来看看 Docker 源码

当文档没有帮助的时候,唯一要做的就是去看源码。

Go 语言的好处是,因为依赖关系通常是在一个 Go 仓库里,你可以通过 grep 来找出做某件事的代码在哪里。所以我克隆了 github.com/moby/moby,然后对一些东西进行 grep,比如 rg CAP_SYS_PTRACE。

我认为是这样的。在 containerd 的 seccomp 实现中,在 contrib/seccomp/seccomp/seccomp_default.go 中,有一堆代码来确保如果一个进程有一个能力,那么它也会(通过 seccomp 规则)获得访问权限,以使用与该能力相关的系统调用。

  1. case "CAP_SYS_PTRACE"
  2.        s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ 
  3.            Names: []string{ 
  4.                "kcmp"
  5.                "process_vm_readv"
  6.                "process_vm_writev"
  7.                "ptrace"
  8.            }, 
  9.            Action: specs.ActAllow, 
  10.            Args:   []specs.LinuxSeccompArg{}, 
  11.        }) 

在 moby 中的 profile/seccomp/seccomp.go 和 默认的 seccomp 配置文件中,也有一些其他的代码似乎做了一些非常类似的事情,所以有可能就是这个代码在做这个事情。

所以我想我们有答案了!

Docker 中的 --cap-add 做的事情比它说的要多

结果似乎是,--cap-add 并不像手册页里说的那样,它更像是 --cap-add-and-also-whiteelist-some-extra-system-calls-if-required。这很有意义! 如果你具有一个像 --CAP_SYS_PTRACE 这样的能力,可以让你使用 process_vm_readv 系统调用,但是该系统调用被 seccomp 配置文件阻止了,那对你没有什么帮助!

所以当你给容器 CAP_SYS_PTRACE 能力时,允许使用 process_vm_readv 和 ptrace 系统调用似乎是一个合理的选择。

就这样!

这是个有趣的小事情,我认为这是一个很好的例子,说明了容器是由许多移动的部件组成的,它们以不完全显而易见的方式一起工作。

责任编辑:未丽燕 来源: Linux.cn
相关推荐

2019-05-07 14:03:53

鼠标中键Windows 10Windows

2021-05-07 07:50:44

Numactl内存代码

2018-07-20 10:50:43

WindowsWindows 10Shift

2019-07-11 10:30:58

USBWindows 10 快速修复

2021-04-06 11:46:18

比特币加密货币资产

2021-06-08 08:52:18

Makefile编译c 文件

2019-03-13 10:45:21

凭据Windows 10远程桌面

2024-06-04 16:42:45

2022-12-01 08:09:05

SQLOracleSPM

2018-11-20 13:52:54

2021-09-08 17:27:54

神经网络AI算法

2023-03-20 09:42:06

2022-12-27 14:43:15

模型GPT

2016-12-06 10:30:39

JavaScriptWriteWriteln

2024-09-11 08:38:02

JavaScriptC#语言编程

2021-04-16 20:47:42

Go 指令函数

2021-04-16 17:37:28

数据智能照明物联网

2020-05-06 22:20:48

Kubernetes边缘计算

2020-08-11 11:51:47

标准物联网IOT

2022-07-14 07:17:11

LXCDocker语言
点赞
收藏

51CTO技术栈公众号