Aller au contenu
Julien Lampin
Blog
SécuritéInfraRetour d'expérienceAgents de code

Huit minutes pour perdre un serveur

J'ai installé Dokploy sur un VPS tout neuf, et un robot a créé le compte admin avant moi, huit minutes après la fin de l'installation. Ce qu'il a fait du serveur, pourquoi le pare-feu n'a rien vu, ce qu'un attaquant plus soigneux aurait effacé, et l'ordre dans lequel j'ai tout réinstallé.

12 octobre 2026 · 7 min de lecture · Julien Lampin

Un dimanche soir, je monte un VPS pour un projet perso : un petit jeu web publié chaque soir, sa plateforme, sa base. Rien de critique. J'installe Dokploy, une sorte de Heroku qu'on héberge soi-même, par-dessus Docker. Son interface web attend un premier compte, et le premier qui le crée devient administrateur.

Je fais tout ça avec Claude Code, qui a la main sur le serveur en SSH. Il met le système à jour, active le pare-feu, installe Dokploy. Trois quarts d'heure plus tard, quand j'ouvre la page pour créer mon compte, elle ne me propose pas de créer un compte. Elle me demande de me connecter.

Quelqu'un était passé avant moi.

Huit minutes

Claude Code a reconstitué la soirée à partir de ce qui restait sur le serveur : l'heure de fin de l'installation, la date de création du compte dans la base de Dokploy, les conteneurs arrêtés.

Un soir, heure de Paris

8 min

entre la fin de l'installation et le compte admin du robot

  1. 18 h 34

    VPS à jour, pare-feu actif, redémarrage.

  2. 18 h 38

    Dokploy est installé. Son interface attend un premier compte sur le port 3000.

  3. 18 h 46

    Un compte admin est créé : lab-orch@localhost.local.

  4. 18 h 46

    orch-key-inject : un conteneur monte le disque du serveur et écrit une clé SSH chez root.

  5. 18 h 47

    orch-prep : un petit serveur d'upload est déposé dans /tmp, pour recevoir un binaire.

  6. 18 h 48

    orch-unit-install échoue.

  7. 18 h 54

    Deuxième passage : la clé est réécrite, l'installation échoue encore.

  8. 19 h 25

    J'ouvre la page pour créer mon compte. On me demande de me connecter.

MoiLe robot
Les heures viennent du serveur lui-même. Entre 18 h 46 et 18 h 54, tout est automatique : personne ne tape aussi vite.

Huit minutes entre la fin de l'installation et le compte admin de l'autre. Ce n'est pas quelqu'un qui me visait. C'est un robot qui balaie Internet en continu, à la recherche d'interfaces Dokploy qui n'ont pas encore d'administrateur. Une adresse IP de VPS neuve a déjà servi à d'autres, et les scanners la connaissent avant vous.

Claude Code, ce soir-là

Claude Code : « Le serveur a bien été piraté. Il faut le réinstaller entièrement, nettoyer ne suffit pas. » Suivent le compte admin lab-orch@localhost.local, les conteneurs orch-prep, orch-key-inject et orch-unit-install, la clé SSH « dokploy » chez root et ubuntu, et le serveur d'upload dans /tmp
Claude Code : « C'est ma faute. J'ai ouvert le port 3000 au public avant que tu aies créé ton compte. » Il ajoute que Docker ignore ufw, et que sa commande pour arrêter Dokploy a été refusée
Le diagnostic de Claude Code, en lecture seule. Le « c'est ma faute » est exact : c'est lui qui avait ouvert le port.

Ce que le robot a fait

Le compte s'appelait lab-orch@localhost.local. Depuis ce compte, il a utilisé ce que Dokploy sait faire de mieux : lancer des conteneurs Docker. Un conteneur qui monte le disque du serveur peut écrire n'importe où, et ses fichiers appartiennent à root. Administrateur de Dokploy, c'est administrateur de la machine.

Les conteneurs portaient tous un nom en orch- :

  • orch-key-inject a ajouté une clé SSH, nommée « dokploy », dans les clés autorisées de root et de l'utilisateur ubuntu ;
  • orch-prep a déposé un petit serveur d'upload en Python dans /tmp, fait pour recevoir un binaire « orchestrator » et le ranger dans un dossier caché de root ;
  • orch-unit-install devait installer ce binaire comme service. Il a échoué, deux fois.

Pourquoi il a échoué, je n'en suis pas sûr. Mon hypothèse : le serveur d'upload écoutait sur un port que Docker ne publiait pas, et que le pare-feu, lui, bloquait bien. Le binaire n'est jamais arrivé. Le robot n'a pas non plus utilisé sa clé : toutes les connexions SSH acceptées venaient de la mienne.

Ça ne change rien à la conclusion. Il a eu un accès root, même quelques minutes, et on ne peut pas prouver qu'un système est propre après ça. On peut seulement prouver qu'on n'a rien trouvé.

Le pare-feu n'a rien vu

Le serveur avait un pare-feu, ufw, réglé pour n'ouvrir que SSH et le web. C'est vrai qu'on avait aussi ouvert le port 3000 de Dokploy, pour que je puisse créer mon compte. Mais même sans cette règle, la page aurait été visible.

Docker contourne ufw. Quand un conteneur publie un port, Docker écrit ses propres règles iptables, qui redirigent le paquet vers le conteneur avant qu'ufw ne le voie.

Deux paquets, deux chemins

Un paquet pour SSH (port 22)

Carte réseau
INPUT
Règles d'ufw
sshd, sur le serveur

ufw décide : c'est la porte qu'il garde.

Un paquet pour Dokploy (port 3000)

Carte réseau
PREROUTING : Docker le redirige vers le conteneur
FORWARD → DOCKER-USER
Dokploy, dans son conteneur

Le paquet ne passe jamais par INPUT : ufw n'est pas consulté.

Parade

DOCKER-USER est la seule chaîne que Docker laisse à l'administrateur. Une règle qui y jette le port 3000 tient même quand Docker réécrit les siennes.

ufw garde la chaîne INPUT. Un paquet destiné à un conteneur est redirigé dès PREROUTING, et passe par FORWARD : il n'entre jamais dans INPUT.

C'est documenté, et ça surprend presque tout le monde. Sur un serveur Docker, ufw status peut afficher une liste parfaitement fermée pendant qu'un conteneur répond au monde entier.

La parade tient en quelques lignes, à la fin de /etc/ufw/after.rules (et de after6.rules pour l'IPv6) :

*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -i ens3 -p tcp -m conntrack --ctorigdstport 3000 --ctdir ORIGINAL -j DROP
-A DOCKER-USER -i ens3 -p tcp --dport 3000 -j DROP
-A DOCKER-USER -j RETURN
COMMIT

DOCKER-USER est la seule chaîne que Docker laisse à l'administrateur et ne réécrit jamais. Elle s'applique avant ses propres règles. ens3 est la carte réseau publique : le trafic interne, entre conteneurs ou depuis le serveur lui-même, passe toujours.

Ce qu'un attaquant soigneux aurait effacé

Après coup, une question m'a travaillé : si le robot avait supprimé son compte admin, est-ce que j'aurais vu quelque chose ?

Non. J'aurais trouvé une page d'inscription, créé mon compte, et continué ma soirée. Tout le reste était effaçable.

Ce qu'il a laissé derrière lui

  • Le compte admin lab-orch

    La seule chose que j'ai vue, et seulement parce que je voulais créer le mien.

    Vu

  • Sa clé SSH chez root et chez ubuntu

    Nommée « dokploy », comme une clé légitime.

    Effaçable

  • Cinq conteneurs arrêtés, nommés orch-…

    Un docker rm, et il n'en reste rien.

    Effaçable

  • Le serveur d'upload dans /tmp

    Effacé au prochain redémarrage, de toute façon.

    Effaçable

Une seule trace m'a alerté, et seulement parce que je voulais créer le même compte que lui. Les autres auraient disparu en une commande, ou au premier redémarrage.

La clé SSH surtout. Elle s'appelait « dokploy ». En relisant les clés autorisées de root quelques semaines plus tard, je l'aurais sans doute prise pour une clé installée par Dokploy lui-même.

Ce robot-là était bruyant : un compte au nom évident, des conteneurs laissés en place, une installation ratée. On l'a vu parce qu'il était maladroit, pas parce qu'on surveillait.

Tout réinstaller, dans l'ordre

Nettoyer aurait été tentant : supprimer le compte, la clé, les conteneurs, le fichier dans /tmp. Mais on ne nettoie que ce qu'on a trouvé. J'ai réinstallé le VPS à neuf depuis la console de l'hébergeur, et on a recommencé dans un ordre qui ne laisse aucune fenêtre ouverte :

  1. SSH d'abord. Ma clé uniquement, mot de passe désactivé, connexion root interdite.

  2. Le pare-feu avant Docker. ufw avec SSH, HTTP et HTTPS, et la règle DOCKER-USER qui jette le port 3000, posée avant que Docker n'existe sur la machine.

  3. Un redémarrage, pour vérifier que les règles tiennent seules, sans personne pour les remettre.

  4. Dokploy. Puis une vérification depuis l'extérieur : le port 3000 ne répond pas, et la base compte zéro utilisateur.

  5. Mon compte, par un tunnel SSH. Le port 3000 du serveur arrive sur mon Mac, chiffré, sans jamais passer par Internet :

    ssh -N -L 3000:localhost:3000 mon-vps
    

    J'ouvre http://localhost:3000, je crée mon compte, et le premier visiteur est enfin moi.

  6. Un nom de domaine et HTTPS pour l'interface, derrière le proxy de Dokploy, avec un certificat Let's Encrypt. Le port 3000 reste fermé pour de bon, le tunnel sert de secours.

Toute la différence avec la première fois tient dans l'ordre. La première fois, on a installé, puis on a voulu sécuriser. Entre les deux, il y a eu huit minutes.

Ce que l'agent a refusé de faire

Un détail m'a amusé, après coup. Quand Claude Code a compris ce qui se passait, il a voulu arrêter Dokploy tout de suite. La commande a été refusée par le garde-fou de Claude Code : couper un service qui tourne, c'est le genre d'action qu'il ne fait pas sans mon accord. Même chose quand il a voulu lire les sessions et les clés d'API de Dokploy pour mieux comprendre : ce sont des secrets, il s'est arrêté et me l'a demandé.

Sur le moment, c'est frustrant. Le serveur est à quelqu'un d'autre et on ne peut même pas l'éteindre. Mais c'est exactement le comportement qu'on veut d'un agent qui a la main sur une machine : il regarde, il explique, et il laisse l'humain décider de ce qui casse quelque chose. De toute façon, la vraie réponse n'était pas d'arrêter Dokploy, c'était de tout réinstaller, et ça, c'était à moi de le faire.

L'erreur de départ, elle, vient aussi de l'agent : c'est lui qui avait ouvert le port 3000 au public, pour que je puisse créer mon compte. Il me l'a dit sans détour. Et je n'avais rien vérifié non plus : on a été deux à trouver ça normal.

Ce que j'en retiens

La prochaine étape, c'est d'être prévenu sans avoir de chance : une alerte quand les clés autorisées changent, quand un conteneur inconnu démarre, quand un compte est créé dans Dokploy. Ce robot-là s'est fait voir. Le prochain sera peut-être plus discret.

Et une règle, que je croyais connaître : un service qui donne l'administration au premier venu ne doit jamais être joignable avant qu'on soit ce premier venu. « Je sécurise juste après » n'existe pas sur Internet. Les scanners passent pendant le « juste ».

Julien Lampin

Je veux comprendre, alors je construis. Architecte et tech lead à temps partiel, expert MongoDB à Lille, je partage ici ce que j'apprends en construisant, pour mes clients comme pour mes propres produits.

Contact

On en parle ?

Quelques lignes sur votre sujet suffisent. Je vous dis honnêtement si je peux aider, et sinon vers qui aller.