Virtualisation et agent IA
Présentation et lancement de la VM, présentation du fichier CLAUDE.md, connexion à la VM en SSH et lancement de Claude Code. Accéléré ×4.
En utilisant un agent IA au quotidien, Claude Code ou OpenCode, trois questions se posent, et elles ne se recouvrent pas :
- Quelles données partent vers le modèle, c’est-à-dire quelles données je dois considérer comme compromises.
- Que s’est-il passé sur ma machine pendant que l’agent travaillait.
- Que s’est-il passé sur le réseau.
Cet article traite la deuxième. Il touche la première par ricochet, et laisse la troisième de côté. Je me suis intéressé à faire travailler un agent IA dans une machine virtuelle, pour garder la mienne propre : être sûr qu’il ne s’y est rien passé que je n’aie fait moi-même.
Le sujet n’est pas nouveau et je n’invente rien. Plusieurs réponses existent, les conteneurs et les machines virtuelles au premier rang. Ce sont ces dernières que j’explore ici. Ce qui suit n’est donc pas une découverte : c’est un montage qui tourne chez moi, avec ce qu’il règle et ce qu’il ne règle pas.
Un mot sur ce choix, puisque les deux réponses coexistent. Un conteneur partage le noyau de la machine hôte ; une machine virtuelle a le sien, et un système complet autour. Le conteneur est plus léger et démarre en un instant ; la VM isole plus franchement et se comporte comme une vraie machine, ce qui compte quand l’agent doit installer un système entier pour faire son travail. C’est la raison de mon choix, avec un second usage en tête : lui confier des tâches lourdes, comme monter une base de données de bout en bout, sans que ça laisse la moindre trace chez moi.
Un agent IA dans une machine virtuelle jetable
Ce que fait un agent, et ce que je ne vois pas
J’utilise Claude Code et OpenCode tous les jours. Quel que soit le projet (site web, Supabase, PostgreSQL, Rust, LaTeX), l’agent teste lui-même ses propositions avant de me les soumettre. Il ajuste par boucles jusqu’à fournir un résultat qui fonctionne et qui correspond à ce que j’attends.
C’est précisément ce qui fait sa valeur, et c’est ce qui pose le problème. Pour tester, la machine qui fait tourner l’agent exécute des scripts, écrit des fichiers temporaires, installe ce qui lui manque, ouvre un navigateur pour vérifier une page. Chacune de ces actions est légitime, et c’est le prix du résultat. Prises ensemble, sur des dizaines de boucles par jour, elles forment une activité que je ne surveille pas.
Valider commande par commande n’est pas une réponse. Ou bien je lis tout, et je perds l’intérêt de l’outil ; ou bien je clique sans lire, et ma validation ne vaut rien. La question n’est donc pas de mieux surveiller l’agent, mais de choisir l’endroit où il travaille.
Une machine propre avant d’être jetable
Le mode jetable ne nettoie rien : il fige un état et y revient à chaque arrêt. Si cet état est douteux, il le reste. La première étape est donc une machine propre, tenue avec le même soin que la mienne.
J’ai une VM Debian ARM sous UTM (moteur QEMU) sur ma machine. Elle a deux régimes. En mode normal, je m’en occupe : mises à jour du système, installation de ce qui doit durer. En mode jetable, tout ce qui est écrit pendant la session disparaît à l’arrêt, et la machine repart de l’état que j’ai validé.
Ce que j’y ai installé tient en trois lignes :
- Claude Code, par le dépôt apt officiel d’Anthropic : il se met à jour avec le reste du système, sans geste particulier.
- OpenCode, par son script d’installation officiel.
- Un dossier partagé avec ma machine, monté au démarrage sur le bureau de la VM.
Puis j’ai écrit un fichier CLAUDE.md au niveau utilisateur, lu par les deux agents. Il leur dit trois choses : tu es dans une VM jetable, tu as tous les droits (sudo compris), et seul le dossier partagé persiste, c’est là que tu trouves le travail et que tu déposes le résultat.
Ce fichier ne restreint pas l’agent, il le renseigne : il lui dit où passe la frontière, pour qu’il dépose son travail du bon côté.
Au quotidien, je travaille avec cette VM comme avec n’importe quelle machine : je m’y connecte en SSH depuis la mienne, je me place dans le dossier partagé, et je lance l’agent là. C’est ce que montre la vidéo en tête d’article.
Le dossier partagé, et rien d’autre
Reste à choisir ce que je partage, avant de lancer la VM. C’est le seul geste qui demande une décision, et c’est celui qui commande tout le reste.
Je partage un dossier de projet, et l’agent y retrouve exactement ce qu’il trouverait chez moi : les mêmes fichiers, les mêmes instructions, le même contexte de travail. Rien ne change pour lui, et c’est voulu, sans quoi le montage ne servirait qu’à des démonstrations. Ce qui change, c’est que tout ce qu’il touche en dehors de ce dossier vit dans une machine qui n’existera plus à la fin de la session.
Lancer l’agent sans moi
La VM est propre et contient mes outils. Restait à la faire travailler sans que je sois devant.
J’ai ajouté un service au démarrage, dont la règle tient en une ligne : si un fichier consignes.md se trouve dans le dossier partagé, lance l’agent avec ce fichier comme prompt. Le fichier est renommé dès sa prise en charge, et porte alors la date et la marque de son traitement.
Mon geste se réduit alors à ceci : j’écris mes consignes, je démarre la VM en mode jetable, je ne touche plus à rien.
Pour l’éprouver, je lui ai demandé une base de clients PostgreSQL : un identifiant unique, un mot de passe haché par la base elle-même et jamais stocké en clair, vingt comptes de démonstration. La consigne insistait sur un point : ce sont les règles de la base qui doivent tenir, pas le script d’insertion. Il devait installer PostgreSQL, exécuter son script, puis prouver chaque règle par des requêtes (les vingt lignes en place et correctement liées entre les deux tables, aucun mot de passe lisible en clair, un mot de passe vérifiable après coup, un identifiant déjà pris refusé), corriger et recommencer jusqu’à ce que tout passe, et enfin rendre le script et un rapport.
La vidéo ci-dessous montre la séquence entière, vue de ma machine. Au départ, le dossier partagé ne contient que consignes.md, et la VM est arrêtée. Je la démarre en mode jetable, et le fichier de consignes est aussitôt renommé : l’agent l’a pris en charge. Ensuite, plus rien ne bouge à l’écran pendant qu’il travaille, la VM restant sur son écran de session. Elle s’éteint d’elle-même, et le dossier partagé porte alors deux fichiers de plus, creation.sql et rapport.md. La VM, elle, est déjà revenue à son état propre.
Accéléré ×4.
La fin de la vidéo ouvre ce rapport.md. On y lit comment il a tenu les deux règles : une contrainte d’unicité sur l’identifiant, et un hachage bcrypt posé par un trigger, donc appliqué par la base elle-même alors que le script d’insertion, lui, passe les mots de passe en clair. Suivent ses vérifications, toutes passées, sur le PostgreSQL 17.11 installé dans la VM.
Pendant la session, il était chez lui : il a installé ce qu’il lui fallait sans rien me demander. Ma machine, elle, n’a reçu ni PostgreSQL ni quoi que ce soit d’autre.
Ce que j’échange avec l’agent n’est alors plus une conversation. C’est un fichier en entrée, deux fichiers en sortie.
Plusieurs machines, et ce qui persiste ailleurs
Ce que je partage, ce qui persiste, et où
Ce que je fais avec une VM, je peux le faire avec plusieurs, dans la limite de la mémoire et des cœurs de la machine hôte.
Deux projets en même temps, chacun dans sa machine jetable. Ou deux tâches du même projet, sans qu’elles se marchent dessus : pas de dépendances communes, pas de port commun, pas d’état partagé. C’est une conséquence directe de l’isolement, et elle vaut à elle seule le détour.
Les machines peuvent aussi se parler. Dans une seconde démonstration, j’en ai monté deux : une Debian jetable, où travaille l’agent, et une OpenBSD persistante, qui garde ce qu’on lui confie. L’agent reçoit ses consignes dans le dossier partagé, se connecte à l’autre machine en SSH et y écrit un fichier. Sa VM s’éteint et disparaît ; le fichier, lui, est toujours là.
Écrire un fichier n’a aucun intérêt en soi. Ce que ça montre en a un : une machine jetable peut utiliser un service qui, lui, persiste ailleurs. Mettez une base PostgreSQL sur la seconde machine, et l’agent travaille sur des données qui lui survivent, sans que rien de tout cela ne touche la mienne.
C’est là que l’architecture devient un choix, et non une contrainte subie. Je décide ce que l’agent peut consulter, donc ce qu’il peut faire fuiter, et je décide ce qu’il peut casser.
Un cas concret : un site servi depuis une machine jetable
Les deux démonstrations précédentes restaient des exercices. La dernière est une tâche que j’aurais eue de toute façon.
J’ai demandé à l’agent de mettre en ligne, sur un serveur web, un site reprenant ma série de publications sur la virtualisation : comme s’il était sur Internet, mais dans une machine jetable.
Dans le dossier partagé, une copie des fichiers de la série (textes, visuels, vidéos) et un fichier consignes.md. Deux choses à faire : générer un site minimaliste pour parcourir les publications, puis installer et configurer le serveur web Caddy pour que je puisse le consulter depuis mon navigateur. Et une consigne qui renverse la précédente : ne pas éteindre la machine, le site devant rester en ligne le temps que je le parcoure.
L’agent a tout fait seul. Il a écrit un script Python qui lit la série sans la modifier et produit le site. Il a installé Caddy, vérifié chaque page, chaque image et chaque vidéo par des requêtes sur l’adresse IP de la VM et non sur localhost, puis contrôlé le rendu dans un navigateur sans interface. La vidéo suit toute la chaîne : elle passe par le site ouvert dans le navigateur de ma machine, à l’adresse de la VM, et finit sur le rapport.
Accéléré ×4.
Tout finit dans un rapport.md : l’adresse à ouvrir, ses choix, ses vérifications et les problèmes rencontrés. Un exemple, qui dit bien ce que permet ce cadre : mes vidéos étaient en .mov, il les a remultiplexées en .mp4 sans réencodage pour qu’elles se lisent de façon fiable dans un navigateur, en laissant les originaux intacts comme la consigne l’exigeait. Décision prise, exécutée et documentée, sans que j’aie eu à l’arbitrer.
J’ai ouvert l’adresse, parcouru le site, lancé les vidéos. Puis j’ai éteint la VM, et le site avec elle : le serveur, ses fichiers, sa configuration, rien n’a survécu et rien n’est resté chez moi. Si j’avais voulu garder le site, il suffisait de le demander dans les consignes : il l’aurait déposé dans le dossier partagé.
Ce que ça règle, et ce que ça ne règle pas
Le gain est net sur la deuxième question. L’agent a tous les droits, sudo compris, sans validation commande par commande, et je n’ai plus à me demander ce qu’il a laissé derrière lui : à l’arrêt, la machine revient à son état propre. « Que s’est-il passé sur ma machine ? » reçoit une réponse vérifiable : rien.
Sur la première, il faut être précis, parce que c’est là qu’on se raconte facilement des histoires. Une machine virtuelle ne protège aucune donnée. Ce que l’agent lit dans le dossier partagé est exactement aussi exposé que s’il l’avait lu chez moi : il le transmet au modèle, et rien dans le montage ne l’en empêche. Anthropic l’écrit noir sur blanc, à propos de ses conteneurs de développement :
« When executed with
--dangerously-skip-permissions, dev containers do not prevent a malicious project from exfiltrating anything accessible inside the container, including the Claude Code credentials stored in~/.claude. »— Documentation de Claude Code, « Development containers »
Une VM ne fait pas mieux sur ce point, et pour la même raison : le canal de sortie n’est pas une faille de l’isolement, c’est l’agent lui-même. Un fichier sensible déposé dans le dossier partagé est un fichier sensible compromis, et le fait qu’il ait transité par une VM n’y change rien.
Ce que la virtualisation déplace, c’est le périmètre, pas la protection. Avant, l’agent travaillait au milieu de tout ce que contient ma machine, et je n’aurais pas su dire ce qu’il avait lu. Maintenant, ce qui est exposé est exactement le contenu d’un dossier que je désigne, avant de démarrer. La question « quelles données sont compromises ? » a donc une réponse, et elle est courte. C’est un vrai gain, et il s’arrête là : ce qui ne doit pas être lu ne se met pas dans le dossier partagé, c’est la seule règle qui tienne.
La frontière joue dans l’autre sens aussi : ce que l’agent écrit dans ce dossier arrive directement chez moi, sans filtre.
La troisième question, le réseau, je ne l’ai pas traitée. Une VM y offre pourtant une prise : on peut lui couper l’accès, le filtrer, ou l’observer depuis l’extérieur.
Ce que je retiens
La virtualisation n’a rien de nouveau ni de sophistiqué : une VM, un dossier partagé, un fichier de consignes, un service au démarrage. Le problème qu’elle traite, lui, mérite d’être pris au sérieux.
Plus j’expérimente, plus je me rends compte qu’un agent doit travailler dans un espace fermé et conçu pour lui. Il installe, il lance, il écrit, et il accède à tout ce qui est à sa portée. Sur ma machine, je peux m’en accommoder. Dans un cadre professionnel, où les données ne m’appartiennent pas, ça ne tient plus.
Reste le réseau, et c’est là que le sujet est à creuser. Un agent consulte ce qu’il veut et envoie ce qu’il veut, où il veut. Tant que cette sortie n’est pas cadrée, le périmètre n’est fermé qu’à moitié.
Une frontière doit s’établir structurellement et matériellement. Elle ne peut pas reposer sur la confiance accordée à l’agent, ni sur une surveillance que personne ne peut tenir seul.