
l'utilitaire ps, avec une touche eBPF et contexte de conteneur
psc (ps container) est un scanner de processus rapide qui utilise les itérateurs eBPF et Google CEL pour interroger l'état du système avec précision et un contexte de conteneur complet.
psc utilise les itérateurs eBPF pour lire les informations sur les processus et les descripteurs de fichier directement depuis les structures de données du noyau. Cette approche est :
/proc. Je prévois également d'ajouter un moyen d'accéder à certains champs de task_struct à la demande pour des utilisations avancées, mais ce n'est qu'une idée/proc, offrant une visibilité qui ne peut pas être subvertie par les rootkits en espace utilisateur ou les astuces LD_PRELOADLes outils Linux traditionnels comme ps, lsof et ss sont puissants mais rigides. Ils produisent des formats fixes qui nécessitent un long tuyautage via grep, awk et sed :
# Traditionnel : trouver les processus nginx appartenant à root
ps aux | grep nginx | grep root | grep -v grep
# psc : exprimez exactement ce que vous voulez
psc 'process.name == "nginx" && process.user == "root"'
# Traditionnel : trouver les processus avec des connexions établies sur le port 443
ss -tnp | grep ESTAB | grep :443 | awk '{print $6}' | cut -d'"' -f2
# psc : une expression claire
psc 'socket.state == established && socket.dstPort == 443'
psc utilise le Common Expression Language (CEL) pour filtrer les processus. Les expressions CEL se lisent presque comme un langage naturel, rendant vos scripts auto-documentés et maintenables. Finis les pipelines complexes de grep | awk | sed | xargs.
Le flag -o vous permet de produire exactement les champs dont vous avez besoin, éliminant tout post-traitement :
psc 'socket.state == listen' -o process.name,socket.srcPort
Des préréglages de sortie sont également disponibles pour afficher rapidement les informations courantes :
psc 'socket.type == tcp && socket.dstPort == 443' -o sockets
Les outils traditionnels n'ont pas de notion de conteneurs. Obtenir des informations sur les conteneurs nécessite d'analyser les chemins cgroup, d'interroger les moteurs d'exécution de conteneurs et de corréler manuellement les PID :
# Traditionnel : trouver les processus dans des conteneurs (fragile, incomplet)
ps aux | xargs -I{} sh -c 'cat /proc/{}/cgroup 2>/dev/null | grep -q docker && echo {}'
# psc : support natif des conteneurs
psc 'container.runtime == docker'
psc extrait automatiquement le contexte des conteneurs (ID, nom, image, moteur d'exécution, labels) pour Docker, containerd, CRI-O et Podman. Déboguez les processus, fichiers et connexions réseau de n'importe quel conteneur directement depuis l'hôte :
# Afficher tous les processus d'un conteneur spécifique
psc 'container.name == "my-app"' --tree
# Trouver les conteneurs tournant en tant que root
psc 'container.runtime == docker && process.user == "root"'
# Lister les conteneurs avec leurs images
psc 'container.id != ""' -o process.pid,process.name,container.name,container.image
Sur Debian/Ubuntu :
sudo apt-get install clang llvm libbpf-dev linux-headers-$(uname -r) linux-tools-$(uname -r)
Sur Fedora/RHEL :
sudo dnf install clang llvm libbpf-devel kernel-devel bpftool
# Générer vmlinux.h (nécessaire une fois par version du noyau)
make vmlinux
# Construire le binaire
make build
Ou manuellement :
bpftool btf dump file /sys/kernel/btf/vmlinux format c > bpf/vmlinux.h
go generate ./...
go build -o psc
sudo make install
psc nécessite les privilèges root pour charger les programmes eBPF.
# Lister tous les processus
sudo psc
# Lister tous les processus en arborescence
sudo psc --tree
Passez une expression CEL comme premier argument pour filtrer les processus :
# Filtrer par nom de processus
psc 'process.name == "nginx"'
# Filtrer par utilisateur
psc 'process.user == "root"'
# Filtrer par contenu de la ligne de commande
psc 'process.cmdline.contains("--config")'
# Filtrer par plage de PID
psc 'process.pid > 1000 && process.pid < 2000'
# Combinaison de conditions
psc 'process.name == "bash" || process.name == "zsh"'
# Afficher uniquement les processus conteneurisés
psc 'container.id != ""'
# Filtrer par moteur d'exécution de conteneur (constantes : docker, containerd, crio, podman)
psc 'container.runtime == docker'
# Filtrer par nom de conteneur
psc 'container.name == "nginx"'
# Filtrer par image de conteneur
psc 'container.image.contains("nginx:latest")'
# Afficher en arborescence pour voir la hiérarchie des processus du conteneur
psc 'container.runtime == docker' --tree
Comprendre pourquoi un processus existe nécessite souvent d'examiner ses descripteurs de fichier ouverts et ses connexions réseau :
# Trouver les processus avec des sockets TCP en écoute
psc 'socket.type == tcp && socket.state == listen'
# Trouver les processus avec des connexions établies
psc 'socket.state == established'
# Trouver les processus connectés à un port spécifique
psc 'socket.dstPort == 443'
# Trouver les processus utilisant des sockets Unix
psc 'socket.family == unix'
# Trouver les processus ayant des fichiers ouverts dans /etc
psc 'file.path.startsWith("/etc")'
Champs des processus (process.X) :
name - Nom du processus (chaîne)pid - ID du processus (entier)ppid - ID du processus parent (entier)tid - ID du thread (entier)euid - ID utilisateur effectif (entier)ruid - ID utilisateur réel (entier)suid - ID utilisateur enregistré (entier)user - Nom d'utilisateur (chaîne)cmdline - Ligne de commande complète (chaîne)state - État du processus (uint)Champs des capacités (process.capabilities.X) :
effective - Masque de bits des capacités effectives (uint)permitted - Masque de bits des capacités autorisées (uint)inheritable - Masque de bits des capacités héritables (uint)Champs des espaces de noms (process.namespaces.X) :
net - Inode de l'espace de noms réseau (entier)pid - Inode de l'espace de noms PID (entier)mnt - Inode de l'espace de noms de montage (entier)uts - Inode de l'espace de noms UTS (entier)ipc - Inode de l'espace de noms IPC (entier)cgroup - Inode de l'espace de noms cgroup (entier)Champs des conteneurs (container.X) :
id - ID du conteneur (chaîne)name - Nom du conteneur (chaîne)image - Image du conteneur (chaîne)runtime - Moteur d'exécution du conteneur (chaîne)labels - Labels du conteneur (map)