Bonjour à tous, après avoir utilisé Codex en Remote SSH pour moderniser mon système de backup, j’avais envie d’aller un cran plus loin : retrouver VS Code Web sur mon serveur, directement dans un navigateur, avec le même workspace et un Codex capable d’investiguer sur la machine. Bref, comme à la maison, mais en version Web, depuis n’importe où.
Le besoin est assez concret. Depuis mon PC personnel, le client lourd VS Code et Remote SSH font parfaitement le travail. Depuis un PC professionnel (ou en nomadisme) sur lequel je ne veux pas, ou ne peux pas, installer toute ma configuration, je voulais simplement ouvrir une URL, passer l’authentification et retrouver mon environnement de dev sur le serveur.
Sur le papier, cela ressemble à une stack Portainer de plus. J’avais déjà Docker, un reverse proxy, un réseau de conteneurs et l’habitude de déployer des services web avec Compose. Il suffisait donc de trouver une image contenant VS Code et Codex, de monter mon workspace et de cliquer sur Deploy the stack.
Évidemment, si cela s’était passé comme ça, cet article ferait huit lignes au lieu de frôler les 5000 mots… Entre le bureau Kasm qui ne démarrait pas, l’image qui ouvrait Codex en ligne de commande au lieu de VS Code, le presse-papiers VNC franchement pénible, une extension Remote SSH indisponible dans le navigateur et l’isolation des conteneurs. Mon petit TP s’est transformé en revue d’architecture complète. Le résultat final fonctionne exactement comme je le voulais, mais la bonne solution a consisté à sortir VS Code de Docker tout en gardant Nginx Proxy Manager devant.
Le cahier des charges : un navigateur, mais le vrai serveur derrière
Je ne cherchais pas un éditeur de texte en ligne posé à côté de mon serveur. Je voulais retrouver la boucle de travail qui m’avait convaincu avec Remote SSH et Codex dans mon VSCode à la maison:
- ouvrir les fichiers réellement présents sur la machine ;
- utiliser un terminal exécuté dans le même environnement ;
- inspecter les services systemd, Docker, le réseau et les journaux ;
- faire analyser ces éléments par Codex sans lui décrire l’infrastructure de mémoire ;
- conserver une validation humaine avant les opérations sensibles ;
- accéder à l’ensemble depuis un navigateur moderne, derrière mon HTTPS existant.
Le dernier point était le plus visible, mais le troisième était le plus important. Un VS Code très joli enfermé dans un conteneur ne peut pas administrer proprement son hôte. On peut lui monter quelques fichiers, le socket Docker et des morceaux du système, voire le rendre privilégié. Comme on peut démonter sa porte d’entrée à la maison parce qu’on trouve l’usage de la clé contraignant, hein. Je voulais donc une interface Web, pas un faux environnement distant. Cette distinction explique pratiquement tous les choix qui suivent.
Premier essai : une stack Kasm dans Portainer
La première piste reposait sur une image Kasm contenant déjà Codex. C’était séduisant : pas de Dockerfile personnalisé, quelques volumes persistants, un raccordement au réseau externe de Nginx Proxy Manager et le port web uniquement exposé à l’intérieur de Docker.
La stack conservait séparément la configuration de l’éditeur, les clés SSH, les données Codex et le workspace. Nginx Proxy Manager terminait le HTTPS et appliquait sa liste d’accès en Basic Auth. Aucun port n’était publié directement sur Internet.
Mais ce bureau distant restait une session graphique transposée dans le navigateur. Le presse-papiers et certaines interactions passaient par la couche Kasm / VNC. Cela fonctionne, mais ce n’est pas la même sensation qu’une application Web native (i.e c’est complètement inutilisable en dehors d’une démo). Pour copier trois lignes de YAML, on doit négocier avec une petite fenêtre de presse-papiers comme en 2008.
Le faux succès : Codex fonctionne, mais VS Code a disparu
Puis j’ai regardé l’écran une seconde fois : j’avais demandé VS Code dans mon navigateur, pas une interface Codex en ligne de commande dans un bureau distant.
L’image choisie faisait ce qu’annonçait son nom. Elle fournissait Codex CLI dans l’environnement Kasm. La présence d’un binaire VS Code dans l’image ne signifiait pas que le conteneur lançait automatiquement l’éditeur souhaité. C’est un piège très classique avec les images riches : elles contiennent dix outils, mais leur point d’entrée n’en démarre qu’un.
Nous avons donc arrêté de vouloir lancer le bureau complet et démarré directement la version web native de VS Code grâce à la commande code serve-web déjà présente dans l’image :
services:
vscode-codex:
image: kasmweb/codex-cli:<version-testée>
entrypoint: ["/usr/bin/code", "serve-web"]
command:
- --host
- 0.0.0.0
- --port
- "8000"
- --without-connection-token
- --accept-server-license-terms
- --default-folder
- /home/kasm-user/workspace
- --disable-telemetryCette fois, plus de bureau XFCE, plus de VNC et plus de presse-papiers intermédiaire. Le navigateur affiche directement le client web de VS Code. La saisie, les raccourcis et les copier-coller redeviennent ceux d’une application web normale.
L’option qui supprime le jeton de connexion mérite une grosse astérisque. L’aide de la commande précise qu’elle ne doit être utilisée que si la connexion est sécurisée autrement. Dans mon cas, ce rôle est assuré par Nginx Proxy Manager, HTTPS, la Basic Auth et un filtrage réseau dédié. Sans ces couches, cette option reviendrait à publier un terminal d’administration avec un joli éditeur autour.
Puis j’ai tenté d’installer Remote SSH dans VS Code Web.
Remote SSH n’est pas disponible dans VS Code Web, et c’est logique
Le Marketplace a répondu que l’extension Remote SSH n’était pas disponible dans Visual Studio Code for the Web. Cette limitation du client Web est d’ailleurs documentée : toutes les extensions de bureau ne peuvent pas fonctionner dans un client exécuté dans le navigateur, notamment lorsqu’elles dépendent d’API ou de composants locaux absents du Web.
Ma première réaction a été de trouver cela à moitié gênant. Après tout, le conteneur était déjà sur le serveur cible. Pourquoi établir une connexion SSH vers la machine sur laquelle il s’exécutait ?
Parce que le conteneur n’est justement pas la machine.
Le terminal intégré voyait son système de fichiers, ses processus et ses outils. Le bind mount donnait accès au workspace, mais pas à systemd, au firewall de l’hôte, à tous les journaux, aux interfaces réseau ou aux autres fichiers système. Codex pouvait très bien analyser le projet, mais il ne pouvait pas confirmer pourquoi un service hôte ne démarrait plus sans qu’on lui ouvre progressivement davantage de portes.
Les options existaient, mais aucune ne me plaisait :
- monter le socket Docker donne pratiquement le contrôle root de l’hôte ;
- multiplier les bind mounts sensibles recrée une vue partielle et fragile du système ;
- utiliser le réseau hôte ne donne pas accès à systemd ni aux fichiers ;
- passer le conteneur en mode privilégié supprime l’essentiel de l’isolation que Docker devait apporter ;
- installer et gérer un serveur SSH à l’intérieur du conteneur ajoute une boucle absurde.
Le conteneur était une excellente frontière pour un environnement de développement isolé. Il devenait la mauvaise frontière pour un outil dont l’objectif était précisément d’administrer l’hôte.
Quand l’outil doit observer le système « live », lui donner un contexte soit disant isolé qui débraye la plupart des sécurités, est souvent plus dangereux que l’exécuter dans un environnement privilégié mais correctement sécurisé.
L’architecture finale : VS Code Web et Codex sur l’hôte
La solution est finalement plus simple que la stack initiale :
- VS Code Web s’exécute directement sur le serveur avec mon utilisateur standard ;
- le processus écoute uniquement sur l’adresse de l’hôte dans le bridge Docker du reverse proxy ;
- Nginx Proxy Manager reste le seul composant accessible depuis Internet ;
- le firewall n’autorise le port interne que depuis le conteneur du proxy ;
- Codex travaille sur l’hôte avec les permissions de mon compte et une élévation explicitement contrôlée ;
- l’ancienne stack reste disponible pendant la validation comme solution de rollback.
Le chemin réel comporte deux connexions. Le navigateur arrive en HTTPS sur l’hôte, où le trafic public est filtré puis dirigé par Docker vers Nginx Proxy Manager. Le proxy ouvre une nouvelle vers l’adresse de l’hôte sur le bridge Docker. Cette seconde connexion va atteindre VS Code Web (Aucun port VS Code n’est publié sur l’interface Internet)
Le schéma ci-dessous détaille cette séparation. Nginx Proxy Manager et les autres applications Web restent dans leur bloc Docker. VS Code Web, le terminal et Codex se trouvent à côté de ce bloc mais dans le même périmètre hôte, là où leurs observations ont un sens : sur la machine réelle, sous un compte non root.
Codex est enfin là où se trouve le problème
Une fois VS Code Web exécuté sur l’hôte, le terminal intégré et Codex travaillent dans l’environnement système. La documentation de Codex CLI décrit précisément cette boucle : inspecter les fichiers, effectuer des changements et lancer les outils installés sur la machine, avec des permissions choisies pour la tâche.
Pour le prototype, nous avons réutilisé le binaire déjà fourni par l’extension Codex installée sur le serveur et ajouté un petit lanceur qui sélectionnait sa version la plus récente. Cela fonctionne, mais ce n’est pas une méthode d’installation que je conseillerais dans un tutoriel durable : l’organisation interne d’une extension peut changer. Le CLI officiel doit plutôt être installé pour l’utilisateur selon la documentation OpenAI, puis contrôlé avec codex –version. Son répertoire d’authentification reste privé, avec des permissions restrictives, et n’est jamais servi par Nginx.
Dans mon cas, cela permet de confronter un script avec les conteneurs actifs, une unité avec systemd, une règle candidate pour le firewall réel ou un inventaire des fichiers. C’est la continuité directe de mon usage en Remote SSH, mais accessible depuis un navigateur.
Le prompt d’amorçage
Cette puissance ne doit pas être confondue avec une permission générale. Un Codex sur l’hôte peut voir davantage de faits, mais une mauvaise commande peut aussi toucher davantage de choses, et… tout péter… version Skynet ! Pour la première conversation, nous avons donc préparé un prompt d’amorçage structuré autour de six éléments :
- le contexte connu du serveur, présenté comme une information à vérifier ;
- l’objectif d’établir ses capacités réelles ;
- un style d’administration prudent et factuel ;
- un ton direct qui signale les incertitudes ;
- le niveau technique de l’utilisateur ;
- un format de réponse imposant preuves, écarts et conclusion.
La première mission de la nouvelle session est strictement en lecture seule. Elle doit déterminer si elle se trouve sur l’hôte, dans un conteneur ou dans un bac à sable, puis vérifier son accès à systemd, Docker, au réseau, au firewall et à sudo. Elle ne doit ni afficher les secrets ni interpréter une capacité root comme une autorisation permanente.
Commence par un audit en lecture seule.
Vérifie où tu t’exécutes réellement.
Distingue ce que tu peux lire, inspecter avec sudo et modifier.
Confirme chaque fait important avec une preuve courte.
N’affiche aucun secret.
Ne change rien pendant cette première phase.Ce prompt ne constitue pas une sandbox. Il évite simplement de commencer une nouvelle session avec deux hypothèses dangereuses : « je suis forcément sur l’hôte » et « si sudo fonctionne, l’utilisateur voulait certainement que je l’utilise partout ».
Le même raisonnement s’applique à un compte autorisé à utiliser sudo sans mot de passe. C’est confortable pour l’automatisation, mais la demande de confirmation de l’agent devient alors une barrière applicative, pas une protection du système d’exploitation. Un compte dédié et des règles sudo limitées aux opérations réellement nécessaires offrent une frontière plus solide. Je préfère cette limite plutôt que laisser croire qu’un bon prompt remplace un modèle de permissions.
La frontière de licence et de support de VS Code Server
Il faut parler d’un point moins amusant, mais indispensable avant de transformer ce retour d’expérience en recette universelle.
La documentation officielle de VS Code Server indique que le produit est conçu pour un seul utilisateur. Sur ce point, c’est bon, je suis seul sur ce serveur. La même FAQ indique en revanche que l’hébergement du serveur « comme un service » n’est pas autorisé. Là, c’est moins clair: mon accès reste privé et personnel, mais systemd lance un service (au sens OS/technique en tout cas), mais en parallèle, la FAQ Remote Development distingue les usages internes ou privés des offres publiques ou commerciales destinées à d’autres utilisateurs. Enfin, Microsoft documente officiellement l’installation de Remote Tunnels comme service.
Je ne vais pas prétendre résoudre ici la nuance juridique entre un processus systemd personnel, une instance interne et une offre de service. Mon usage est privé, personnel, mono-utilisateur et placé derrière une authentification. Je n’en fais pas une plateforme proposée à des tiers. Cela ne transforme pas pour autant serve-web en produit d’hébergement officiellement garanti pour tous les scénarios.
Avant de reproduire cette architecture, relisez donc les conditions applicables à la version que vous téléchargez. Pour un contexte d’entreprise, multi-utilisateur ou commercial, c’est mort sans validation : voyez le sujet avec votre service juridique ou votre commercial Microsoft au lieu de citer mon blog avec beaucoup d’assurance.
Autres pistes pour faire du VS Code en remote
Trois alternatives méritent d’être comparées :
- Remote SSH reste le chemin le plus classique pour un client VS Code lourd et donne l’expérience la plus complète ;
- Remote Tunnels est le chemin officiellement documenté pour accéder au serveur depuis vscode.dev, avec authentification Microsoft ou GitHub et un tunnel sortant Azure, mais sans votre reverse proxy personnel ;
- code-server ou OpenVSCode Server répondent plus directement au besoin de self-hosting Web avec des licences libres, mais la compatibilité du Marketplace et des extensions propriétaires doit être testée, notamment pour l’intégration Codex souhaitée.
Le choix ne se réduit donc pas à « cela démarre ». Il faut décider qui maîtrise le point d’entrée, où passe l’authentification, quelles extensions sont indispensables et sous quelles conditions le logiciel peut être utilisé.
Bref, on verra si Microsoft vient me tirer les oreilles (ou me féliciter 🙂 ). En attendant, la limite que je perçoit est écrite noir sur blanc ici.
How-to : reproduire ce montage chez vous
Il vous encore le chemin complet pour le faire ça chez vous. Voici donc la version reproductible, avec des valeurs génériques. Ne recopiez pas une adresse, une interface ou un nom de conteneur depuis une capture : relevez-les sur votre propre serveur.
Une limite doit être posée avant la première commande (voir le § au dessus). La FAQ Microsoft indique actuellement qu’une instance VS Code Server est mono-utilisateur et que son hébergement « comme un service » n’est pas autorisé par sa licence. Mon montage est privé, personnel et expérimental, mais un processus systemd pourrait être interprété comme un service (avec les juristes on ne sait jamais…). Ne transformez pas ce retour d’expérience en validation juridique…
Étape 1 : vérifier les prérequis et préparer la marche arrière
Ce How-to suppose que vous disposez déjà :
- d’un hôte Linux compatible avec les prérequis de VS Code, administré par systemd ;
- d’un utilisateur standard dédié au travail dans VScode, avec un workspace existant et des droits maîtrisés ;
- de Docker et de Nginx Proxy Manager fonctionnels sur un bridge défini par l’utilisateur ;
- d’un nom DNS, d’un certificat HTTPS et les Access List Nginx Proxy Manager ;
- d’un accès SSH de secours, idéalement complété par la console du fournisseur ;
- d’un mécanisme connu pour sauvegarder, tester et restaurer les règles du firewall.
Sauvegardez également vos systemd, la configuration persistante du firewall et la fiche du Proxy Host avant de vous lancer. Le rollback doit être prêt avant le premier test, pas imaginé lorsque le navigateur ne répondra plus.
Étape 2 : relever le réseau Docker au lieu de l’inventer
Il faut identifier quatre valeurs : le réseau auquel Nginx Proxy Manager est connecté, sa passerelle vue depuis l’hôte, l’interface bridge correspondante et l’adresse actuelle du conteneur du proxy. Les commandes suivantes permettent de les retrouver sans publier mes identifiants :
docker network ls
docker ps --format 'table {{.Names}}\t{{.Networks}}'
docker network inspect <réseau-du-proxy> \
--format 'passerelle={{(index .IPAM.Config 0).Gateway}} id={{.Id}} options={{json .Options}}'
docker inspect <conteneur-npm> \
--format '{{range $name, $config := .NetworkSettings.Networks}}{{$name}}={{$config.IPAddress}} {{end}}'
ip -brief addressVérifiez que l’adresse du proxy restera stable avant de l’utiliser dans une règle source en /32.
Étape 3 : installer VS Code CLI et lancer VS Code Web avec systemd
Le client VS Code lourd avait déjà installé son backend lors de mes connexions Remote SSH. Nous avons réutilisé le CLI disponible sur la machine pour valider le principe, puis conservé une copie stable afin qu’une mise à jour ou un nettoyage de Remote SSH ne casse pas le chemin de l’unité systemd.
Pour une installation reproductible, partez plutôt du CLI autonome documenté par Microsoft et contrôlez sa version. Ne faites pas pointer un service de démarrage vers le premier binaire trouvé dans un cache portant un identifiant de build. L’exemple suivant vise une machine Linux x86-64 ; choisissez l’archive adaptée sur la page officielle si votre serveur utilise une autre architecture.
uname -m
VSCODE_TMP="$(mktemp -d)"
curl -fL 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' \
--output "$VSCODE_TMP/vscode_cli.tar.gz"
tar -tf "$VSCODE_TMP/vscode_cli.tar.gz"
tar -xf "$VSCODE_TMP/vscode_cli.tar.gz" -C "$VSCODE_TMP"
"$VSCODE_TMP/code" --version
sudo install -D -m 0755 "$VSCODE_TMP/code" /opt/vscode-cli/code
/opt/vscode-cli/code --versionLe CLI récupère ensuite les composants serveur nécessaires ; une copie stable du lanceur ne fige donc pas automatiquement toute la chaîne. Si vous exigez une reproductibilité stricte, organisez la mise à niveau et étudiez l’option de sélection d’un commit proposée par le CLI.
L’unité utilisée ressemble à ceci, avec des chemins et adresses génériques :
[Unit]
Description=Visual Studio Code Web on the Docker proxy bridge
Wants=network-online.target
After=network-online.target docker.service
[Service]
Type=simple
User=<utilisateur-standard>
Group=<groupe-standard>
WorkingDirectory=/srv/mon-workspace
Environment=HOME=/home/<utilisateur-standard>
Environment=PATH=/home/<utilisateur-standard>/.local/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/opt/vscode-cli/code serve-web \
--host <adresse-hote-du-bridge> \
--port 8000 \
--without-connection-token \
--accept-server-license-terms \
--server-data-dir /home/<utilisateur-standard>/.local/share/vscode-web/server \
--cli-data-dir /home/<utilisateur-standard>/.vscode-server/cli \
--default-folder /srv/mon-workspace \
--disable-telemetry
Restart=on-failure
RestartSec=5s
KillSignal=SIGINT
TimeoutStopSec=30s
UMask=0027
[Install]
WantedBy=multi-user.targetTrois détails comptent davantage que le reste.
- D’abord, le service ne tourne pas en root. L’éditeur, ses extensions et Codex héritent des droits du compte. Une commande privilégiée reste une frontière visible, pas l’état normal de toute la session.
- Ensuite, l’écoute ne se fait ni sur toutes les interfaces ni sur l’adresse publique. Elle cible l’adresse portée par le bridge utilisé par Nginx Proxy Manager. Un scan du serveur depuis Internet ne doit donc jamais voir ce port.
- Enfin, les données du serveur et du CLI vivent dans des répertoires persistants du compte. Le workspace reste un dossier normal de l’hôte. Il n’y a plus de traduction d’UID, de volume d’initialisation ou de chemin différent selon que j’utilise le navigateur ou Remote SSH.
Enregistrez cette unité dans /etc/systemd/system/vscode-web.service après avoir remplacé tous les placeholders. Testez d’abord que le workspace existe, que l’utilisateur peut y travailler et que l’adresse choisie appartient bien au bridge attendu. La séquence de mise en service reste ensuite volontairement banale :
sudo systemctl daemon-reload
sudo systemctl enable --now vscode-web.service
sudo systemctl status vscode-web.service
sudo journalctl -u vscode-web.service -n 100
sudo ss -ltnpEt boom VScode en mode Web ! Et le dernier contrôle ne doit pas seulement trouver le port. Il doit confirmer la bonne adresse d’écoute. Si le service apparaît sur toutes les interfaces, je l’arrête et je corrige le bind avant de toucher au reverse proxy.
Étape 4 : relier Nginx Proxy Manager au service de l’hôte
On explique souvent comment un service de l’hôte contacte un conteneur. Ici, le flux intéressant part de Nginx Proxy Manager, dans Docker, vers VS Code Web, sur l’hôte.
Un bridge Docker défini par l’utilisateur possède une interface correspondante sur l’hôte. Cette interface porte l’adresse de passerelle du réseau. En faisant écouter VS Code Web uniquement sur cette adresse, les conteneurs du bridge peuvent le joindre sans exposer le port sur toutes les interfaces.
La documentation du driver bridge Docker rappelle que les réseaux définis par l’utilisateur disposent de leur propre adressage et de règles d’isolation. Avec les valeurs relevées à l’étape 2, créez un nouveau Proxy Host dans Nginx Proxy Manager :
- schéma : HTTP sur le réseau interne ;
- hôte cible : adresse de la passerelle du bridge ;
- port : 8000 dans cet exemple ;
- support WebSocket : activé ;
- HTTPS public et certificat : gérés par le proxy ;
- Access List : Basic Auth avec un secret long et unique.
Créez l’Access List avant d’associer le Proxy Host, puis contrôlez le certificat, Force SSL et le support WebSocket. Une fenêtre privée doit recevoir une demande d’authentification ; après connexion, ouvrez un terminal intégré et laissez-le quelques minutes pour confirmer que le WebSocket reste stable.
Il ne faut surtout pas ajouter une publication Docker de type ports, puisqu’il n’existe plus de conteneur VS Code. Il ne faut pas davantage faire écouter le service sur toutes les interfaces « pour tester » puis oublier de revenir en arrière. On sait tous qu’un test temporaire qui survit six minutes obtient généralement le statut officiel de configuration en prod…
Étape 5 : transformer l’intention en règle firewall
Une adresse d’écoute privée limite déjà l’exposition, mais elle ne prouve pas que seul Nginx Proxy Manager peut atteindre le service. Plusieurs conteneurs partagent parfois un même réseau de proxy. La règle ci-dessous concerne la seconde connexion, celle que le conteneur Nginx Proxy Manager ouvre vers le service de l’hôte. Elle appartient donc à INPUT ; le trafic HTTPS public publié par Docker suit, lui, le chemin DNAT et FORWARD.
J’ai ajouté une autorisation très ciblée avant le rejet final de la chaîne :
-A INPUT \
-i <bridge-proxy> \
-s <adresse-du-conteneur-npm>/32 \
-d <adresse-hote-du-bridge>/32 \
-p tcp --dport 8000 \
-m conntrack --ctstate NEW \
-m comment --comment "VS Code Web from reverse proxy" \
-j ACCEPTLa politique générale INPUT reste restrictive. La règle n’accepte qu’une nouvelle connexion TCP venant de l’adresse attendue, par le bon bridge, vers la bonne adresse locale et le bon port. Les flux établis sont ensuite traités par la règle conntrack générale du firewall.
L’ordre compte : cette autorisation doit être évaluée avant le rejet final de la chaîne. Je pars d’un export des règles actives, j’en fais un candidat séparé, puis j’ajoute la nouvelle règle avant le rejet final. Comme pour toute modification de firewall réalisée à distance, le candidat est parsé avant chargement et appliqué avec un rollback automatique :
sudo iptables-save >/tmp/rules.v4.current
cp /tmp/rules.v4.current /tmp/rules.v4.candidate
# Modifier /tmp/rules.v4.candidate avec votre éditeur,
# puis insérer la règle avant le rejet final de INPUT.
sudo iptables-restore --test </tmp/rules.v4.candidate
sudo iptables-apply -t 120 /tmp/rules.v4.candidateiptables-apply est l’exemple utilisé sur mon système ; adaptez la commande au mécanisme transactionnel de votre distribution. Une seconde session SSH et l’accès console du fournisseur restent prêts pendant les deux minutes. On ne confirme les règles qu’après avoir testé une nouvelle connexion SSH, le site public, le proxy et VS Code Web. Le fichier validé peut ensuite remplacer la configuration persistante selon le mécanisme utilisé par la distribution.
Cette approche dépend de la stabilité de l’adresse du proxy. Dans mon environnement, elle est gérée explicitement. Si elle change, l’accès tombe en panne, ce qui est préférable à une règle élargie silencieusement à tout le réseau Docker. Sur une plateforme où les adresses sont dynamiques, je créerais plutôt un réseau dédié à ce seul dialogue ou j’automatiserais la génération contrôlée de la règle.
La Basic Auth ne remplace pas le filtrage, et le filtrage ne remplace pas l’authentification. HTTPS protège le secret sur le trajet public. Nginx Proxy Manager décide qui peut franchir le proxy. Le bind et iptables empêchent de contourner facilement ce proxy. Ces couches répondent à des échecs différents.
J’ai choisi de ne pas conserver le jeton applicatif de VS Code dans l’URL, notamment pour éviter sa présence dans l’historique, les copier-coller ou certains journaux. Cela rend la robustesse de Nginx Proxy Manager et de sa liste d’accès encore plus importante. L’anti-brute-force spécifique aux services Docker reste un chantier séparé, volontairement visible dans le backlog plutôt que déclaré « couvert » par optimisme.
La Basic Auth avec un mot de passe très long et unique correspond à mon compromis actuel, pas au maximum théorique. Pour une exposition plus sensible, un VPN, un certificat client ou une passerelle d’identité avec MFA réduisent davantage le risque lié au vol ou au brute-force d’un mot de passe. Le fait que l’URL ne soit pas documentée publiquement ne compte évidemment pas comme une couche de sécurité.
J’ai encore quelques TP en tête (dans les brouillons en fait 😉 ) autour de CrowdSec ou Fail2ban pour Nginx Proxy Manager. En attendant, cette URL ouvre un environnement d’administration et mon compte peut, dans mon cas, élever ses droits. Le premier attaquant qui franchit toutes les couches ne gagne pas qu’une jolie page d’accueil : c’est champagne pour lui… et finit pour moi…
Étape 6 : installer Codex pour l’utilisateur, jamais en root par défaut
La documentation officielle de Codex CLI propose désormais un installateur autonome pour macOS et Linux. Lancez-le avec l’utilisateur qui exécutera VS Code Web, sans sudo, puis vérifiez le binaire :
curl -fsSL https://chatgpt.com/codex/install.sh | sh
codex --version
codexAu premier lancement, choisissez la méthode de connexion proposée par Codex, puis revenez dans le workspace. Si votre politique interdit l’exécution directe d’un script téléchargé, récupérez-le, inspectez-le ou utilisez l’une des autres méthodes d’installation publiées par OpenAI. Le répertoire d’authentification de Codex reste privé dans le compte utilisateur : ne le montez pas dans Nginx, ne le sauvegardez pas en clair et ne recopiez jamais son contenu dans un diagnostic.
Étape 7 : valider
Commencez par les preuves les plus proches du service. La troisième commande suppose que curl est présent dans le conteneur du proxy ; sinon, utilisez un conteneur de diagnostic raccordé au même réseau. La dernière requête est volontairement envoyée sans identifiant et doit recevoir un refus HTTP, généralement 401 :
sudo systemctl is-active vscode-web.service
sudo systemctl --no-pager --full status vscode-web.service
sudo ss -ltnp | grep ':8000'
docker exec <conteneur-npm> \
curl -fsS http://<adresse-hote-du-bridge>:8000/ >/dev/null
curl -sS -o /dev/null -w '%{http_code}\n' \
https://<nom-public-de-vscode>/La validation a porté sur plusieurs niveaux :
- état actif du service systemd et absence de boucle de redémarrage ;
- écoute uniquement sur l’adresse privée prévue ;
- accès direct refusé depuis les autres chemins ;
- requête HTTP réussie depuis le conteneur Nginx Proxy Manager ;
- connexion HTTPS publique avec Basic Auth ;
- établissement des WebSockets et stabilité de l’interface ;
- ouverture du workspace réel et création d’un terminal sur l’hôte ;
- redémarrage du service sans perte des données utiles ;
- absence d’impact sur SSH, Docker et les autres proxy hosts.
La règle firewall possède également sa sauvegarde précédente, que j’applique elle aussi avec un délai de confirmation plutôt qu’avec un chargement aveugle :
sudo systemctl disable --now vscode-web.service
sudo iptables-apply -t 120 <sauvegarde-firewall-précédente>Ce que le résultat permet vraiment
Depuis mon PC personnel, je peux toujours administrer le serveur avec VS Code et Remote SSH. Depuis un navigateur, je retrouve le même dossier de travail sur l’hôte, le terminal local au serveur et Codex. Ce ne sont pas deux interfaces attachées à une mystérieuse session graphique unique, mais deux clients qui travaillent sur les mêmes fichiers et la même machine.
Comme je n’utilise pas les deux pour modifier le même fichier simultanément, ce fonctionnement me convient parfaitement. Git reste la source de vérité pour les changements importants. Les réglages d’interface peuvent différer entre le client lourd et le Web, ce qui ne me gêne pas : l’objectif est de partager le contexte d’exécution, pas la position exacte de chaque onglet.
Le gain principal n’est même pas de pouvoir coder depuis un navigateur. C’est de pouvoir ouvrir un incident, sélectionner un fichier, demander à Codex de comparer son contenu avec l’état réel du service, contrôler les commandes proposées et rester dans la même boucle jusqu’à la validation.
La contrepartie est évidente : cette URL mène à un environnement d’administration. Le mot de passe doit être long, unique et protégé par HTTPS. Le proxy doit être mis à jour. Les tentatives d’authentification doivent être supervisées. Le port interne doit rester invisible depuis Internet. Les permissions Codex et sudo doivent être choisies avec la même attention que pour une session SSH.
Le détour par Docker était utile
Au final, la stack Kasm n’était pas une erreur inutile. Elle a permis de valider progressivement l’accès Web, le reverse proxy, la persistance, le montage du workspace et le comportement de VS Code dans le navigateur. Elle a surtout matérialisé la question que je n’avais pas posée au départ : est-ce que je veux développer dans un conteneur, ou administrer le serveur qui héberge le conteneur ?
Pour le premier besoin, je garderais volontiers l’isolation Docker. Pour le second, une exécution directe sous un utilisateur non privilégié, une surface réseau minuscule et des élévations visibles sont plus cohérentes.
Le résultat est assez démentiel à l’usage. J’ouvre une page depuis une machine qui ne possède ni mon workspace ni ma configuration SSH, et je retrouve un VS Code capable de travailler sur le vrai serveur. Codex peut investiguer en direct, mais il commence par prouver ce qu’il voit. Nginx Proxy Manager reste le portier. Le firewall vérifie que personne ne contourne la porte. Et l’ancien chemin reste disponible si le nouveau décide de partir en vacances.

Ce n’est pas « une IA root dans un navigateur ». C’est justement tout l’intérêt : un éditeur sur l’hôte, un agent sous contrôle, plusieurs frontières explicites et un humain qui décide quand les franchir.
Bon voilà, je voulais un VS Code dans mon navigateur. J’ai obtenu un véritable poste d’administration Web, la documentation de ses limites et probablement le début de beaucoup de nouveaux TP sécurité (ca permet d’aller 30x plus vite sur l’administration du serveur là, Codex c’est comme si j’avais un super admin linux avec 30 ans d’expérience qui bosse à plein temps sur le serveur). Pour le coup, celui-là va rester dans ma boîte à outils.
Geekez bien !


