strace
. Voici ce qui s'est passé lors de l'exécution strace
dans le conteneur Docker sur mon ordinateur portable:
$ docker run -it ubuntu:18.04 /bin/bash
$ # ... install strace ...
root@e27f594da870:/# strace ls
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace
fonctionne grâce à un appel système ptrace
, donc ptrace
cela ne fonctionnera pour rien sans permission ! Mais c'est facile à réparer, et sur mon ordinateur portable, je l'ai fait comme ceci:
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
Mais je n'étais pas intéressé à résoudre le problème, mais à comprendre pourquoi cette situation se présente. Alors pourquoi ça
strace
ne marche pas, mais ça --cap-add=SYS_PTRACE
corrige tout?
Hypothèse 1: les processus de conteneur n'ont pas leur propre privilège CAP_SYS_PTRACE
Le problème étant résolu de manière cohérente
--cap-add=SYS_PTRACE
, il m'a toujours semblé que les processus de conteneur Docker, par définition, n'avaient pas leur propre privilège CAP_SYS_PTRACE
, mais pour deux raisons, quelque chose ne s'additionnait pas ici.
Raison 1: à titre expérimental, je, étant connecté en tant qu'utilisateur régulier, pourrais facilement démarrer
strace
n'importe quel processus, cependant, je CAP_SYS_PTRACE
n'ai rien trouvé dans le privilège de mon processus actuel :
$ getpcaps $$
Capabilities for `11589': =
Raison 2: dans
man capabilities
le privilège se CAP_SYS_PTRACE
lit comme suit:
CAP_SYS_PTRACE
* Trace arbitrary processes using ptrace(2);
Le fait
CAP_SYS_PTRACE
est que, par analogie avec root, nous pouvons prendre le contrĂ´le du processus arbitraire de n'importe quel utilisateur. Pour ptrace
votre utilisateur, ce privilège n'a pas besoin d'un processus conventionnel.
De plus, j'ai effectué une autre vérification: j'ai lancé le conteneur Docker
docker run --cap-add=SYS_PTRACE -it ubuntu:18.04 /bin/bash
, puis j'ai révoqué le privilège CAP_SYS_PTRACE
- et j'ai strace
continué à fonctionner correctement même sans le privilège. Pourquoi?!
Hypothèse 2: s'agit-il d'un espace de noms personnalisé?
Mon hypothèse suivante (et beaucoup moins fondée) ressemblait à "hmm, peut-être que le processus est dans un espace de noms d'utilisateur différent et
strace
ne fonctionne pas ... juste parce que?" Cela ressemble à un ensemble de déclarations pas très cohérentes, mais j'ai quand même essayé de regarder le problème de ce point de vue.
Ainsi, le processus est-il dans un espace de noms différent défini par l'utilisateur? Voici à quoi cela ressemble dans le conteneur:
root@e27f594da870:/# ls /proc/$$/ns/user -l
... /proc/1/ns/user -> 'user:[4026531837]'
Et voici Ă quoi cela ressemble sur l'hĂ´te:
bork@kiwi:~$ ls /proc/$$/ns/user -l
... /proc/12177/ns/user -> 'user:[4026531837]'
root dans le conteneur est le mĂŞme utilisateur que root sur l'hĂ´te, car ils ont un identifiant d'espace de nom d'utilisateur commun (4026531837), il ne devrait donc pas y avoir de
strace
raisons interférentes de ce côté . Comme vous pouvez le voir, l'hypothèse s'est avérée médiocre, mais je ne me suis pas encore rendu compte que les utilisateurs dans le conteneur et sur l'hôte sont les mêmes, et cette approche m'a semblé intéressante.
Hypothèse 3: l'appel système est ptrace
bloqué par une règleseccomp-bpf
Je savais déjà qu'il existe une règle dans Docker pour restreindre un grand nombre d'appels système à exécuter par les processeurs de conteneurs dans Docker
seccomp-bpf
, et il s'est avéré qu'il y avait et dans sa liste d'appels bloqués par définition ptrace
! (En fait, la liste d'appels est une liste d'exceptions et ptrace
n'y entre tout simplement pas, mais le résultat ne change pas.)
Maintenant, il est clair pourquoi cela ne fonctionne pas dans un conteneur Docker
strace
, car il est Ă©vident qu'un ptrace
appel complètement bloqué ne fonctionnera pas. Testons
cette hypothèse et voyons si nous pouvons utiliser
strace
le conteneur Docker si nous désactivons toutes les règles 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 ...
Bien! Tout fonctionne, et le secret est révélé! C'est juste ...
Pourquoi --cap-add=SYS_PTRACE
résout-il le problème?
Nous n'avons toujours pas expliqué pourquoi il
--cap-add=SYS_PTRACE
résout le problème émergent des appels. La page principale docker run
explique le fonctionnement de l'argument comme suit --cap-add
:
--cap-add=[]
Add Linux capabilities
Tout cela n'a rien à voir avec les règles de seccomp! Quel est le problème?
Jetons un coup d'Ĺ“il au code source de Docker.
Si la documentation n'aide pas déjà , il ne nous reste plus qu'à plonger dans la source.
Une bonne chose à propos de Go est qu'en proposant des dépendances dans un référentiel Go, vous
grep
pouvez parcourir l'ensemble du référentiel et trouver le code qui vous intéresse. Je l'ai donc github.com/moby/moby
cloné et scanné à la recherche d'expressions de la forme rg CAP_SYS_PTRACE
.
À mon avis, c'est ce qui se passe ici: dans l'implémentation de seccomp dans le conteneur, dans la section contrib / seccomp / seccomp_default.go, il y a beaucoup de code qui, grâce à la règle seccomp, vérifie si un processus avec des privilèges a la permission d'utiliser des appels système conformément à ce privilège.
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{},
})
Il existe toujours du code dans moby qui, pour les profils / seccomp / seccomp.go et pour les profils seccomp, par définition, effectue des opérations similaires, nous avons donc probablement trouvé notre réponse!
Docker --cap-add
peut faire plus que dit
En conséquence, il
--cap-add
ne semble pas faire exactement ce qui est Ă©crit sur la page principale et devrait plutĂ´t ressembler Ă --cap-add-and-also-whitelist-some-extra-system-calls-if-required
. Et cela semble être vrai: si vous avez un privilège comme CAP_SYS_PTRACE
ce qui vous permet d'utiliser un appel système process_vm_readv
, mais cet appel est bloqué par le profil de seccomp, qui ne vous aide pas beaucoup, permettant ainsi les appels système par process_vm_readv
et Ă ptrace
travers des CAP_SYS_PTRACE
regards raisonnables.
S'avère strace
fonctionner dans les dernières versions de Docker
Pour les versions 4.8 et supérieures du noyau, grâce à ce commit, les appels système sont enfin autorisés dans Docker 19.03
ptrace
. Sauf que sur mon ordinateur portable, Docker est toujours la version 18.09.7, et ce commit est Ă©videmment absent.
C'est tout!
Il s'est avéré intéressant de traiter ce problème, et je pense que c'est un bon exemple d'interaction de "remplissage" de conteneurs non triviale.
Si vous avez aimé cet article, vous aimerez peut-être aussi mon magazine How Containers Work , qui explique les fonctionnalités de gestion des conteneurs de 24 pages du noyau Linux. Là , vous pouvez voir les privilèges et seccomp-bpf .