Environnements waza : isoler des agents avec Go et Linux
Environnements waza : isoler des agents avec Go et Linux
Un script Python malveillant a accédé à nos variables d’environnement de production. L’incident s’est produit dans l’un de nos Environnements waza lors d’un test automatisé.
Nous utilisions Go 1.22 pour orchestrer des agents autonomes. Le processus enfant, bien qu’isolé par un simple dossier, partageait encore le même espace de noms réseau et les mêmes descripteurs de fichiers que le parent. Une fuite de jetons AWS a été détectée 14 minutes après le lancement du conteneur de test.
Cet article détaille comment nous avons reconstruit notre système d’isolation. Vous apprendrez à manipuler les primitives Linux via Go pour créer de véritables Environnements waza hermétiques.
🛠️ Prérequis
Pour reproduire ces mécanismes, une machine Linux est indispensable.
- Linux Kernel 5.15 ou supérieur (pour le support complet de Cgroups v2).
- Go 1.22+.
- Privilèges root ou capacités
CAP_SYS_ADMINpour l’utilisation deunshare. - Installation des outils de debug :
strace,bpftrace.
📚 Comprendre Environnements waza
L’isolation de processus repose sur deux piliers du noyau Linux : les Namespaces et les Cgroups. Les Namespaces (espaces de noms) permettent de masquer les ressources du système (PID, Network, Mount, UTS, IPC, User). Les Cgroups (Control Groups) limitent l’utilisation des ressources physiques (CPU, RAM, I/O).
Dans nos Environnements waza, nous ne nous contentons pas de chroot. Le chroot est une illusion de sécurité insuffisante. Un processus root dans un chroot peut s’échapper en manipissant les descripendant de fichiers. Nous utilisons donc la primitive unshare(2).
Structure simplifiée de l'isolation :
[ Host Process (Root) ]
|
|-- [ syscall.Unshare(CLONE_NEWNS | CLONE_NEWPID | ...) ]
| |
| |-- [ Child Process (Sandboxed) ]
| |-- [ Isolated Mount Namespace ]
| |-- [ Isolated PID Namespace ]
| |-- [ Limited Cgroups ]
En Go, l’utilisation de ces primitives est complexe. Le scheduler de Go déplace les goroutines entre les threads système (M). Or, les appels unshare affectent le thread appelant. Si une goroutine effectue un unshare, seule cette thread est isolée. Les autres threads du même processus partagent toujours l’espace de noms original. Cela nécessite l’utilisation de runtime.LockOSThread() pour garantir la stabilité de l’isolation.
🐹 Le code — Environnements waza
📖 Explication
Dans le premier snippet, l’utilisation de runtime.LockOSThread() est la clé. Sans cela, le Go scheduler peut déplacer l’exécution de la fonction IsolerProcess sur un autre thread M après l’appel à unshare, rendant l’isolation inopérante pour les opérations suivantes.
Le champ Cloneflags utilise des constantes du package syscall. CLONE_NEWNS est crucial pour isoler les points de montage. Si vous oubliez ce flag, l’agent peut monter de nouveaux systèmes de fichiers sur des répertoires sensibles de l’hôte.
L’assignation de cmd.Env à une liste restreinte est une défense en profondeur. Ne jamais faire cmd.Env = os.Environ(). C’est l’erreur qui a causé notre fuite de données. La doc officielle de Go précise que l’environnement est hérité par défaut.
🔄 Second exemple
▶️ Exemple d’utilisation
Pour tester notre isolation, lancez le code suivant. Il tente de lancer un shell dans un namespace restreint.
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
"runtime"
)
func main() {
// On simule la création d'un environnement waza
runtime.LockOSThread()
cmd := exec.Command("/bin/sh")
cmd.SysProcAttr = &syscall.Sysoptr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Println("Lancement de l'agent dans un Environnement waza...")
err := cmd.Run()
if err != nil {
fmt.Printf("Erreur: %v\n", err)
}
}\
Sortie attendue (si vous vérifiez le hostname dans le shell lancé) :
🚀 Cas d'usage avancés
1. Exécution de plugins tiers : Utiliser les Environnements waza pour charger des modules .so ou .py sans risquer l'accès au filesystem hôte. cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWNS}.
2. Analyse de malware : Isoler un processus réseau pour observer ses communications sans qu'il puisse scanner le réseau local. Cloneflags: syscall.CLONE_NEWNET.
3. Limitation de ressources (Quotas) : Combiner l'isolation avec l'écriture dans /sys/fs/cgroup/ pour limiter la consommation CPU. Cela empêche un agent de monopoliser les cycles processeur de l'orchestrateur.
✅ Bonnes pratiques
Pour garantir la sécurité des Environnements waza, suivez ces règles :
- Principe du moindre privilège : Ne lancez jamais l'agent avec les privilèges root. Utilisez
setuid pour descendre les privilèges dans le SysProcAttr.
- Immuabilité : Le système de fichiers de l'agent doit être en lecture seule. Utilisez
MS_RDONLY lors du montage des volumes.
- Surveillance des ressources : Implémentez toujours une limite de mémoire via Cgroups. Un agent sans limite est une bombe à retardement pour l'OOM Killer.
- Audit des syscalls: Utilisez
seccomp pour interdire les appels système dangereux comme mount ou ptrace.
- Nettoyage: Assurez-vous que les descripteurs de fichiers (FD) sont fermés avant le fork pour éviter que l'agent n'hérite d'un socket de base de données.
Points clés
- L'isolation par namespace est indispensable pour les agents tiers.
- Le Go scheduler nécessite runtime.LockOSThread pour les syscalls d'isolation.
- Le CLONE_NEWPID empêche la visibilité des processus hôtes.
- L'héritage de l'environnement est le vecteur de fuite de secrets n°1.
- Le Cgroups v2 est nécessaire pour prévenir l'épuisement de la RAM.
- L'utilisation de chroot seul est une faille de sécurité majeure.
- Le nettoyage des variables d'environnement doit être explicite.
- L'isolation réseau via CLONE_NEWNET est vitale pour les agents suspects.
❓ Questions fréquentes
Est-ce que l'isolation est suffisante pour du code malveillant ?
Non, seuls les Environnements waza avec Seccomp et Cgroups offrent une protection sérieuse. L'isolation de namespace ne bloque pas l'exploitation de failles kernel.
Pourquoi utiliser Go plutôt que Python pour l'orchestrateur ?
Go offre un contrôle précis sur les primitives système et une gestion efficace de la concurrence pour gérer des milliers d'agents simultanément.
Comment limiter l'accès au disque ?
Utilisez le namespace Mount (CLONE_NEWNS) et montez un système de fichiers temporaire (tmpfs) en lecture seule.
L'utilisation de containers Docker est-elle une alternative ?
Docker utilise les mêmes primitives. Créer vos propres Environnements waza est utile pour des besoins de très faible latence ou des architectures ultra-légères.
📚 Sur le même blog
🔗 Le même sujet sur nos autres blogs
📝 Conclusion
La création d'Environnements waza demande une compréhension profonde du noyau Linux et du runtime Go. L'isolation simple ne suffit pas ; il faut une stratégie de défense en profondeur combinant Namespaces, Cgroups et Seccomp. Pour approfondir la gestion des processus en Go, consultez la documentation Go officielle. Un processus mal configuré est une porte ouverte sur votre infrastructure.