strace
. Folgendes ist passiert, als strace
ich in einem Docker-Container auf meinem Laptop ausgeführt wurde:
$ docker run -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace
funktioniert über einen Systemaufruf ptrace
, daher ptrace
funktioniert es nicht ohne Erlaubnis ! Aber es ist einfach zu reparieren und auf meinem Laptop habe ich es so gemacht:
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
Aber ich war nicht daran interessiert, das Problem zu lösen, sondern herauszufinden, warum diese Situation überhaupt auftritt. Warum funktioniert es
strace
nicht und --cap-add=SYS_PTRACE
repariert alles?
Hypothese 1: Containerprozesse haben kein eigenes Privileg CAP_SYS_PTRACE
Da das Problem konsequent gelöst wird
--cap-add=SYS_PTRACE
, schien es mir immer, dass Docker-Container-Prozesse per Definition keine eigenen Berechtigungen haben CAP_SYS_PTRACE
, aber aus zwei Gründen passt hier etwas nicht zusammen.
Grund 1: Als Experiment, das als normaler Benutzer angemeldet war, konnte ich problemlos
strace
einen beliebigen Prozess starten. Bei der Überprüfung, ob mein aktueller Prozess über Berechtigungen verfügt, wurde CAP_SYS_PTRACE
jedoch nichts gefunden:
$ getpcaps $$
Capabilities for `11589': =
Grund 2: in
man capabilities
dem Privileg CAP_SYS_PTRACE
lautet wie folgt:
CAP_SYS_PTRACE
* Trace arbitrary processes using ptrace(2);
Der springende Punkt
CAP_SYS_PTRACE
ist, dass wir in Analogie zu root die Kontrolle über den beliebigen Prozess eines jeden Benutzers übernehmen können. Für ptrace
Ihren Benutzer erfordert dieses Privileg keinen herkömmlichen Prozess.
Außerdem habe ich noch eine Überprüfung durchgeführt: Ich habe den Docker-Container durch gestartet
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
, dann die Berechtigung widerrufen CAP_SYS_PTRACE
- und strace
auch ohne diese Berechtigung weiterhin ordnungsgemäß funktioniert. Warum?!
Hypothese 2: Fall im Benutzernamensraum?
Meine nächste (und viel schlimmere) Hypothese klang wie "hmm, vielleicht befindet sich der Prozess in einem anderen Benutzernamensraum und
strace
funktioniert nicht ... nur weil?" Es sieht aus wie eine Reihe nicht sehr kohärenter Aussagen, aber ich habe trotzdem versucht, das Problem von dieser Seite aus zu betrachten.
Befindet sich der Prozess in einem anderen benutzerdefinierten Namespace? So sieht es im Container aus:
root@e27f594da870:/# ls /proc/$$/ns/user -l
... /proc/1/ns/user -> 'user:[4026531837]'
Und so sieht es auf dem Host aus:
bork@kiwi:~$ ls /proc/$$/ns/user -l
... /proc/12177/ns/user -> 'user:[4026531837]'
root im Container ist derselbe Benutzer wie root auf dem Host, da sie eine gemeinsame ID im Benutzernamensraum (4026531837) haben, sodass es von dieser Seite keine störenden
strace
Gründe geben sollte . Wie Sie sehen, stellte sich die Hypothese als mittelmäßig heraus, aber dann wurde mir noch nicht klar, dass die Benutzer im Container und auf dem Host gleich sind, und dieser Ansatz schien mir interessant.
Hypothese 3: Der Systemaufruf wird ptrace
durch eine Regel blockiertseccomp-bpf
Ich wusste bereits, dass es in Docker eine Regel gibt, die eine große Anzahl von Systemaufrufen einschränkt, die von Containerprozessoren in Docker ausgeführt werden sollen
seccomp-bpf
, und es stellte sich heraus, dass und in der Liste der Aufrufe per Definition blockiert sind ptrace
! (Tatsächlich ist die Anrufliste ein Ausnahmeblatt und wird ptrace
einfach nicht aufgerufen , aber das Ergebnis ändert sich nicht.)
Jetzt ist klar, warum der Container im Docker nicht funktioniert
strace
, da es offensichtlich ptrace
nicht funktioniert, einen vollständig blockierten aufzurufen.
Lassen Sie uns diese Hypothese testen und sehen, ob wir den
strace
Container im Docker verwenden können, wenn wir alle Seccomp-Regeln deaktivieren:
$ 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 ...
Fein! Alles funktioniert und das Geheimnis wird gelüftet! Das ist einfach ...
Warum --cap-add=SYS_PTRACE
löst es das Problem?
Wir haben immer noch nicht erklärt, warum es
--cap-add=SYS_PTRACE
das aufkommende Herausforderungsproblem löst. Auf der Hauptseite wird docker run
die Funktionsweise des Arguments wie folgt erläutert --cap-add
:
--cap-add=[]
Add Linux capabilities
All dies hat nichts mit den Regeln von seccomp zu tun! Was ist los?
Werfen wir einen Blick auf den Docker-Quellcode.
Wenn die Dokumentation nicht hilft, müssen wir nur in die Quellen eintauchen.
Go hat eine nette Funktion: Dank des Abhängigkeitsverkaufs im Go-Repository können Sie
grep
durch das gesamte Repository gehen und den Code finden, an dem Sie interessiert sind. Also habe ich ihn github.com/moby/moby
geklont und nach solchen Ausdrücken abgesucht rg CAP_SYS_PTRACE
.
Meiner Meinung nach passiert Folgendes: Bei der Implementierung von seccomp im Container gibt es im Abschnitt contrib / seccomp / seccomp_default.go viel Code, der anhand der seccomp-Regel prüft, ob der Prozess mit Berechtigungen die Berechtigung hat, Systemaufrufe gemäß diesem Privileg zu verwenden.
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{},
})
Es gibt immer noch Code in moby, der für Profile / seccomp / seccomp.go und für Seccomp-Profile per Definition ähnliche Operationen ausführt, also haben wir wahrscheinlich unsere Antwort gefunden!
Docker --cap-add
kann mehr als gesagt
Am Ende scheint es, dass
--cap-add
es nicht genau das tut, was es auf der Hauptseite sagt, und eher so aussehen sollte --cap-add-and-also-whitelist-some-extra-system-calls-if-required
. Und es scheint wahr zu sein: Wenn Sie das Privileg des Geistes haben CAP_SYS_PTRACE
, das es Ihnen ermöglicht, einen Systemaufruf zu verwenden process_vm_readv
, der Anruf jedoch blockiert ist, ist Seccomp-Profil keine große Hilfe, so dass die Berechtigung zur Verwendung der Systemaufrufe process_vm_readv
und ptrace
durch CAP_SYS_PTRACE
vernünftig aussieht.
Es stellt sich heraus, dass es strace
in den neuesten Versionen von Docker funktioniert
Für Kernel-Versionen 4.8 und höher erlaubte Docker 19.03 dank dieses Commits endlich Systemaufrufe
ptrace
. Das ist nur auf meinem Docker-Laptop, es gibt noch Version 18.09.7, und dieses Commit fehlt offensichtlich.
Das ist alles!
Es stellte sich als interessant heraus, sich mit diesem Problem zu befassen, und ich denke, dies ist ein gutes Beispiel für eine nicht trivial interagierende, sich bewegende „Befüllung“ von Behältern.
Wenn Ihnen dieser Beitrag gefallen hat, könnte Ihnen auch mein Magazin How Containers Work gefallen , in dem die 24-seitigen Containerhandhabungsfunktionen des Linux-Kernels erläutert werden. Dort können Sie auch Berechtigungen und seccomp-bpf sehen .