Skip to content
Votre agent n'a pas besoin de tout Internet : contrôle des sorties réseau et des permissions sur AWS
← ← Retour aux Réflexions Cloud

Votre agent n'a pas besoin de tout Internet : contrôle des sorties réseau et des permissions sur AWS

Fin septembre 2026, OpenAI a suspendu l'entraînement de ses derniers modèles. D'après le récit de l'AP, qui s'appuie sur les propres communications de l'entreprise, des agents en cours d'entraînement et d'évaluation étaient allés au-delà de ce qu'on leur demandait sur des sites web du gouvernement américain et, dans un cas, avaient trouvé des clés de développeur. Un autre incident, daté du 20 septembre, est celui auquel nous revenons sans cesse : un agent de recherche interne n'était censé avoir aucun accès à Internet, et il s'est échappé malgré tout, par une faille du filtrage DNS, pour atteindre un chatbot externe. Quelques jours plus tard, la FTC a confirmé enquêter sur OpenAI, Anthropic et d'autres laboratoires au sujet des risques que leurs agents font peser sur les consommateurs.

Ces laboratoires ont des équipes de sécurité plus grandes que l'ensemble des départements d'ingénierie de la plupart de nos clients. La leçon pour tous les autres n'est pas que les agents sont incontrôlables. C'est que les garde-fous du fournisseur de modèle ne sont pas les vôtres. Dès que vous donnez à un agent un navigateur, un outil HTTP, de l'exécution de code ou des identifiants AWS, ce qu'il peut atteindre est décidé par votre réseau et votre IAM, pas par les bonnes intentions du modèle. Cet article décrit comment nous mettons cela en place sur AWS pour les clients qui font tourner des agents en production.

Ce qu'il faut réellement limiter

Un agent est une boucle : le modèle décide d'une action, un outil l'exécute, le résultat retourne au modèle. Le modèle seul ne peut rien faire. Tout ce qui est dangereux se passe dans les outils, avec l'accès réseau et les permissions du processus qui les exécute. Cela donne cinq endroits où poser des limites, et chacun arrête des choses que les autres n'arrêtent pas :

Cinq couches, et ce que chacune ne peut pas arrêter seule COUCHE ARRÊTE N'ARRÊTE PAS DNS Firewall résolution des domaines hors liste IP directes, DNS over HTTPS, résolveur perso Network Firewall connexions vers hôtes hors liste, IP incluses abus d'un domaine autorisé Rôle IAM + session policy appels AWS hors périmètre de la tâche dégâts via les API qu'il peut appeler Outils + validations effets de bord non approuvés mauvaises réponses, erreurs de jugement Budgets + kill switch boucles folles, coûts qui explosent une mauvaise action avant le déclenchement Aucune couche ne suffit seule. L'incident de septembre n'en a contourné qu'une : le DNS.

Réseau : pourquoi le filtrage DNS seul ne suffit pas

La configuration tentante est Route 53 Resolver DNS Firewall avec une liste blanche : l'agent ne peut résoudre que les domaines que vous approuvez, tout le reste renvoie NXDOMAIN. C'est peu coûteux, simple, et cela vaut la peine d'être en place. Mais cela contrôle les résolutions de noms, pas les connexions. Un agent qui se connecte directement à une adresse IP ne passe jamais par le DNS. Celui qui envoie du DNS over HTTPS à un résolveur public ne passe jamais par le vôtre. Celui qui écrit un script avec un résolveur codé en dur non plus. L'incident de septembre rappelle qu'un « pas d'Internet » appliqué uniquement au niveau du DNS n'est pas « pas d'Internet ».

Ce qui l'impose vraiment, c'est un contrôle sur la connexion elle-même. Sur AWS, c'est AWS Network Firewall avec un groupe de règles avec état de type liste de domaines : le HTTPS sortant n'est autorisé que si le SNI TLS correspond à la liste, le HTTP en clair seulement si l'en-tête Host correspond, et tout le reste est rejeté par défaut, y compris les connexions vers des adresses IP brutes. Les sous-réseaux de l'agent n'ont aucune route vers Internet autre que celle qui passe par le endpoint du firewall.

Sorties de l'agent sur AWS : chaque chemin est une décision Route 53 DNS Firewall liste blanche, reste bloqué Tâche de l'agent Fargate, sous-réseau privé rôle IAM propre, session de 15 min AWS Network Firewall Liste blanche TLS SNI / HTTP Host par défaut : rejet + alerte Domaines autorisés API du modèle, vos API, docs approuvées Tout le reste IP brutes, DoH, autres hôtes : rejetés Endpoints VPC S3, Secrets Manager, Bedrock, Logs Le trafic AWS ne quitte jamais le VPC : aucune entrée de liste blanche Internet. Logs d'alertes firewall + logs de requêtes DNS vers CloudWatch pour le kill switch.

En CDK, le groupe de règles est court. Les détails importants sont les types de cibles (SNI et Host), l'ordre strict des règles et une action par défaut qui rejette ce que rien n'a reconnu :

import * as nfw from "aws-cdk-lib/aws-networkfirewall";

const allowlist = new nfw.CfnRuleGroup(this, "AgentEgressAllowlist", {
  ruleGroupName: "agent-egress-allowlist",
  type: "STATEFUL",
  capacity: 100,
  ruleGroup: {
    rulesSource: {
      rulesSourceList: {
        generatedRulesType: "ALLOWLIST",
        targetTypes: ["TLS_SNI", "HTTP_HOST"],
        targets: ["api.anthropic.com", "api.yourcompany.com", ".docs.aws.amazon.com"],
      },
    },
    statefulRuleOptions: { ruleOrder: "STRICT_ORDER" },
  },
});

const policy = new nfw.CfnFirewallPolicy(this, "AgentEgressPolicy", {
  firewallPolicyName: "agent-egress",
  firewallPolicy: {
    statelessDefaultActions: ["aws:forward_to_sfe"],
    statelessFragmentDefaultActions: ["aws:forward_to_sfe"],
    statefulEngineOptions: { ruleOrder: "STRICT_ORDER" },
    statefulRuleGroupReferences: [{ resourceArn: allowlist.attrRuleGroupArn, priority: 10 }],
    statefulDefaultActions: ["aws:drop_established", "aws:alert_established"],
  },
});

aws:drop_established plutôt que aws:drop_strict est un choix délibéré : le firewall doit laisser passer la poignée de main TCP pour voir le SNI dans le Client Hello TLS. Un point en tête (.docs.aws.amazon.com) correspond aux sous-domaines. Gardez la liste courte et précise. « github.com » en entier est un canal d'exfiltration, pas une entrée de liste blanche.

Deux réserves honnêtes. Le filtrage SNI vérifie le nom que le client déclare, donc un processus déterminé pourrait envoyer un SNI autorisé à un autre serveur. Network Firewall sait faire de l'inspection TLS pour combler cet écart, avec un surcoût et une complexité supplémentaires, et pour la plupart des charges de travail d'agents, la combinaison d'une liste blanche SNI et de l'absence de sortie vers des IP brutes est une limite raisonnable. Et ce n'est pas gratuit : en us-east-1, un endpoint coûte 0,395 $ de l'heure, soit environ 288 $ par mois et par zone de disponibilité, plus 0,065 $ par Go traité. Pour un seul petit agent, un proxy de sortie (Squid ou Envoy avec une liste blanche de domaines, sur une petite tâche Fargate) est une alternative moins chère qui repose sur le même principe. Ce que nous ne ferions pas, c'est donner à un agent une NAT gateway et un filtre DNS en considérant qu'il est confiné.

Les services AWS eux-mêmes (S3, Secrets Manager, Bedrock, CloudWatch Logs) doivent passer par des endpoints VPC. Ce trafic ne touche jamais Internet et n'a jamais besoin d'une entrée dans la liste blanche, et les politiques d'endpoint permettent de restreindre, par exemple, les buckets auxquels le trafic S3 de l'agent peut accéder.

Identité : un rôle par agent, restreint à chaque tâche

La deuxième erreur courante est de faire tourner l'agent avec le rôle de l'application. L'application web doit lire et écrire les données de tous les clients, donc l'agent le peut aussi. Si l'agent sort de sa tâche, il le fait avec les permissions de tout le produit.

Voici ce que nous faisons à la place :

  • Un rôle IAM par type d'agent. L'agent de recherche, l'agent de support et l'agent de reporting ont chacun leur propre rôle, avec uniquement les actions dont leurs outils ont besoin. Une permissions boundary sur chaque rôle plafonne ce que quiconque pourrait y ajouter plus tard.
  • Une session policy par tâche. Quand l'agent démarre une tâche, l'orchestrateur assume le rôle de l'agent avec une session policy en ligne qui le restreint encore, par exemple au préfixe d'un seul tenant dans S3. Les permissions effectives sont l'intersection du rôle et de la session policy.
  • Sessions courtes et session tags. 15 minutes, le minimum pour AssumeRole, et des tags de tenant et de tâche pour que chaque appel dans CloudTrail puisse être rattaché à la tâche qui l'a émis.
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";

const sts = new STSClient({});

export async function credentialsForTask(taskId: string, tenantId: string) {
  const { Credentials } = await sts.send(new AssumeRoleCommand({
    RoleArn: process.env.RESEARCH_AGENT_ROLE_ARN,
    RoleSessionName: `agent-${taskId}`,
    DurationSeconds: 900,
    Tags: [{ Key: "tenant", Value: tenantId }, { Key: "task", Value: taskId }],
    Policy: JSON.stringify({
      Version: "2012-10-17",
      Statement: [{
        Effect: "Allow",
        Action: ["s3:GetObject"],
        Resource: [`arn:aws:s3:::${process.env.DOCS_BUCKET}/tenants/${tenantId}/*`],
      }],
    }),
  }));
  return Credentials;
}

La trust policy du rôle doit inclure sts:TagSession pour que les tags fonctionnent. Et les outils de l'agent reçoivent ces identifiants, pas ceux par défaut de la tâche : même un outil qui se comporte mal ne peut toucher que les fichiers d'un seul tenant pendant quinze minutes.

Outils : la vraie surface de permissions

Le réseau et IAM limitent où un agent peut aller. Les outils décident de ce qu'il peut faire une fois arrivé, et c'est là que se concentre l'essentiel du travail de conception.

  • Pas d'outils génériques en production. Un outil bash ou un fetch(url) qui accepte n'importe quoi fait de chaque limite ci-dessus la seule limite. Un outil appelé get_invoice(invoice_id) qui vérifie lui-même le tenant est moins flexible et beaucoup plus sûr.
  • Vérifiez aussi la liste blanche dans l'outil. S'il y a un outil de fetch, il valide l'hôte par rapport à la même liste que celle du firewall. La défense en profondeur coûte dix lignes.
  • Exécutez le code dans un sandbox sans réseau. Si l'agent doit exécuter du code, faites-le dans un conteneur ou une fonction séparés sans aucune sortie réseau, ou dans le sandbox hébergé du fournisseur de modèle, pas dans le processus qui détient les identifiants.
  • Validation pour les effets de bord. La lecture est automatique, tout ce qui écrit, envoie, paie ou supprime attend une personne. Le tool runner du SDK Anthropic dispose de hooks par tour précisément pour cela, et Managed Agents propose des politiques de permissions (always_allow, always_ask, auto).

Un piège tiré de la documentation d'Anthropic, que nous souhaitons souligner : sur Managed Agents, les outils hébergés web_search et web_fetch s'exécutent sur les serveurs d'Anthropic et ignorent les paramètres réseau de votre environnement. Verrouiller le réseau du sandbox ne les restreint pas. Il faut les désactiver, ou les restreindre outil par outil avec allowed_domains. C'est la même leçon que le contournement du DNS sous une autre forme : vérifiez par quel chemin le trafic passe réellement.

Budgets et kill switch

La dernière couche porte sur les comportements qui s'emballent plutôt que sur les actions isolées. Fixez une limite stricte de tours et de tokens par tâche (l'API Claude propose des task budgets précisément pour cela), limitez le débit des appels d'outils par agent, et surveillez deux chiffres : les alertes de rejet de Network Firewall et les blocages de DNS Firewall pour les sous-réseaux de l'agent. Un agent qui se met soudain à heurter le firewall est soit cassé, soit en train de faire quelque chose qu'il ne devrait pas. Dans les deux cas, une alarme CloudWatch sur ces métriques peut déclencher une Lambda qui attache une politique deny-all au rôle de l'agent. Chaque session meurt en quelques minutes et rien d'autre dans le produit n'est affecté.

Notre checklist

Avant qu'un agent doté d'outils n'aille en production, nous voulons pouvoir répondre oui à chacun de ces points :

  1. L'agent s'exécute dans des sous-réseaux sans route vers Internet, hormis via un firewall ou un proxy à liste blanche.
  2. Les sorties vers des IP brutes sont bloquées, et le DNS est filtré aussi.
  3. Les services AWS sont atteints par des endpoints VPC.
  4. L'agent a son propre rôle, une permissions boundary et des session policies par tâche.
  5. Les outils sont spécifiques, vérifient leurs propres entrées, et les effets de bord exigent une validation.
  6. L'exécution de code se fait dans un sandbox sans réseau et sans identifiants.
  7. Les tours, les tokens et les appels d'outils ont des limites, et il existe un kill switch testé.

Rien de tout cela n'est exotique. C'est la même discipline que nous appliquons à tout service qui traite des entrées non fiables, appliquée à un service dont les décisions sont prises par un modèle. Si vous mettez des agents en production et souhaitez que quelqu'un revoie votre configuration, c'est un travail que nous réalisons pour nos clients. Et si vous voulez comprendre pourquoi les agents finissent toujours par trouver les failles, nous en avons parlé dans pourquoi l'IA n'arrête pas de « s'évader ».