strace
. Aqui está o que aconteceu ao executar strace
em um contêiner Docker no meu laptop:
$ docker run -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace
funciona através de uma chamada do sistema ptrace
, para ptrace
que não funcione sem permissão ! Mas isso é fácil de consertar, e no meu laptop eu fiz tudo assim:
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
Mas foi interessante para mim não resolver o problema, mas descobrir por que essa situação geralmente surge. Então, por que
strace
não funciona, mas --cap-add=SYS_PTRACE
corrige tudo?
Hipótese 1: Os processos de contêiner não têm seus próprios privilégios CAP_SYS_PTRACE
Como o problema é resolvido de maneira consistente
--cap-add=SYS_PTRACE
, sempre me pareceu que os processos de contêiner do Docker, por definição, não têm seus próprios privilégios CAP_SYS_PTRACE
, mas por dois motivos, algo não se soma aqui.
Razão 1: como experiência, eu, sendo logado como usuário comum, poderia iniciar
strace
qualquer processo com facilidade , mas CAP_SYS_PTRACE
não encontrei nada no privilégio do meu processo atual :
$ getpcaps $$
Capabilities for `11589': =
Razão 2: no
man capabilities
privilégio, CAP_SYS_PTRACE
leia o seguinte:
CAP_SYS_PTRACE
* Trace arbitrary processes using ptrace(2);
O ponto principal
CAP_SYS_PTRACE
é que, por analogia com o root, podemos assumir o controle do processo arbitrário de qualquer usuário. Para ptrace
seu usuário, esse privilégio não precisa de um processo convencional.
Além disso, realizei mais uma verificação: iniciei o contêiner do Docker
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
e revoguei o privilégio CAP_SYS_PTRACE
- e strace
continuei a funcionar corretamente mesmo sem o privilégio. Por quê?!
Hipótese 2: Caso no espaço para nome do usuário?
Minha próxima hipótese (e muito menos fundamentada) soou como "hmm, talvez o processo esteja em um espaço para nome de usuário diferente e
strace
não funcione ... só porque?" Parece um conjunto de declarações não muito coerentes, mas ainda tentei analisar o problema desse lado.
Então, o processo está em um namespace de usuário diferente? É assim que fica no contêiner:
root@e27f594da870:/# ls /proc/$$/ns/user -l
... /proc/1/ns/user -> 'user:[4026531837]'
E é assim que fica no host:
bork@kiwi:~$ ls /proc/$$/ns/user -l
... /proc/12177/ns/user -> 'user:[4026531837]'
raiz no contêiner é o mesmo usuário que raiz no host, pois eles têm um identificador de espaço de nome de usuário comum (4026531837), portanto, não deve haver
strace
motivos de interferência nesse lado . Como você pode ver, a hipótese acabou sendo mais ou menos, mas ainda não percebi que os usuários no contêiner e no host são os mesmos, e essa abordagem me pareceu interessante.
Hipótese 3: a chamada do sistema é ptrace
bloqueada por uma regraseccomp-bpf
Eu já sabia que para limitar o lançamento de um grande número de chamadas de sistema por processadores de contêiner no Docker, existe uma regra
seccomp-bpf
, e constatou-se que em sua lista de chamadas bloqueadas por definição, também existem ptrace
! (De fato, a lista de chamadas é uma lista de exceção e ptrace
simplesmente não entra nela, mas o resultado não muda.)
Agora está claro por que o contêiner não funciona em um contêiner do Docker
strace
, porque é óbvio que uma ptrace
chamada completamente bloqueada não funcionará.
Vamos testar esta hipótese e ver se podemos usar
strace
o contêiner do Docker se desativarmos todas as regras do seccomp:
$ 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 ...
Bem! Tudo funciona e o segredo é revelado! Isso é só ...
Por --cap-add=SYS_PTRACE
que resolve o problema?
Ainda não explicamos por que ele
--cap-add=SYS_PTRACE
resolve o problema do desafio emergente. A página principal docker run
explica como o argumento funciona da seguinte maneira --cap-add
:
--cap-add=[]
Add Linux capabilities
Nada disso tem nada a ver com regras seccomp! Qual é o problema?
Vamos dar uma olhada no código fonte do Docker.
Se a documentação ainda não ajudar, tudo o que resta para nós é mergulhar na fonte.
O Go possui um recurso interessante: graças à venda de dependência no repositório Go, você
grep
pode percorrer todo o repositório e encontrar o código no qual está interessado. Então eu o github.com/moby/moby
clonei e o vasculhei por expressões do tipo rg CAP_SYS_PTRACE
.
Na minha opinião, é o que acontece aqui: na implementação do seccomp no contêiner, na seção contrib / seccomp / seccomp_default.go, há muito código que, através da regra seccomp, verifica se um processo com privilégios tem permissão para usar chamadas de sistema de acordo com esse privilégio.
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{},
})
Ainda existe um código no moby que para profiles / seccomp / seccomp.go e seccomp, por definição, executa operações semelhantes, portanto, provavelmente encontramos nossa resposta!
O Docker --cap-add
pode fazer mais do que o dito
No final, parece que
--cap-add
não faz exatamente o que diz na página principal e deve parecer --cap-add-and-also-whitelist-some-extra-system-calls-if-required
. E isso parece ser verdade: se você tem um privilégio como CAP_SYS_PTRACE
esse que permite que você use uma chamada de sistema process_vm_readv
, mas essa chamada é bloqueado pelo perfil seccomp, que não ajudar muito, permitindo assim chamadas do sistema através de process_vm_readv
e ptrace
através de CAP_SYS_PTRACE
olhares razoáveis.
Acontece que strace
funciona nas versões mais recentes do Docker
Nas versões 4.8 e superior do kernel, graças a essa confirmação, as chamadas do sistema são finalmente permitidas no Docker 19.03
ptrace
. Exceto que, no meu laptop, o Docker ainda é a versão 18.09.7, e esse commit está obviamente ausente.
Isso é tudo!
Acabou sendo interessante lidar com esse problema, e acho que esse é um bom exemplo de "preenchimento" móvel de interação não trivial.
Se você gostou deste post, também pode gostar da minha revista How Containers Work , que explica os recursos de manipulação de contêiner de 24 páginas do kernel Linux. Você também pode ver privilégios e seccomp-bpf lá .