strace
. Esto es lo que sucedió cuando se ejecutó strace
en el contenedor Docker en mi computadora portátil:
$ docker run -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace
funciona a través de una llamada al sistema ptrace
, por ptrace
lo que no funcionará sin permiso . Pero esto es fácil de solucionar, y en mi computadora portátil hice todo lo siguiente:
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
Pero fue interesante para mí no resolver el problema, sino descubrir por qué generalmente surge esta situación. Entonces, ¿por qué
strace
no funciona, sino que --cap-add=SYS_PTRACE
corrige todo?
Hipótesis 1: los procesos de contenedores no tienen su propio privilegio CAP_SYS_PTRACE
Como el problema se resuelve de manera estable
--cap-add=SYS_PTRACE
, siempre me pareció que los procesos de contenedor Docker, por definición, no tienen sus propios privilegios CAP_SYS_PTRACE
, pero por dos razones, algo no converge aquí.
Razón 1: Como experimento,
strace
al iniciar sesión como usuario normal, podría iniciar fácilmente cualquier proceso, pero comprobar si mi proceso actual tenía privilegios CAP_SYS_PTRACE
no encontró nada:
$ getpcaps $$
Capabilities for `11589': =
Razón 2: en
man capabilities
el privilegio se CAP_SYS_PTRACE
lee lo siguiente:
CAP_SYS_PTRACE
* Trace arbitrary processes using ptrace(2);
El punto
CAP_SYS_PTRACE
es que, por analogía con la raíz, podemos tomar el control del proceso arbitrario de cualquier usuario. Para el ptrace
proceso normal de su usuario, este privilegio no es necesario.
Además, realicé una comprobación más: inicié el contenedor Docker
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
, luego revoqué el privilegio CAP_SYS_PTRACE
y strace
continué trabajando correctamente incluso sin el privilegio. ¡¿Por qué?!
Hipótesis 2: ¿Es un espacio de nombres personalizado?
Mi siguiente (y mucho peor) hipótesis sonó como "hmm, tal vez el proceso está en un espacio de nombre de usuario diferente y
strace
no funciona ... ¿solo porque?" Parece un conjunto de declaraciones no muy coherentes, pero aún intenté ver el problema desde este lado.
Entonces, ¿está el proceso en un espacio de nombres diferente definido por el usuario? Así es como se ve en el contenedor:
root@e27f594da870:/# ls /proc/$$/ns/user -l
... /proc/1/ns/user -> 'user:[4026531837]'
Y así es como se ve en el host:
bork@kiwi:~$ ls /proc/$$/ns/user -l
... /proc/12177/ns/user -> 'user:[4026531837]'
root en el contenedor es el mismo usuario que root en el host, ya que comparten un nombre de usuario común en el espacio de nombres de usuario (4026531837), por lo que no debería haber ninguna
strace
razón de interferencia de ese lado . Como puede ver, la hipótesis resultó ser regular, pero aún no me di cuenta de que los usuarios en el contenedor y en el host son los mismos, y este enfoque me pareció interesante.
Hipótesis 3: la llamada al sistema está ptrace
bloqueada por una reglaseccomp-bpf
Ya sabía que hay una regla en Docker para restringir una gran cantidad de llamadas al sistema que deben ejecutar los procesadores de contenedores en Docker
seccomp-bpf
, ¡y resultó que en su lista de llamadas están bloqueadas por definición ptrace
! (De hecho, la lista de llamadas es una lista de excepciones y ptrace
simplemente no entra, pero el resultado no cambia).
Ahora está claro por qué no funciona en un contenedor Docker
strace
, porque es obvio que una ptrace
llamada completamente bloqueada no funcionará.
Probemos esta hipótesis y veamos si podemos usar el
strace
contenedor en el Docker si deshabilitamos todas las reglas de 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 ...
¡Multa! ¡Todo funciona y se revela el secreto! Eso es solo ...
¿ --cap-add=SYS_PTRACE
Por qué resuelve el problema?
Todavía no hemos explicado por qué
--cap-add=SYS_PTRACE
resuelve el problema emergente con las llamadas. La página principal docker run
explica cómo funciona el argumento de la siguiente manera --cap-add
:
--cap-add=[]
Add Linux capabilities
¡Nada de esto tiene nada que ver con las reglas de seccomp! ¿Cuál es el problema?
Echemos un vistazo al código fuente de Docker.
Si la documentación no ayuda, todo lo que tenemos que hacer es sumergirnos en las fuentes.
Una cosa buena de Go es que al vender dependencias en un repositorio de Go,
grep
puede recorrer todo el repositorio y encontrar el código que le interesa. Así que lo github.com/moby/moby
cloné y lo busqué en busca de expresiones de ese tipo rg CAP_SYS_PTRACE
.
En mi opinión, esto es lo que sucede aquí: en la implementación de seccomp en el contenedor, en la sección contrib / seccomp / seccomp_default.go, hay mucho código que, a través de la regla seccomp, verifica si un proceso con privilegios tiene permiso para usar llamadas al sistema de acuerdo con este privilegio.
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{},
})
Todavía hay código en moby que para profiles / seccomp / seccomp.go y para perfiles seccomp, por definición, lleva a cabo operaciones similares, por lo que probablemente encontramos nuestra respuesta.
Docker --cap-add
es capaz de más de lo que se dice.
Al final, parece que
--cap-add
no es exactamente lo que dice en la página principal, y más bien debería verse --cap-add-and-also-whitelist-some-extra-system-calls-if-required
. Y parece ser cierto: si tiene el privilegio del espíritu CAP_SYS_PTRACE
, que le permite usar una llamada del sistema process_vm_readv
, pero la llamada está bloqueada en el perfil de Seccomp, no es de mucha ayuda, por lo que la autorización para usar las llamadas del sistema process_vm_readv
y a ptrace
través de CAP_SYS_PTRACE
parece razonable.
Resulta que strace
funciona en las últimas versiones de Docker
Para las versiones de kernel 4.8 y superiores, gracias a este commit , Docker 19.03 finalmente permitió llamadas al sistema
ptrace
. Eso es solo en mi computadora portátil Docker, todavía hay la versión 18.09.7, y esta confirmación obviamente falta.
¡Eso es todo!
Resultó ser interesante tratar este problema, y creo que este es un buen ejemplo de un "llenado" de contenedores en movimiento que no interactúa de manera no trivial.
Si le gustó esta publicación, puede que le guste mi revista " Cómo funcionan los contenedores ", sus 24 páginas explican las características del kernel de Linux para organizar el trabajo de contenedores. También puede ver privilegios y seccomp-bpf allí .