
l'utilità ps, con un tocco eBPF e contesto container
psc (ps container) è uno scanner di processi veloce che utilizza iteratori eBPF e Google CEL per interrogare lo stato del sistema con precisione e contesto completo dei container.
psc utilizza iteratori eBPF per leggere le informazioni sui processi e sui descrittori di file direttamente dalle strutture dati del kernel. Questo approccio è:
/proc. Ho in mente di aggiungere anche un modo per accedere a determinati campi di task_struct su richiesta per usi avanzati, ma è solo un'idea/proc, fornendo visibilità che non può essere sovvertita da rootkit in userland o trucchi con LD_PRELOADGli strumenti Linux tradizionali come ps, lsof e ss sono potenti ma rigidi. Producono formati fissi che richiedono estese operazioni di piping con grep, awk e sed:
# Traditional: Find nginx processes owned by root
ps aux | grep nginx | grep root | grep -v grep
# psc: Express exactly what you mean
psc 'process.name == "nginx" && process.user == "root"'
# Traditional: Find processes with established connections on port 443
ss -tnp | grep ESTAB | grep :443 | awk '{print $6}' | cut -d'"' -f2
# psc: One clear expression
psc 'socket.state == established && socket.dstPort == 443'
psc utilizza il Common Expression Language (CEL) per filtrare i processi. Le espressioni CEL si leggono quasi come un linguaggio naturale, rendendo i tuoi script auto-documentanti e manutenibili. Niente più decifrazioni di pipeline complesse di grep | awk | sed | xargs.
Il flag -o consente di visualizzare esattamente i campi necessari, eliminando del tutto la post-elaborazione:
psc 'socket.state == listen' -o process.name,socket.srcPort
Sono disponibili anche preset di output per stampare rapidamente informazioni comuni:
psc 'socket.type == tcp && socket.dstPort == 443' -o sockets
Gli strumenti tradizionali non hanno il concetto di container. Ottenere informazioni sui container richiede l'analisi dei percorsi cgroup, l'interrogazione dei runtime dei container e la correlazione manuale dei PID:
# Traditional: Find containerized processes (fragile, incomplete)
ps aux | xargs -I{} sh -c 'cat /proc/{}/cgroup 2>/dev/null | grep -q docker && echo {}'
# psc: Native container support
psc 'container.runtime == docker'
psc estrae automaticamente il contesto del container (ID, nome, immagine, runtime, etichette) per Docker, containerd, CRI-O e Podman. Esegui il debug di processi, file e connessioni di rete di qualsiasi container direttamente dall'host:
# Show all processes in a specific container
psc 'container.name == "my-app"' --tree
# Find containers running as root
psc 'container.runtime == docker && process.user == "root"'
# List containers with their images
psc 'container.id != ""' -o process.pid,process.name,container.name,container.image
Su Debian/Ubuntu:
sudo apt-get install clang llvm libbpf-dev linux-headers-$(uname -r) linux-tools-$(uname -r)
Su Fedora/RHEL:
sudo dnf install clang llvm libbpf-devel kernel-devel bpftool
# Generate vmlinux.h (required once per kernel version)
make vmlinux
# Build the binary
make build
Oppure manualmente:
bpftool btf dump file /sys/kernel/btf/vmlinux format c > bpf/vmlinux.h
go generate ./...
go build -o psc
sudo make install
psc richiede privilegi di root per caricare i programmi eBPF.
# List all processes
sudo psc
# List all processes as a tree
sudo psc --tree
Passa un'espressione CEL come primo argomento per filtrare i processi:
# Filter by process name
psc 'process.name == "nginx"'
# Filter by user
psc 'process.user == "root"'
# Filter by command line content
psc 'process.cmdline.contains("--config")'
# Filter by PID range
psc 'process.pid > 1000 && process.pid < 2000'
# Combine conditions
psc 'process.name == "bash" || process.name == "zsh"'
# Show only containerized processes
psc 'container.id != ""'
# Filter by container runtime (constants: docker, containerd, crio, podman)
psc 'container.runtime == docker'
# Filter by container name
psc 'container.name == "nginx"'
# Filter by container image
psc 'container.image.contains("nginx:latest")'
# Show as tree to see container process hierarchy
psc 'container.runtime == docker' --tree
Capire perché un processo esiste spesso richiede l'esame dei suoi descrittori di file aperti e delle connessioni di rete:
# Find processes with listening TCP sockets
psc 'socket.type == tcp && socket.state == listen'
# Find processes with established connections
psc 'socket.state == established'
# Find processes connected to a specific port
psc 'socket.dstPort == 443'
# Find processes using Unix sockets
psc 'socket.family == unix'
# Find processes with files open in /etc
psc 'file.path.startsWith("/etc")'
Campi del processo (process.X):
name - Nome del processo (stringa)pid - ID del processo (int)ppid - ID del processo padre (int)tid - ID del thread (int)euid - ID utente effettivo (int)ruid - ID utente reale (int)suid - ID utente salvato (int)user - Nome utente (stringa)cmdline - Linea di comando completa (stringa)state - Stato del processo (uint)Campi delle capacità (process.capabilities.X):
effective - Maschera di bit delle capacità effettive (uint)permitted - Maschera di bit delle capacità consentite (uint)inheritable - Maschera di bit delle capacità ereditabili (uint)Campi dei namespace (process.namespaces.X):
net - Inode del namespace di rete (int)pid - Inode del namespace PID (int)mnt - Inode del namespace di mount (int)uts - Inode del namespace UTS (int)ipc - Inode del namespace IPC (int)cgroup - Inode del namespace cgroup (int)Campi del container (container.X):
id - ID del container (stringa)name - Nome del container (stringa)image - Immagine del container (stringa)runtime - Runtime del container (stringa)labels - Etichette del container (mappa)Campi file/socket (file.X o socket.X):
path - Percorso del file (stringa)fd - Numero del descrittore di file (int)srcPort - Porta di origine (int)dstPort - Porta di destinazione (int)type - Tipo di socket (tcp, udp)state - Stato del socket (per il filtraggio, usa costanti come listen, established)family - Famiglia di indirizzi (unix, inet, inet6)unixPath - Percorso del socket Unix (stringa)fdType - Tipo di FD (file_type, socket_type)Usale senza virgolette nelle espressioni: