21 juillet 2026 12 min de lecture

Exploiter Claude Code en toute sécurité sur son propre VPS

Un VPS Debian renforcé permet de garder les sessions Claude Code accessibles en permanence. Ce guide couvre le compte utilisateur, les clés SSH, le pare-feu, l’hygiène des données, tmux et l’accès sécurisé depuis un iPhone.

Sur son propre ordinateur, une session Claude Code s’interrompt inévitablement lorsque l’ordinateur portable se met en veille ou que la connexion réseau est coupée. Un VPS continue de fonctionner et reste accessible depuis plusieurs appareils. En même temps, il est connecté en permanence à l’Internet public et fait l’objet de scans automatisés peu après son démarrage.

Ce guide réunit ces deux exigences : Claude Code reste disponible dans une session tmux, tandis que le serveur Debian n’offre vers l’extérieur qu’une connexion SSH protégée par clé. Le renforcement n’est pas spécifique à Claude et convient également à d’autres serveurs Linux accessibles publiquement.

Pourquoi un VPS peut être pertinent

Par rapport à une installation purement locale, le serveur offre trois avantages pratiques :

  • Persistance. Dans une session tmux, Claude continue de fonctionner même si la connexion SSH est interrompue. Une tâche qui prend dix minutes ou une heure s’achève sans que l’ordinateur portable doive rester ouvert.
  • Accessibilité. La même session est accessible depuis le poste de travail, l’ordinateur portable et l’iPhone. On lance une tâche depuis son bureau et on consulte le résultat en déplacement.
  • Contrôle des données. On décide soi-même de ce qui se trouve sur le serveur. Aucun service de synchronisation, aucune donnée d’accès sauvegardée accidentellement, à condition de procéder soigneusement lors de la migration (voir plus bas).

tmux est donc une fonction de disponibilité et de confort, et non une mesure de sécurité. Le vrai travail réside dans la sécurisation.

Situation de départ

La base est Debian 13 (Trixie), installée en version minimale, sans bureau ni services réseau supplémentaires. Le fournisseur met à disposition un pare-feu en amont qui s’applique indépendamment du système d’exploitation. L’objectif est un serveur sur lequel seul SSH est accessible depuis l’extérieur, et uniquement avec des clés protégées par phrase secrète.

1. Mettre le système à jour

Immédiatement après l’installation, mettre à jour l’ensemble des paquets :

sudo apt update
sudo apt full-upgrade
Explication des options
OptionEffet
updateRelit les listes de paquets de toutes les sources configurées
full-upgradeMet à jour tous les paquets et peut installer de nouveaux paquets ou supprimer des paquets existants si nécessaire

Contrairement à upgrade, full-upgrade résout aussi les dépendances qui nécessitent de nouveaux paquets ou la suppression de paquets. Sur un système fraîchement installé, c’est la bonne approche pour appliquer réellement toutes les mises à jour de sécurité disponibles. Redémarrer une fois après les mises à jour du noyau.

2. Un utilisateur dédié plutôt que root

Travailler en tant que root est inutilement risqué : chaque faute de frappe s’applique à l’ensemble du système, et la connexion root directe est la première chose que tentent les attaques automatisées. Il faut donc créer un utilisateur dédié (ici claude) disposant de droits sudo pour les cas où ils sont nécessaires :

sudo adduser claude
sudo usermod -aG sudo claude
Explication des options
OptionEffet
-aAjout : complète la liste des groupes de l’utilisateur au lieu de la remplacer ; valable uniquement avec -G
-G sudoGroupe(s) supplémentaire(s) auquel/auxquels l’utilisateur est ajouté
claudeL’utilisateur concerné ; avec adduser le nom du compte à créer

Désormais, toute l’administration passe par claude et sudo, et non plus par l’accès root direct.

3. Clés Ed25519 avec phrase secrète, une par appareil

L’authentification doit s’effectuer exclusivement avec des clés SSH, et non avec des mots de passe. Ed25519 est le standard actuel : court, rapide et cryptographiquement solide. L’essentiel est que la clé soit générée sur le client, donc sur le PC et non sur le serveur, puis protégée par une phrase secrète. La phrase secrète constitue la deuxième ligne de défense si la clé privée tombe un jour entre de mauvaises mains.

Sur le PC :

ssh-keygen -t ed25519 -C "pc-thinkpad"
Explication des options
OptionEffet
-t ed25519Type de clé, ici le procédé elliptique Ed25519
-C "pc-thinkpad"Commentaire ajouté à la clé publique

Le commentaire (-C) désigne l’appareil. Cela s’avère utile par la suite : une clé distincte est générée pour chaque appareil, une pour le PC et une autre pour l’iPhone. Si un appareil est perdu, il suffit de supprimer précisément sa clé publique de ~/.ssh/authorized_keys, sans devoir redéployer tous les autres accès.

Seule la clé publique doit être placée sur le serveur. La clé privée ne quitte jamais l’appareil. À la fin, authorized_keys ne contient que des clés publiques, chacune accompagnée du commentaire de son appareil :

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...pc  pc-thinkpad
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...ios iphone-15

Transférer initialement la clé publique du PC. Tant que l’authentification par mot de passe est encore active, le plus simple est d’utiliser :

ssh-copy-id claude@SERVER
Explication des options
OptionEffet
claude@SERVERUtilisateur et hôte cible ; la clé publique standard y est ajoutée à ~/.ssh/authorized_keys

Tester ensuite que la connexion avec la clé fonctionne avant de désactiver l’authentification par mot de passe à l’étape suivante. Les permissions de fichiers doivent être correctes, sinon sshd ignore le fichier : ~/.ssh à 700, authorized_keys à 600.

4. Renforcer SSH : ni root ni mot de passe

La configuration du serveur se trouve dans /etc/ssh/sshd_config et (sur Debian 13) dans les fichiers Drop-in sous /etc/ssh/sshd_config.d/. Les modifications doivent être placées dans un fichier Drop-in dédié ; le fichier principal reste ainsi intact et les mises à jour de paquets n’écrasent rien. Créer le fichier /etc/ssh/sshd_config.d/99-haertung.conf :

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes

Cela désactive la connexion root directe et l’authentification par mot de passe. Désormais, seules les personnes possédant une clé privée correspondante peuvent se connecter. Avant de recharger, vérifier la syntaxe de la configuration :

sudo sshd -t
Explication des options
OptionEffet
-tMode test : vérifie la validité du fichier de configuration et des clés sans démarrer le service

Si sshd -t ne retourne rien, le fichier est valide. Ne recharger qu’ensuite :

sudo systemctl reload ssh
Explication des options
OptionEffet
reloadIndique au service de recharger sa configuration sans interrompre les connexions existantes
sshL’unité cible, ici le service OpenSSH

Important : laisser la session SSH existante ouverte et tester le nouvel accès dans un second terminal. L’ancienne session ne doit être fermée qu’une fois la connexion par clé vérifiée. Cette précaution réduit pratiquement à zéro le risque de se verrouiller l’accès. Une erreur de configuration peut autrement entraîner la perte de tout accès.

5. Déplacer SSH vers un port inhabituel

Le port standard 22 est testé en permanence par des bots. Le remplacer par un port élevé librement choisi (dans l’exemple 61417) laisse dans le vide la majeure partie de ce bruit automatisé. Ce n’est explicitement pas un gain de sécurité à proprement parler : changer de port ne remplace pas une authentification forte, cela réduit seulement le volume de journaux et la charge des scans. L’obligation d’utiliser des clés de l’étape 4 reste la véritable protection.

Le choix du port n’est pas arbitraire. L’IANA distingue trois zones : les ports 0–1023 (well-known ports) sont réservés aux services standards (SSH lui-même sur 22, HTTP sur 80, HTTPS sur 443), nécessitent root pour s’y lier et n’ont rien à faire pour un port SSH choisi librement ; ce sont précisément les ports attendus par les scanners comme par les services standards qui pourraient être installés ultérieurement. Les ports 1024–49151 (ports enregistrés) sont attribués sur demande à des applications individuelles, par exemple 3306 (MySQL), 5432 (PostgreSQL), 6379 (Redis) ou 8080/8443 comme alternatives HTTP répandues ; un port choisi au hasard dans cette plage entre facilement en conflit ultérieurement avec un logiciel qui attend précisément son port enregistré. Les ports 49152–65535 (ports dynamiques/privés) ne sont attribués à aucun service selon l’IANA et sont prévus pour des usages temporaires et privés : c’est la plage appropriée pour un port permanent choisi par soi-même.

Une réserve subsiste : de nombreux systèmes Linux, dont Debian, utilisent une partie de cette même plage comme port source pour leurs propres connexions sortantes (net.ipv4.ip_local_port_range, par défaut autour de 32768–60999). Un service qui écoute durablement n’entre pas réellement en conflit avec cela, car le noyau n’attribue pas un port déjà lié, mais un port supérieur à 60999 évite également cette ambiguïté théorique. L’exemple de cet article (61417) se situe donc délibérément dans cette plage. Avant le changement, vérifier également avec ss -lntup (voir étape 7) que le port choisi n’est pas déjà occupé sur son propre serveur.

Avec Debian 13, il y a une particularité : SSH peut être démarré via l’activation par socket systemd. Si c’est le cas, l’indication Port dans sshd_config est tout simplement ignorée ; le port doit alors être défini au niveau du socket. Vérifier d’abord quelle situation s’applique :

systemctl is-enabled ssh.socket
Explication des options
OptionEffet
is-enabledIndique si l’unité est activée pour le démarrage du système
ssh.socketL’unité socket du service SSH

Si la commande répond enabled, SSH fonctionne via le socket. Modifier alors le port à cet endroit :

sudo systemctl edit ssh.socket
Explication des options
OptionEffet
editCrée un fichier d’override Drop-in pour l’unité et l’ouvre dans l’éditeur
ssh.socketL’unité socket à remplacer

Dans l’éditeur, saisir les lignes suivantes. La première ligne ListenStream=, vide, supprime le port 22 prédéfini ; la seconde définit le nouveau :

[Socket]
ListenStream=
ListenStream=61417

Appliquer ensuite les changements :

sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
Explication des options
OptionEffet
daemon-reloadRelit tous les fichiers d’unité, y compris le fichier d’override qui vient d’être créé
restart ssh.socketRedémarre l’unité socket afin qu’elle écoute sur le nouveau port

Si l’activation par socket n’est pas active (disabled), il faut à la place ajouter Port 61417 au fichier Drop-in de l’étape 4, puis exécuter sudo sshd -t et sudo systemctl restart ssh.

Ici aussi, il faut d’abord ouvrir le nouveau port dans le pare-feu (étape suivante), puis se connecter et tester, en laissant l’ancienne session ouverte jusqu’à ce que l’accès par le nouveau port soit confirmé.

6. Pare-feu : fermé par défaut

Le pare-feu du fournisseur situé en amont constitue la limite la plus efficace, car il intercepte les paquets avant même qu’ils n’atteignent le système d’exploitation. Deux règles de base :

  • Action par défaut pour les connexions entrantes : DROP. Tout ce qui n’est pas explicitement autorisé est rejeté, sans commentaire ni retour à l’expéditeur.
  • Une seule exception : TCP entrant sur le port cible 61417. Rien d’autre ne doit être accessible depuis l’extérieur.

Le trafic sortant reste autorisé. C’est intentionnel : le serveur doit pouvoir télécharger des paquets, synchroniser l’heure et atteindre l’API pour Claude Code. Un filtrage sortant restrictif apporte peu de protection supplémentaire pour un serveur individuel, mais rend l’exploitation sensiblement plus contraignante.

Pour une défense en profondeur supplémentaire, les mêmes règles peuvent être dupliquées côté hôte avec nftables ou ufw. Pour la configuration décrite, le pare-feu du fournisseur suffit.

7. Vérifier la surface d’attaque

Après le renforcement, contrôler ce que le serveur propose réellement vers l’extérieur. Deux commandes suffisent. Premièrement : quels services écoutent sur quelles adresses ?

sudo ss -lntup
Explication des options
OptionEffet
-lAffiche uniquement les sockets en écoute
-nSortie numérique : les ports et adresses ne sont pas résolus en noms
-tInclut les sockets TCP
-uInclut les sockets UDP
-pAffiche le processus derrière chaque socket ; sudo est nécessaire pour cela

La colonne d’adresse est déterminante : un service sur 0.0.0.0 ou [::] est accessible depuis l’extérieur, un service sur 127.0.0.1 ou [::1] ne l’est que localement. Dans l’état sécurisé, seul SSH devrait apparaître publiquement. Des services tels que chronyd (synchronisation de l’heure) peuvent apparaître, mais uniquement liés à des adresses locales. Si chronyd écoute exclusivement sur 127.0.0.1 et ::1, il n’est pas joignable depuis l’extérieur et est donc sans risque.

Deuxièmement : existe-t-il des services système défaillants indiquant un problème de configuration ?

systemctl --failed
Explication des options
OptionEffet
--failedListe uniquement les unités en état d’échec

La réponse devrait être 0 loaded units listed, sans le moindre service défaillant. Les unités défaillantes ne sont pas seulement un problème d’exploitation, mais aussi potentiellement un problème de sécurité si elles cachent un service réseau partiellement démarré ou mal configuré.

8. Installer et exploiter Claude Code

Claude Code nécessite un environnement d’exécution Node.js récent. Après l’avoir installé, configurer la CLI selon les instructions officielles et s’authentifier à nouveau sur le serveur, sans téléverser les données d’accès locales (nous y revenons juste après).

Pour une exploitation durable avec tmux :

tmux new -s claude
Explication des options
OptionEffet
newCrée une nouvelle session
-s claudeAttribue le nom de session qui permettra de la reprendre plus tard

Démarrer Claude dans la session. Avec Ctrl-b, puis d, on se détache de la session sans y mettre fin ; Claude continue de fonctionner. Pour y revenir :

tmux attach -t claude
Explication des options
OptionEffet
attachReconnecte le terminal à une session en cours
-t claudeSélectionne la session cible par son nom

Ainsi, une tâche en cours survit aux connexions interrompues, aux changements d’appareil et à la mise en veille nocturne de l’ordinateur portable.

9. Hygiène des données lors de la migration

La partie la plus délicate du transfert vers le serveur n’est pas la technique, mais la question de ce qu’il faut emporter. Trois règles :

  • Aucune clé privée sur le serveur. authorized_keys ne contient que des clés publiques. Les clés privées restent sur les appareils terminaux.
  • Ne pas copier les données d’accès sans distinction. Les fichiers locaux sensibles tels qu’un .credentials.json ne doivent pas être transférés sans vérification sur le VPS. Il faut plutôt s’authentifier à nouveau sur le serveur.
  • Placer d’abord la configuration dans un dossier de migration. Ne pas écrire directement les mémoires et configurations Claude existantes dans les chemins de configuration actifs, mais les transférer d’abord dans un dossier de migration séparé et y vérifier ce qui doit réellement être repris. Ce qui n’est plus nécessaire, comme d’anciennes entrées MCP ou des paramètres orphelins, est délibérément laissé de côté plutôt que déplacé sans examen.

10. Aperçus web via un tunnel SSH

Pour les aperçus web, par exemple un serveur de développement local démarré par Claude, la tentation est grande d’ouvrir simplement un port supplémentaire. Il ne faut pas le faire. Chaque port ouvert supplémentaire augmente la surface d’attaque. À la place, l’aperçu passe par un tunnel de port SSH chiffré : le service écoute uniquement localement sur le serveur, et SSH le transmet au client.

Depuis le PC, rendre accessible un service exécuté localement sur le port 4321 :

ssh -p 61417 -L 4321:localhost:4321 claude@SERVER
Explication des options
OptionEffet
-p 61417Port sur lequel le serveur SSH écoute (celui choisi à l’étape 5)
-L 4321:localhost:4321Redirection de port local : les connexions au port local 4321 sont retransmises par le tunnel vers localhost:4321 du point de vue du serveur
claude@SERVERUtilisateur et hôte cible de la connexion SSH

Ouvrir ensuite http://localhost:4321 dans le navigateur local. Le trafic passe intégralement par la connexion SSH existante et authentifiée, sans devoir ouvrir le moindre port supplémentaire dans le pare-feu.

Accès depuis l’iPhone

L’accès en déplacement fonctionne selon le même modèle de sécurité que depuis le PC. Il suffit d’un client SSH avec gestion des clés. Termius, Blink Shell et Secure ShellFish sont répandus ; tous peuvent générer des clés Ed25519 et les stocker dans le trousseau iOS, parfois protégées par Face ID.

La procédure correspond à l’étape 3, mais sur l’iPhone :

  1. Générer dans le client SSH une clé Ed25519 dédiée à l’iPhone, sans copier la clé du PC. La clé privée reste dans le trousseau de l’appareil.
  2. Ajouter la clé publique de l’iPhone comme ligne supplémentaire dans ~/.ssh/authorized_keys sur le serveur, avec un commentaire explicite (iphone-15).
  3. Créer la connexion dans le client : adresse du serveur, utilisateur claude, port 61417, clé de l’iPhone pour l’authentification.

C’est précisément pourquoi une clé distincte par appareil est utile : si l’iPhone est perdu, il suffit de supprimer du serveur la ligne iphone-15 de authorized_keys, et l’appareil est exclu tandis que l’accès PC et toutes les autres clés continuent de fonctionner sans changement.

Après la connexion, récupérer la session Claude en cours avec tmux attach -t claude et reprendre là où l’on s’était arrêté à son bureau. Le tunnel de port de l’étape 10 fonctionne également depuis iOS ; Termius et Secure ShellFish prennent en charge la redirection de port.

Checklist

Voici le déroulement complet en résumé :

  1. Debian 13 installé et entièrement mis à jour avec apt full-upgrade.
  2. Utilisateur dédié claude avec droits sudo ; connexion root directe désormais inutilisée.
  3. Clés Ed25519 protégées par phrase secrète, une par appareil ; uniquement des clés publiques dans authorized_keys.
  4. sshd renforcé : PermitRootLogin no, PasswordAuthentication no; vérifié avec sshd -t avant rechargement, session existante laissée ouverte jusqu’au test.
  5. SSH sur le port 61417, défini sur ssh.socket en cas d’activation par socket, sinon dans la configuration sshd.
  6. Pare-feu du fournisseur : DROP entrant par défaut, seule exception TCP 61417 ; trafic sortant autorisé.
  7. Surface d’attaque vérifiée avec ss -lntup (seul SSH est public, chronyd est local) et systemctl --failed (aucune erreur).
  8. Claude Code authentifié à nouveau sur le serveur, exploité dans une session tmux.
  9. Hygiène des données : aucune clé privée ni donnée d’accès sur le serveur, configuration d’abord vérifiée via un dossier de migration.
  10. Aucun port supplémentaire ; les aperçus web passent par un tunnel SSH.

Après cette configuration, seul SSH est accessible depuis l’extérieur sur le port défini, et exclusivement avec une clé protégée par phrase secrète. Claude Code fonctionne indépendamment de l’appareil terminal dans tmux ; les aperçus web restent accessibles via des tunnels SSH, sans ouvrir de port supplémentaire.

Sources

  1. OpenSSH Manual – sshd_config(5)

    Référence de toutes les directives sshd, notamment PermitRootLogin, PasswordAuthentication et PubkeyAuthentication.

    https://man.openbsd.org/sshd_config
  2. Debian Wiki – SSH

    Indications spécifiques à Debian concernant la configuration SSH, y compris les fichiers Drop-in sous /etc/ssh/sshd_config.d/.

    https://wiki.debian.org/SSH
  3. systemd.socket(5) – freedesktop.org

    Fonctionnement de l’activation par socket et de la directive ListenStream=, utile pour changer le port SSH sous Debian 13.

    https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html
  4. ss(8) – iproute2 Manpage

    Options de ss pour lister les sockets en écoute avec leur processus et leur adresse de liaison.

    https://man7.org/linux/man-pages/man8/ss.8.html
  5. Claude Code – Documentation officielle

    Installation, authentification et exploitation de Claude Code.

    https://docs.claude.com/en/docs/claude-code/overview

Commentaires

Les commentaires sont chargés depuis GitHub / Giscus.