StartupKit
FR

Fonctionnement de l'isolation réseau

Découvrez le modèle de sécurité du triage IA tenant compte du code, la frontière réseau externe à l'agent, l'isolation d'un runner GitLab et la restriction des destinations vers lesquelles l'agent peut envoyer des données issues d'un rapport malveillant.

Pourquoi c’est important

Le triage IA tenant compte du code fournit simultanément à un agent votre code source et le contenu d’un rapport contrôlé par un attaquant. C’est une cible d’injection de prompt. Un chercheur malveillant peut rédiger un rapport qui tente de détourner l’agent, de lui faire lire des secrets puis de les envoyer vers un hôte externe.

Appliquez les restrictions réseau hors de l’agent pour bloquer toute destination absente de la liste d’autorisation, y compris lorsque le modèle suit des instructions malveillantes. La configuration ci-dessous conserve deux exceptions : le point de terminaison du modèle et l’hôte MCP de Kit.

Danger

Considérez tout contenu de rapport comme hostile. Le prompt de triage entoure les champs de délimiteurs et demande au modèle de ne jamais exécuter leurs instructions. Cette hygiène renforce la défense, mais ne constitue pas la frontière. Celle-ci repose sur le refus par défaut de toute sortie décrit ci-dessous.

Principe fondamental : confinement au niveau de l’environnement

La décision la plus importante est la suivante : la liste d’autorisation réseau propre à l’agent ne sert jamais de frontière de sécurité. Les recommandations d’Anthropic sur le confinement des systèmes agentiques demandent d’abord un confinement au niveau de l’environnement. Les restrictions que l’agent s’impose ne sont qu’une défense supplémentaire.

Une liste propre à l’agent ne suffit pas :

  • Les listes d’autorisation se contournent. Claude Code a déjà corrigé des failles de contournement, par exemple une classe de service MCP mal interprétée et une différence de traitement entre SOCKS5 et les proxy HTTP.
  • L’agent peut invoquer autre chose. Une injection peut le pousser à utiliser un autre outil, générer une commande ou exploiter une dépendance autorisée.
  • Les secrets restent lisibles. Masquer une variable CI dans les journaux ne l’empêche pas d’être lue au moment de l’exécution.
  • Une autorisation DNS ne suffit pas. Le rebind DNS, les redirections et les adresses IP directes contournent les contrôles naïfs fondés sur le nom d’hôte.

Les restrictions sur les destinations des données doivent s’appliquer hors de l’agent, dans la configuration réseau et celle du runner. Les points de terminaison autorisés reçoivent tout de même les données utilisées pour l’analyse et l’enregistrement du résultat.

La frontière : sorties refusées par défaut et proxy vers deux destinations

L’isolation repose sur quatre contrôles appliqués par l’environnement. Le dépôt d’exemple les illustre dans .gitlab-ci.yml et le répertoire infra/.

1. Un runner GitLab autogéré

Vous devez utiliser un runner autogéré. Les runners partagés de GitLab.com ne permettent pas d’imposer vous-même l’isolation du réseau hôte. Le README du dépôt d’exemple en fait un prérequis.

2. Un exécuteur Docker non privilégié

Réglage Valeur Pourquoi
privileged false Le mode privilégié équivaut presque à l’accès root à l’hôte. Il est exclu pour un chemin alimenté par un rapport non fiable.
Exécution en tant que utilisateur non-root Supprimez SETUID/SETGID et toute capacité inutile.
Socket Docker non monté Un socket monté permet de prendre le contrôle de l’hôte.
Volumes hôte aucun Aucun montage de l’hôte dans la tâche.
FF_NETWORK_PER_BUILD 1 GitLab crée un réseau bridge dédié à chaque tâche, puis le détruit. L’isolation est reproductible.
# config.toml on your self-managed runner
[[runners]]
  executor = "docker"
  environment = ["FF_NETWORK_PER_BUILD=1"]
  [runners.docker]
    privileged = false

3. Refus de toute sortie au niveau de l’hôte

Warning

GitLab Runner ne fournit aucune liste d’autorisation sortante native. Aucun réglage intégré ne limite les tâches à certains hôtes. Une proposition existe, mais n’est pas disponible. La frontière doit être appliquée au niveau de l’hôte ou de l’espace de noms.

Appliquez une politique de sortie avec DROP par défaut sur le réseau de la tâche à l’aide de iptables/nftables, ou placez la tâche dans un espace de noms réseau sans route vers l’intranet. Toute sortie est refusée par défaut. Ouvrez ensuite exactement les deux destinations ci-dessous, rien d’autre, y compris votre réseau interne.

4. Un proxy sortant qui termine TLS et valide le jeton

La seule route autorisée passe par un proxy qui accepte exactement deux destinations :

  1. Le point de terminaison d’inférence du LLM, par exemple https://api.deepseek.com/anthropic.
  2. L’hôte MCP limité de Kit, pour lire le rapport puis renvoyer le triage.

Le proxy ne se contente pas d’épingler ces hôtes :

  • il limite les chemins à l’inférence et à MCP. Les sous-API de stockage de fichiers du fournisseur restent bloquées ;
  • il valide le jeton de session propre à l’exécution. Un attaquant qui introduit sa propre clé d’API ne peut pas l’utiliser, car le proxy n’accepte que le jeton émis pour cette exécution.

Cette approche reprend le principe du proxy MITM dans la VM qu’Anthropic utilise pour confiner ses propres agents. Un filtre sortant de type harden-runner constitue un analogue conceptuel raisonnable pour l’implémentation de référence.

Le masquage relève de l’hygiène, pas de la protection

Stockez la clé du modèle comme variable CI masquée et protégée. Kit transmet le jeton MCP séparément lorsqu’il déclenche chaque pipeline. N’affichez jamais ces valeurs et vérifiez que les journaux des exécutions de test ne les exposent pas.

Important

Le masquage remplace seulement la valeur par [MASKED] dans les journaux. Une tâche compromise peut encore la lire pendant l’exécution. La documentation de GitLab précise elle-même que le masquage n’est pas infaillible. Les restrictions réseau bloquent les destinations non approuvées ; le masquage réduit seulement l’exposition dans les journaux.

Le caractère limité du jeton reste donc essentiel : même lu par une tâche détournée, il ne couvre qu’un rapport pendant une heure et autorise l’envoi d’un seul résultat final de triage. Les éléments de preuve du pipeline doivent être transmis avant cet envoi. Les règles réseau bloquent les autres destinations, mais conservent l’accès au point de terminaison du modèle et à l’hôte MCP de Kit.

Prouvez-le avec le canari d’injection

Le dépôt d’exemple fournit un rapport de test dans examples/sample-report. Il contient un canari d’injection qui tente d’appeler un hôte externe avec curl. Exécutez le pipeline et vérifiez que la frontière sortante bloque l’appel. Si la requête réussit, votre environnement n’est pas isolé. Corrigez le pare-feu avant de connecter Kit.

Vérifiez également qu’après la tâche :

  • aucune route vers le réseau interne n’était disponible ;
  • seul le proxy pouvait être joint ;
  • le journal du proxy ne contient que l’inférence et les appels MCP attendus ;
  • le réseau propre à la tâche a bien été détruit.

Le fichier SECURITY.md du dépôt d’exemple le rappelle.

Checklist

  • Utilisez un runner GitLab autogéré, pas les runners SaaS partagés
  • Configurez l’exécuteur Docker avec privileged = false, un utilisateur non-root, aucun socket Docker et aucun volume hôte
  • Activez FF_NETWORK_PER_BUILD=1 pour isoler chaque tâche
  • Refusez toute sortie par défaut au niveau de l’hôte avec iptables/nftables ou un espace de noms réseau
  • Autorisez dans le proxy exactement deux hôtes et chemins : le modèle et l’hôte MCP de Kit
  • Validez le jeton propre à l’exécution et bloquez les sous-API de stockage du fournisseur
  • Le jeton est limité à un rapport et une heure ; joignez les preuves du pipeline avant d’envoyer l’unique résultat final ; le masquage n’est qu’une mesure d’hygiène
  • Exécutez le canari d’injection et confirmez le blocage de l’exfiltration
  • Épinglez une version récente de Claude Code ou du bac à sable, en défense supplémentaire

Pour aller plus loin

Tapez pour rechercher...