- Les informations sur tous les processus, même ceux de courte durée, doivent être consignées.
- Nous devrions avoir des informations sur le chemin complet du fichier exécutable pour tous les processus en cours d'exécution.
- Nous ne devrions pas, dans des limites raisonnables, modifier ou recompiler notre code pour différentes versions du noyau.
- : - Kubernetes Docker, , / .
cgroup ID
. , , «» « ». , « », « », « », API, Docker . ID , . Docker .
Parlons des API Linux courantes qui peuvent vous aider dans cette tâche. Afin de ne pas compliquer l'histoire, nous porterons une attention particulière aux processus créés à l'aide d'appels système
execve
. Si nous parlons d'une solution plus complète du problème, alors lors de sa mise en œuvre, il est nécessaire, en outre, de surveiller les processus créés à l'aide des appels système fork/clone
et de leurs variantes, ainsi que les résultats des appels execveat
.
Solutions simples implémentées en mode utilisateur
- Contacter
/proc
. Cette méthode, en raison du problème des processus de courte durée, ne nous convient pas particulièrement. - netlink. netlink ,
PID
. ./proc
, . - Linux. — , . API . . . — , , , . , API ,
auditd
osquery
. , ,auditd
go-audit, en théorie, peut atténuer ce problème. Mais dans le cas des solutions d'entreprise, vous ne pouvez pas savoir à l'avance si les clients utilisent de tels outils, et s'ils le font, lesquels. Il n'est pas non plus possible de savoir à l'avance quels contrôles de sécurité qui fonctionnent directement avec l'API d'audit sont utilisés par les clients. Le deuxième inconvénient est que les API d'audit ne savent rien des conteneurs. Et ceci malgré le fait que cette question est discutée depuis de nombreuses années.
Outils de débogage simples en mode noyau
La mise en œuvre de ces mécanismes implique l'utilisation de "sondes" de différents types en un seul exemplaire.
▍Tracepoints
Utilisation de tracepoints (
tracepoint
). Les points de trace sont des capteurs connectés statiquement à des emplacements spécifiques du noyau lors de la compilation. Chacun de ces capteurs peut être activé indépendamment des autres, à la suite de quoi il émettra des notifications dans les cas où l'emplacement du code du noyau est atteint, où il est intégré. Le noyau contient plusieurs points de trace qui nous conviennent, dont le code est exécuté à différents points de l'appel système execve
. Ce - sched_process_exec
, open_exec
, sys_enter_execve
, sys_exit_execve
. Afin d'obtenir cette liste, j'ai exécuté la commandecat /sys/kernel/tracing/available_events | grep exec
et filtré la liste résultante en utilisant les informations obtenues en lisant le code du noyau. Ces traces nous conviennent mieux que les mécanismes décrits ci-dessus, puisqu'ils permettent d'organiser l'observation de processus éphémères. Mais aucun d'entre eux ne donne d'informations sur le chemin d'accès complet au fichier exécutable du processus dans le cas où les paramètres exec
sont le chemin relatif vers un tel fichier. En d'autres termes, si l'utilisateur exécute une commande comme cd /bin && ./ls
, alors nous obtiendrons les informations de chemin dans le formulaire ./ls
, pas dans le formulaire /bin/ls
. Voici un exemple simple:
# the sched_process_exec
sudo -s
cd /sys/kernel/debug/tracing
echo 1 > events/sched/sched_process_exec/enable
# ls
cd /bin && ./ls
# sched_process_exec
# ,
cd -
cat trace | grep ls
#
echo 0 > events/sched/sched_process_exec/enable
▍ Capteurs Kprobe / kretprobe
Les capteurs
kprobe
vous permettent d'extraire des informations de débogage de presque n'importe où dans le noyau. Ils sont comme des points d'arrêt spéciaux dans le code du noyau qui donnent des informations sans arrêter l'exécution du code. Un capteur kprobe
, contrairement aux trackpoints, peut être connecté à une grande variété de fonctions. Le code d'un tel capteur sera déclenché lors de l'exécution d'un appel système execve
. Mais je n'ai trouvé dans le graphe d'appel execve
aucune fonction, dont les paramètres sont à la fois le PID
processus et le chemin complet de son fichier exécutable. En conséquence, nous sommes confrontés au même "problème de chemin relatif" que lors de l'utilisation de tracepoints. Ici, vous pouvez, en vous basant sur les particularités d'un noyau particulier, "modifier" quelque chose. Après tout, les capteurskprobe
peut lire les données de la pile d'appels du noyau. Mais une telle solution ne fonctionnera pas de manière stable dans différentes versions du noyau. Par conséquent, je ne le considère pas.
▍Utilisation de programmes eBPF avec des points de trace, avec des sondes kprobe et kretprobe
Ici, nous parlons du fait que lorsque du code est exécuté, des points de trace ou des capteurs seront déclenchés, mais le code des programmes eBPF sera exécuté, et non le code des gestionnaires d'événements ordinaires.
L'utilisation de cette approche nous ouvre de nouvelles possibilités. Maintenant, nous pouvons exécuter du code arbitraire dans le noyau lorsque nous faisons un appel système
execve
. Ceci, en théorie, devrait nous donner la possibilité d'extraire toutes les informations dont nous avons besoin du noyau et de les envoyer dans l'espace utilisateur. Il existe deux façons d'obtenir ce type de données, mais aucune d'entre elles ne répond aux exigences ci-dessus.
- ,
task_struct
linux_binprm
. , , . ,sched_process_exec
, eBPF- , dentrybprm->file->f_path.dentry
, . eBPF- . . , eBPF- , , , . - eBPF . , . — API. — . (, eBPF,
cgroup ID
, ).
«»
-
LD_PRELOAD
exec
libc
. , . , , , , . -
execve
,fork/clone
chdir
, . ,execve
execve
. — eBPF- eBPF- , . - ,
ptrace
. -. —ptrace
seccomp
SECCOMP_RET_TRACE
.seccomp
execve
,execve
seccomp
execve
. - Utilisation d'AppArmor. Vous pouvez écrire un profil AppArmor pour empêcher les processus d'appeler des fichiers exécutables. Si vous utilisez ce profil en mode formation (plainte), AppArmor n'interdira pas l'exécution des processus, mais émettra uniquement des notifications sur les violations des règles spécifiées dans le profil. Si nous connectons un profil à chaque processus en cours, nous obtenons une solution fonctionnelle, mais très peu attrayante et trop «hackish». Cela ne vaut probablement pas la peine d'utiliser cette approche.
Autres solutions
Je dirai tout de suite qu'aucune de ces solutions ne répond à nos exigences, mais je vais néanmoins les énumérer:
- Utilisation de l'utilitaire
ps
. Cet outil fait simplement référence/proc
et, par conséquent, souffre des mêmes problèmes que l'accès direct à/proc
. - execsnoop, eBPF. , , ,
kprobe/kretprobe
, , , . ,execsnoop
, , , . -
execsnoop
, eBPF. —kprobe
, .
À l'avenir, il sera possible d'utiliser la fonction d'assistance eBPF get_fd_path, qui n'est pas encore disponible . Une fois ajouté au noyau, il sera utile pour résoudre notre problème. Certes, le chemin complet vers le fichier exécutable du processus devra être obtenu en utilisant une méthode qui ne permet pas de lire les informations à partir des structures de données du noyau.
Résultat
Aucune des API que nous avons examinées n'est parfaite. Ci-dessous, je veux donner quelques conseils sur les approches à utiliser pour obtenir des informations sur les processus et quand les utiliser:
- —
auditd
go-audit. , . , , , . , , , - API , , . — , . - , , , , ,
execsnoop
. — . - , , , , , . , . , , eBPF- eBPF-,
perf
... Une histoire sur tout cela mérite un article séparé. La chose la plus importante à retenir lors du choix de cette méthode de surveillance des processus est la suivante. Si vous utilisez des programmes eBPF, vérifiez la possibilité de leur compilation statique, ce qui vous permettra de ne pas dépendre des en-têtes du noyau. Mais c'est précisément cette dépendance que nous essayons d'éviter d'utiliser cette méthode. L'utilisation de cette méthode signifie également que vous ne pouvez pas travailler avec des structures de données du noyau et que vous ne pouvez pas utiliser de frameworks comme BCC qui compilent des programmes eBPF lors de l'exécution. - Si les processus de courte durée ne vous intéressent pas et que les recommandations précédentes ne vous conviennent pas, utilisez les fonctionnalités de netlink avec
/proc
.
Comment organisez-vous la surveillance des processus en cours d'exécution sous Linux?