Tu as verrouillé l’interface web de Proxmox dans la partie 1 : compte dédié, double authentification, pare-feu. La porte de devant est solide. Mais il reste une entrée dont on n’a pas parlé : l’accès SSH au système Debian qui fait tourner Proxmox. Durcir SSH — désactiver le login root, passer à l’authentification par clé et bloquer les tentatives répétées avec fail2ban — Dans cet article, nous allons voir comment mettre en place tout ça .

Avant d’aller plus loin, une question honnête, parce que c’est sûrement celle que tu te poses : faut-il vraiment sécuriser le SSH d’un homelab privé ? La réponse franche, c’est : pas de façon urgente.Si ton serveur est derrière un pare-feu (OPNsense ou autre), il n’est pas exposé à Internet, et la partie 1 a déjà restreint l’accès SSH à ton seul réseau local. Concrètement, aujourd’hui, ton SSH n’est pas ta faille la plus pressante. Si tu ne faisais rien de plus, tu ne te ferais pas pirater demain matin.

Alors pourquoi un article entier dessus ? Pour deux raisons, et c’est important que tu les comprennes bien.

La première, c’est l’hygiène. Durcir le SSH d’un serveur, c’est comme fermer sa voiture à clé même dans un garage fermé : ça ne te sauve pas aujourd’hui, mais c’est un réflexe sain qui ne coûte presque rien à prendre. Un mot de passe root qui traîne sur une machine, même privée, c’est une mauvaise habitude qu’on ne veut pas laisser s’installer.

La seconde, et c’est la vraie raison, c’est que la méthode que tu vas apprendre ici est exactement et obligatoirement celle a utiliser sur une machine exposée sur le net — comme un VPS. Là, ce n’est plus du confort : dès qu’un serveur expose son port SSH sur le réseau public, il reçoit en continu des milliers de tentatives de connexion automatisées qui testent root avec des dictionnaires de mots de passe. Tant que root accepte un mot de passe, il suffit qu’un seul essai tombe juste pour que tout tombe. Sur le VPS, le durcissement SSH n’est pas optionnel, il est en première ligne.

L’intérêt de le faire maintenant, sur ton homelab, c’est que tu apprends à faire là où l’erreur ne coûte rien. Si tu te verrouilles dehors sur ta machine de démo, tu pourras régler le problème en deux clics depuis l’interface. Le jour où tu feras la même manip sur un VPS, tu la feras les yeux fermés, parce que tu l’auras déjà faite ici.

SSH, c’est la porte de service de ton serveur. L’interface web, c’est l’entrée principale, celle qu’on voit. SSH, c’est l’entrée discrète à l’arrière, par laquelle on fait passer la maintenance, les transferts de fichiers, les scripts — et que tout le monde oublie de verrouiller. Pour l’instant, sur ton serveur, cette porte s’ouvre encore avec root et un mot de passe : exactement la situation qu’on a corrigée pour l’interface en partie 1, mais à l’étage en dessous.

La bonne nouvelle : trois changements suffisent, dans une seule session de terminal. Contrairement à la partie 1 où tout se passait dans l’interface web, on travaille cette fois en ligne de commande — mais ne t’inquiètes pas, je t’expliquerai chaque commande, une par une, avant de les lancer. Ce durcissement n’a rien de spécifique à Proxmox : il vaut pour n’importe quel serveur Linux. Ce que tu apprends ici, tu le rejoueras à l’identique sur ton VPS, sur un Raspberry Pi ou n’importe quelle machine que tu administres à distance.

Un point à régler parce qu’il découle directement de la partie 1. Le compte dédié que tu as créé là-bas vit dans le realm Proxmox VE (@pve) : il pilote l’interface, mais il n’ouvre aucun shell sur le système Debian en dessous. Si on coupe l’accès root en SSH sans rien préparer, tu te retrouves sans le moindre compte capable de se connecter — enfermé dehors. La toute première étape sera donc de créer un vrai utilisateur système Linux, avec les droits d’administration via sudo. C’est ta clé perso, mais côté Debian cette fois.

À la fin de cet article, plus personne ne se connectera à ton serveur sans ta clé privée — ni avec root, ni avec un mot de passe, jamais. Et les robots qui s’acharneront se feront bannir automatiquement avant d’avoir eu la moindre chance.

On va procéder dans cet ordre :

  1. Créer un utilisateur système avec sudo — ta clé perso côté Debian, distincte du compte @pve de l’interface.
  2. Générer une paire de clés SSH sur ton poste de travail.
  3. Déposer ta clé publique sur le serveur, et tester la connexion par clé avant de toucher à quoi que ce soit.
  4. Durcir la configuration SSH : login root désactivé, mot de passe désactivé, clé obligatoire.
  5. Installer fail2ban pour bannir automatiquement les adresses qui multiplient les échecs.
  6. Tout vérifier, serrure par serrure.

💡 Une note sur les captures de cet article. Le nœud Proxmox s’appelle ici HMT et non LABHMT comme dans la partie 1 : entre les deux articles, j’ai monté une machine physique dédiée à la production de contenu pour Home Made Tech, et c’est sur elle que tournent désormais toutes les démos. La procédure reste strictement identique, seul le nom du nœud change.

Une règle de méthode avant de commencer, et elle n’est pas négociable : à chaque étape sensible, on garde une session SSH ouverte pendant qu’on en teste une nouvelle dans une seconde fenêtre. Tant que la nouvelle connexion n’est pas validée, on ne ferme jamais l’ancienne. C’est le réflexe qui t’évite de te verrouiller dehors. Sur ton serveur Proxmox, tu gardes de toute façon un filet — le shell web de l’interface reste un accès root, même SSH bouclé. Mais sur un VPS, ce filet n’existe pas : la prudence n’y est pas une option.

1. Créer ton utilisateur système avec sudo

C’est ta clé perso, version Debian. Souviens-toi du piège annoncé en intro : le compte dédié de la partie 1 vit dans le realm Proxmox VE et n’ouvre aucun shell sur le système. Si tu désactivais root en SSH maintenant, plus aucun compte ne pourrait se connecter. On commence donc par créer un vrai utilisateur Linux, capable de se connecter en SSH et d’exécuter les commandes d’administration via sudo.

Pour cette étape — et cette étape seulement — on se connecte encore en root. Ouvre une session SSH sur ton serveur, ou utilise le shell intégré à l’interface Proxmox (Datacenter → ton nœud → Shell). C’est la dernière fois qu’on utilise root aussi librement.

Écran de login Proxmox, connexion avec root@pam

Connexion à l’interface Proxmox avec le compte root@pam, dernière fois qu’on l’utilisera librement.

Première surprise pour qui vient d’un Ubuntu ou d’une Debian de bureau : sur Proxmox, la commande sudo n’existe pas. Proxmox est conçu pour être administré directement en root, donc le paquet n’est tout simplement pas installé. On va donc l’installer. La mise à jour de l’index des paquets juste avant n’est pas optionnelle : sans elle, le serveur risque de répondre que le paquet est introuvable.

apt update && apt install sudo
Commande apt update && apt install sudo dans le shell Proxmox

La commande à lancer en root depuis le shell Proxmox.

Maintenant, on crée l’utilisateur. La commande adduser de Debian est conviviale : elle crée le compte, son dossier personnel, et te demande un mot de passe de façon interactive. Ici je choisis de créer l’utilisateur “HMT”.Remplace “NOM” par celui que tu veux.

adduser "NOM"

Exemple:

adduser HMT

Elle va te demander un mot de passe — choisis-en un solide et unique. Ce mot de passe te servira pour deux choses : confirmer tes commandes sudo, et te connecter à la console physique en cas de pépin. Les autres champs (nom complet, téléphone…) sont facultatifs, tu peux les laisser vides en appuyant sur Entrée.

Installation de sudo terminée, puis adduser HMT

Création de l’utilisateur HMT après installation de sudo.

À ce stade, le compte existe mais c’est un utilisateur ordinaire : il peut se connecter, mais pas administrer le système. On lui donne les droits d’administration en l’ajoutant au groupe sudo. Sur Debian, c’est ce groupe précis qui autorise l’usage de sudo — pas un autre nom, pas un fichier à éditer à la main.

usermod -aG sudo HMT

Le -aG est important : le -a veut dire « ajoute à » (append). Si tu l’oublies et que tu écris juste -G, tu remplaces tous les groupes de l’utilisateur par le seul groupe sudo, ce qui peut casser des choses. Avec -aG, on ajoute sans rien retirer.

Commande usermod -aG sudo HMT après confirmation de la création du compte

Activation des droits d’administration sur le compte HMT.

⚠️ Le piège à ne pas commettre. L’appartenance à un groupe n’est prise en compte qu’à l’ouverture d’une nouvelle session. Si tu testes sudo dans la session où tu as lancé la commande, ça peut échouer alors que tout est correct. Il faut ouvrir une nouvelle connexion pour que le groupe soit réellement actif. C’est exactement ce qu’on va faire à l’étape suivante — et c’est aussi notre test.

Tester immédiatement — sans fermer ta session root

Même réflexe qu’en partie 1, et même logique que le « règle avant pare-feu » : on vérifie que la nouvelle clé tourne avant de changer quoi que ce soit. Surtout, garde ta session root ouverte. On ouvre une seconde fenêtre de terminal et on s’y connecte avec le nouveau compte :

ssh "NOM"@"ip Proxmox"

Exemple;

ssh HMT@192.168.70.2

⚠️ 192.168.70.2 est la nouvelle ip de mon Proxmox Demo. Remplace la par l’ ip de ton propre Proxmox.

La toute première connexion te demandera de confirmer l’empreinte du serveur (réponds yes), puis le mot de passe du compte HMT.

Première connexion SSH avec HMT, demande de mot de passe

On est passé du terminal du PC au terminal Proxmox via SSH.

Une fois connecté, on demande à sudo de nous faire passer pour root le temps d’une commande. La toute première fois, sudo te demandera ton mot de passe (celui du compte, pas celui de root) :

sudo whoami

Si la réponse est root, c’est gagné : ton compte sait s’élever en administrateur. Ta clé perso côté Debian est opérationnelle.

sudo whoami renvoyant root dans la session HMT

Le test qui valide tout : sudo whoami renvoie root dans la nouvelle session.

Tant que ce test ne renvoie pas root, ne va pas plus loin et ne ferme pas ta session root. Reviens dans la fenêtre root et vérifie que l’utilisateur est bien dans le groupe sudo avec groups HMT (la liste affichée doit contenir sudo). C’est presque toujours soit le -aG oublié, soit le test fait dans l’ancienne session au lieu d’une nouvelle.

💡 Sur ton VPS, c’est souvent déjà à moitié fait. Les images Debian fournies par les hébergeurs installent fréquemment sudo d’office, et certaines créent même un utilisateur non-root dès le départ. Avant de recréer un compte, vérifie ce qui existe : which sudo te dira si sudo est présent, et getent group sudo te listera les comptes qui y sont déjà. Inutile de dupliquer ce qui est là.

2. Générer ta paire de clés SSH

Cette étape ne se passe pas sur le serveur, mais sur ton poste de travail — celui depuis lequel tu te connectes en SSH. C’est important : la moitié sensible de la clé, celle qu’on appelle la clé privée, doit rester sur ta machine et n’en bouger jamais.

Avant de cliquer sur quoi que ce soit, un détour de deux minutes pour comprendre ce qu’on fait. Une paire de clés SSH, c’est deux fichiers qui forment un duo mathématiquement lié. La clé publique est faite pour être distribuée — tu la déposes sur chaque serveur où tu veux te connecter. La clé privée reste sur ton poste, à un seul endroit, et tu ne la partages avec personne, jamais, sous aucun prétexte.

L’analogie utile, c’est le cadenas et sa clé. La clé publique, c’est le cadenas : tu peux en faire des copies, les laisser traîner, les coller sur tous les serveurs du monde, ça n’a aucune importance. La clé privée, c’est la seule qui ouvre ces cadenas. Quand tu te connectes, le serveur te lance un défi qu’il « cadenasse » avec ta clé publique, et seule ta clé privée peut le résoudre. Si quelqu’un met la main sur ta clé privée, il peut se connecter à ta place sur tous les serveurs où le cadenas correspondant est posé.

Pourquoi c’est plus solide qu’un mot de passe ? Un mot de passe, ça se devine, ça se vole dans une fuite de données, ça se brute-force. Une clé SSH moderne, c’est un secret de 256 bits que personne ne va trouver par essai-erreur, même avec des années de calcul. Et contrairement à un mot de passe, elle n’est jamais transmise au serveur lors de la connexion : seule la preuve qu’on la possède transite.

Choisir le bon type de clé

Le type recommandé en 2026, sans hésitation, c’est ed25519. C’est rapide, court, et reposant sur des fondations mathématiques modernes (les courbes elliptiques). Tu peux l’avoir croisé sous l’autre nom — RSA — qui reste fonctionnel mais demande des clés bien plus longues (4096 bits minimum) pour offrir une sécurité équivalente, et qui disparaît progressivement des bonnes pratiques. On part directement sur ed25519.

💡 Sous Windows, c’est la même chose — ou presque. Depuis Windows 10 et 11, le client OpenSSH est intégré nativement : la commande ssh-keygen fonctionne directement, sans rien installer. Ouvre simplement PowerShell ou l’Invite de commandes (cherche powershell ou cmd dans le menu Démarrer), et tu peux taper toutes les commandes de cette section à l’identique. La seule différence est le chemin où vivent tes clés : sous Windows, c’est C:\Users\toncompte\.ssh\ au lieu de ~/.ssh. Tu peux y accéder avec ~\.ssh (PowerShell comprend le tilde) ou %USERPROFILE%\.ssh (Invite de commandes). Tout le reste — ssh-keygen, ssh, scp, ssh-copy-id à la section suivante — fonctionne pareil. Si tu tombes sur de vieux tutoriels qui te font installer PuTTY et PuTTYgen, tu peux les ignorer en 2026 : OpenSSH natif fait la même chose, en plus propre.

Vérifier que tu n’as pas déjà une clé

Avant d’en créer une nouvelle, on vérifie qu’il n’y en a pas déjà une sur ton poste. Si tu utilises Git, par exemple, tu en as peut-être déjà une.

ls -al ~/.ssh

(Sous Windows : dir ~\.ssh dans PowerShell, ou dir %USERPROFILE%\.ssh à l’Invite de commandes. Si le dossier n’existe pas, c’est normal — ssh-keygen le créera.)

Si tu vois un fichier id_ed25519 (clé privée) et son jumeau id_ed25519.pub (clé publique), tu as déjà une paire utilisable et tu peux directement passer à l’étape suivante. Si tu vois id_rsa et id_rsa.pub, c’est une vieille clé RSA qui fonctionnera aussi, mais profiter de l’occasion pour passer en ed25519 est une bonne idée. Et si le dossier .ssh n’existe pas ou est vide, on en crée une.

ls -al ~/.ssh listant la paire de clés, puis cat id_ed25519.pub affichant la clé publique

Vérification du contenu du dossier ~/.ssh puis affichage de la clé publique.

Créer la paire de clés

La commande est courte mais chaque option a son rôle :

ssh-keygen -t ed25519 -C "HMT@beastmaker-PC"

Le -t ed25519 choisit l’algorithme. Le -C ajoute un commentaire à la clé — pas un mot de passe, juste un libellé qui apparaîtra dans la clé pour t’aider à la reconnaître plus tard. Mets quelque chose qui te dit d’où vient cette clé : ton compte et ton poste suffisent.

ssh-keygen va te poser deux questions. « Enter file in which to save the key » — laisse vide et appuie sur Entrée, le chemin par défaut est celui que SSH utilise automatiquement. « Enter passphrase » — c’est le point important.

⚠️ La passphrase de la clé privée. SSH te propose ici de chiffrer ta clé privée avec une passphrase. Ce n’est pas le mot de passe d’un compte, c’est une protection locale du fichier de clé : si quelqu’un copie ta clé privée sans la passphrase qui va avec, elle est inutilisable. Sans passphrase, à l’inverse, quiconque copie le fichier peut se connecter à ta place sur tous les serveurs où ta clé publique est déposée.

Recommandation : mets une passphrase. Tu ne la taperas pas à chaque connexion, parce qu’un agent SSH la garde en mémoire pour la session — tu la saisis une fois en début de journée, et c’est terminé.

Une fois validée, la commande t’affiche un schéma ASCII appelé « randomart » : c’est une empreinte visuelle de ta clé, inutile pour l’usage courant.

Vérifier ce qui a été créé

ls -al ~/.ssh

Tu dois voir deux fichiers : id_ed25519, ta clé privée (permissions -rw-------, lisible uniquement par toi, ne sort jamais de ton poste), et id_ed25519.pub, ta clé publique (permissions -rw-r--r--, qu’on déposera sur le serveur). Tu peux lire la publique sans risque :

cat ~/.ssh/id_ed25519.pub

Une seule ligne s’affiche, commençant par ssh-ed25519, suivie d’une longue chaîne, et finissant par ton commentaire.

⚠️ Ne confonds jamais les deux fichiers. Le suffixe .pub est la seule différence visuelle, et c’est elle qui fait toute la différence entre « je dépose mon cadenas » et « je donne ma clé privée à un inconnu ». Quand on te demande « ta clé SSH », c’est toujours celle qui se termine par .pub.

3. Déposer ta clé publique sur le serveur et tester

L’objectif de cette étape est simple à formuler et critique à exécuter : avant de toucher à la moindre option de durcissement, on s’assure que la connexion par clé fonctionne réellement. Tant que ce n’est pas le cas, on ne touche à rien. C’est la règle d’or de tout l’article, et elle a sauvé plus d’administrateurs que toutes les sauvegardes du monde.

Pour rappel : à ce stade, ton serveur accepte encore les connexions par mot de passe. Ta clé publique n’est encore nulle part sur lui. On va l’y déposer pendant que le mot de passe sert encore de filet, et on testera la connexion par clé pendant que ce filet est toujours en place. Si quelque chose foire, le mot de passe sera là pour rattraper.

Déposer la clé avec ssh-copy-id

Il existe une commande dédiée à cette opération, qui évite toute manipulation manuelle dans le fichier authorized_keys du serveur : ssh-copy-id. Elle se connecte au serveur avec ton mot de passe (le compte HMT), crée le dossier ~/.ssh s’il n’existe pas avec les bonnes permissions, et ajoute ta clé publique au fichier authorized_keys du compte distant.

Sur ton poste :

ssh-copy-id HMT@192.168.70.2

La commande te demandera le mot de passe du compte HMT. Une fois validée, elle te répond combien de clés elle a ajoutées (Number of key(s) added: 1) et te suggère de tester la connexion.

Sortie de ssh-copy-id réussie

La clé publique a été ajoutée au compte HMT sur le serveur.

💡 Si tu as plusieurs clés sur ton poste, ssh-copy-id envoie par défaut toutes les clés publiques disponibles dans ton dossier ~/.ssh. Pour n’envoyer qu’une clé précise, utilise l’option -i : ssh-copy-id -i ~/.ssh/id_ed25519.pub HMT@192.168.70.2. C’est plus propre et tu sais exactement ce que tu déposes.

⚠️ Sous Windows, ssh-copy-id n’est pas inclus dans le client OpenSSH natif de Microsoft, c’est un des rares trous de la version Windows. La solution équivalente en une ligne PowerShell : type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh HMT@192.168.70.2 "cat >> ~/.ssh/authorized_keys" — ça envoie le contenu de ta clé publique au serveur, qui l’ajoute à son authorized_keys. Résultat strictement identique.

Le test qui sauve : se connecter par clé pendant que le mot de passe est encore là

C’est l’étape qui sépare ceux qui dorment tranquille de ceux qui passent la nuit à dépanner depuis une console physique. Toujours sur ton poste :

ssh HMT@192.168.70.2

Trois cas possibles, et il faut savoir reconnaître lequel tu as :

Cas 1 — la connexion s’ouvre sans rien demander. C’est le cas si tu n’avais pas mis de passphrase sur ta clé privée. Tu arrives directement au prompt HMT@HMT:~$. La connexion par clé fonctionne.

Cas 2 — la connexion demande la passphrase de ta clé. Si tu avais protégé ta clé privée par une passphrase, ton système te la demande maintenant pour pouvoir l’utiliser. Tu tapes la passphrase, et la connexion s’ouvre. Note bien : ce n’est pas le mot de passe du compte HMT qui t’est demandé ici, c’est la passphrase locale de ta clé privée. Cette demande prouve justement que tu te connectes par clé, pas par mot de passe.

Cas 3 — la connexion demande le mot de passe du compte HMT. C’est le mauvais signal. SSH n’a pas réussi à utiliser ta clé et il retombe sur le mot de passe en mode secours. Si tu en arrives là, ne valide pas et ne continue surtout pas vers la section 4. Tu n’as pas encore la clé qui marche, et désactiver le mot de passe maintenant te verrouillerait dehors.

Pour distinguer rapidement le cas 2 du cas 3, regarde le texte exact :

  • Passphrase de la clé : Enter passphrase for key '/home/beastmaker/.ssh/id_ed25519':
  • Mot de passe du compte : HMT@192.168.70.2's password:

Le premier prouve que SSH a trouvé ta clé. Le second prouve qu’il a échoué et qu’il bascule sur le mot de passe.

Connexion SSH par clé sans demande de mot de passe

La passphrase de la clé est demandée — c’est le bon signe : SSH se connecte par clé.

Si tu es dans le cas 3 : diagnostic rapide

Pas de panique, le filet est encore là. Reste dans cette session pour ne pas perdre ton accès, et lance dans une nouvelle fenêtre :

ssh -v HMT@192.168.70.2

Le -v (verbose) affiche tout ce que SSH essaie. Trois lignes à chercher :

  • Offering public key: /home/.../id_ed25519 → ton client propose bien la clé.
  • Server accepts key: ... → le serveur l’a acceptée. Si tu vois ces deux lignes, le problème est ailleurs.
  • Authentications that can continue: publickey,password → si SSH retombe ensuite sur password, c’est que le serveur a refusé ta clé.

Les causes habituelles, dans l’ordre de fréquence :

  1. La clé publique n’est pas (ou pas correctement) dans ~/.ssh/authorized_keys du compte HMT sur le serveur. Vérifie sur le serveur : cat ~/.ssh/authorized_keys doit afficher la même ligne que ton id_ed25519.pub local.
  2. Les permissions du dossier ~/.ssh ou du fichier authorized_keys sur le serveur sont trop ouvertes. SSH refuse silencieusement d’utiliser une clé si les permissions ne sont pas correctes — c’est volontaire, c’est sa façon de protéger les comptes mal configurés. Sur le serveur : chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys.
  3. Tu te connectes avec le mauvais nom d’utilisateur. Vérifie la commande exacte.

Une fois la cause corrigée, retente ssh HMT@192.168.70.2. Tu dois être dans le cas 1 ou 2.

Ne ferme pas encore cette session

Tu as maintenant une session SSH ouverte sur le serveur, connectée par clé (et non par mot de passe). C’est ton filet pour la section suivante : on garde cette session active pendant qu’on durcit la configuration SSH, et on ouvre une seconde fenêtre pour tester. Si quelque chose foire pendant le durcissement, cette session reste valide tant que tu ne la fermes pas — même si la nouvelle configuration interdit toute nouvelle connexion.

C’est le réflexe à graver une bonne fois pour toutes : pendant une opération sensible sur SSH, on ne ferme jamais une session qui marche tant qu’une nouvelle n’a pas été validée.

4. Durcir la configuration SSH

C’est là qu’on ferme les trois portes restantes : login root, authentification par mot de passe, et tout ce qui n’est pas une clé. Le test de la section 3 a prouvé que tu peux te connecter par clé. À partir de maintenant, on peut donc fermer le reste sans craindre de se verrouiller — à condition de procéder dans le bon ordre.

Pourquoi un fichier drop-in et pas le fichier principal

Le réflexe historique, c’est d’ouvrir /etc/ssh/sshd_config et d’y modifier directement les directives. Ça marche, mais c’est une mauvaise pratique pour deux raisons.

D’abord, ce fichier est susceptible d’être mis à jour par le système lors d’un apt upgrade d’OpenSSH. Les versions récentes du paquet Debian sont prudentes, mais c’est une source d’ennuis classique : tu finis avec des .dpkg-old ou .dpkg-dist qui traînent, des morceaux de config qui se perdent, et tu ne sais plus quelle est ta version de référence.

Ensuite, en mélangeant tes durcissements aux centaines de lignes commentées par défaut, ton intention devient invisible. Six mois plus tard, tu rouvres le fichier et tu cherches ce que tu as changé.

La pratique propre, supportée nativement par OpenSSH depuis longtemps, c’est le fichier drop-in. Tu crées un fichier séparé dans /etc/ssh/sshd_config.d/, qui ne contient que tes durcissements. Le fichier principal reste tel que Debian l’a livré (et se met donc à jour proprement), et il charge automatiquement tous les fichiers .conf du dossier sshd_config.d. Ta config personnelle vit à part, lisible, versionnable, supprimable d’un coup si besoin.

💡 Vérifie d’abord que ton sshd_config charge bien les drop-in. Sur Debian 13 et Proxmox VE 9, c’est le cas par défaut, mais une commande te le confirme en deux secondes : grep -i "^Include" /etc/ssh/sshd_config. Tu dois voir une ligne Include /etc/ssh/sshd_config.d/*.conf. Si rien ne sort, ajoute-la en première ligne du fichier principal avant de continuer.

Créer le fichier de durcissement

Dans ta session SSH actuelle (celle qui marche, celle qu’on ne ferme pas), on crée le fichier avec les droits d’admin :

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Le nom du fichier suit une convention simple : un préfixe numérique (99-) pour l’ordre de lecture (les fichiers sont chargés par ordre alphabétique, et 99- garantit que tes réglages passent en dernier et l’emportent sur le reste), un nom parlant, l’extension .conf obligatoire.

Commande sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Ouverture du fichier de durcissement dans nano.

Colle dans le fichier ces quatre directives :

# Hardening SSH — voir homemadetech.fr/posts/securiser-proxmox-partie-2
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes

Lecture ligne par ligne, parce que chaque option a son rôle :

  • PermitRootLogin no — désactive complètement la connexion SSH avec le compte root. C’est le cœur du durcissement. Les robots qui testent en boucle root avec des milliers de mots de passe se feront refuser au niveau du protocole, avant même d’avoir une chance de tomber juste.
  • PasswordAuthentication no — désactive l’authentification par mot de passe pour tous les comptes. Plus aucun mot de passe n’est accepté pour ouvrir une session SSH. C’est la clé ou rien.
  • KbdInteractiveAuthentication no — désactive aussi l’authentification dite « interactive clavier », un autre canal qu’OpenSSH expose et qui peut, selon la configuration PAM, contourner PasswordAuthentication. La désactiver explicitement ferme ce contournement classique. Sans cette ligne, ton durcissement a un trou.
  • PubkeyAuthentication yes — affirme explicitement que l’authentification par clé est autorisée. C’est l’option par défaut, mais l’écrire noir sur blanc rend ta config auto-documentée.

Sauvegarde et ferme nano (Ctrl+O, Entrée, Ctrl+X).

Fichier 99-hardening.conf ouvert dans nano avec les quatre directives

Le fichier drop-in 99-hardening.conf avec les quatre directives de durcissement.

⚠️ Sur Proxmox en cluster, remplace PermitRootLogin no par PermitRootLogin prohibit-password. Le compromis officiel : root reste interdit en mot de passe, mais autorisé par clé — ce qui est nécessaire pour les opérations cross-node de PVE (migrations live, console d’un autre nœud). Sur un nœud unique comme ton serveur de démo, no est propre et préférable. Si tu passes en cluster plus tard, il te suffira d’éditer ce drop-in.

Valider la syntaxe avant de redémarrer

C’est l’étape qu’on saute trop souvent et qui finit par coûter cher. OpenSSH propose un mode de test qui vérifie la syntaxe complète de la configuration sans rien appliquer. Si tu as fait une faute de frappe, il te le dit. Si tout est OK, il ne dit rien.

sudo sshd -t

Si la commande te répond par un silence (retour au prompt sans message), ta config est syntaxiquement valide. Si elle te répond par une erreur (bad configuration option, ligne X), corrige le fichier avant d’aller plus loin. Ne redémarre jamais sshd sur une config qui n’a pas passé sshd -t. C’est une règle absolue.

Recharger sshd

Maintenant qu’on sait que la config est valide, on dit au service SSH d’en tenir compte. On utilise reload, plus doux qu’un restart, et qui surtout ne casse pas les sessions SSH actuellement ouvertes — y compris ta session de filet.

sudo systemctl reload ssh

💡 Pour ta culture : sur Debian, le service s’appelle ssh (et non sshd comme sur d’autres distributions). Si une commande te dit « Unit sshd.service could not be found », c’est juste un alias qui manque. Utilise ssh et tout fonctionne.

Le test critique — sans fermer ta session de filet

C’est l’instant où on prouve que tout marche. Tu as toujours ta session SSH ouverte (celle de la section 3). On va en ouvrir une deuxième dans une nouvelle fenêtre sur ton poste, et tester deux choses, dans cet ordre.

Test 1 — la connexion par clé doit toujours marcher. Nouvelle fenêtre :

ssh HMT@192.168.70.2

Tu dois retomber dans le cas 2 (passphrase de ta clé demandée, puis connexion ouverte). Si oui, ta clé continue d’être acceptée, et tu peux fermer cette fenêtre.

Test 2 — la connexion en root doit être refusée. Toujours dans une nouvelle fenêtre :

ssh root@192.168.70.2

La réponse attendue, sans ambiguïté : Permission denied (publickey). SSH refuse même de te demander un mot de passe — c’est la directive PermitRootLogin no qui parle.

Sortie Permission denied (publickey) pour ssh root@192.168.70.2

Root est refusé : la directive PermitRootLogin no fait son travail.

Si tu obtiens à la place une demande de mot de passe pour root, c’est que PermitRootLogin no n’a pas pris effet. Vérifie deux choses : (1) ton fichier 99-hardening.conf est bien dans /etc/ssh/sshd_config.d/ et pas ailleurs, et (2) le reload s’est bien passé (sudo systemctl status ssh doit afficher active (running) sans erreur).

⚠️ Ne ferme toujours pas ta session de filet de la section 3. On a encore une étape : fail2ban. Tant que la section 5 n’est pas validée, on garde au moins une session ouverte.

5. Bannir les attaquants automatiquement avec fail2ban

Tu as fermé la porte aux connexions par mot de passe et à root. Concrètement, plus aucun robot qui teste des identifiants ne peut entrer. Alors pourquoi installer encore quelque chose ?

Pour deux raisons. La première est défensive : un attaquant peut continuer à essayer, indéfiniment, des milliers de tentatives par minute. Chaque tentative consomme un peu de CPU, de bande passante, et pollue tes logs au point qu’un vrai incident s’y noie. La seconde est plus prosaïque : sur un VPS exposé, ton journal d’authentification grossit de plusieurs mégaoctets par jour de tentatives échouées. C’est du bruit qu’on peut faire taire.

C’est exactement le rôle de fail2ban : il surveille les logs en temps réel, repère les adresses IP qui multiplient les échecs d’authentification, et les bannit automatiquement au niveau du pare-feu pour une durée définie. Au bout de cinq mauvaises tentatives, l’IP est bloquée et ne peut même plus atteindre ton port SSH pendant le temps du ban. Les robots passent à la cible suivante.

Là encore, garde en tête la logique de cet article : sur ton homelab derrière OPNsense, fail2ban n’a pas grand-chose à se mettre sous la dent, parce que personne d’extérieur n’atteint ton SSH. C’est sur le VPS qu’il prend tout son sens, en attrapant les robots qui scannent Internet en permanence. On l’installe ici pour apprendre le geste là où il est sans risque.

Installer fail2ban

Dans ta session SSH (la session filet, toujours en place) :

sudo apt update && sudo apt install fail2ban
Commande sudo apt update && sudo apt install fail2ban

Installation de fail2ban depuis les dépôts Debian.

Le service démarre automatiquement à la fin de l’installation. On le vérifie tout de suite :

sudo systemctl status fail2ban

Tu dois voir active (running) en vert. Si oui, fail2ban tourne avec sa configuration par défaut. Mais cette configuration par défaut, justement, on va y toucher — et là encore, on le fait proprement.

systemctl status fail2ban montrant active (running)

Le service fail2ban est actif et fonctionne avec sa configuration par défaut.

Pourquoi un fichier jail.local et pas l’édition de jail.conf

Même logique que pour sshd_config : le fichier livré par le paquet (/etc/fail2ban/jail.conf) ne doit jamais être édité. Il sera potentiellement réécrit à la prochaine mise à jour de fail2ban, et tes réglages disparaîtront avec.

La pratique propre est documentée par fail2ban lui-même : on crée un fichier /etc/fail2ban/jail.local, qui surcharge jail.conf ligne par ligne. Ce qui est dans jail.local gagne, le reste reste géré par jail.conf (et se met donc à jour normalement).

Créer la configuration locale

sudo nano /etc/fail2ban/jail.local

Colle ce contenu :

# Configuration locale fail2ban — durcissement SSH
# Voir homemadetech.fr/posts/securiser-proxmox-partie-2

[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 192.168.70.0/24

[sshd]
enabled = true

Lecture ligne par ligne :

  • bantime = 1h — durée du bannissement quand une IP se fait attraper. Une heure est un compromis raisonnable : assez long pour décourager un robot et libérer tes logs, assez court pour qu’un humain qui s’est juste planté trois fois récupère son accès dans la matinée. Tu peux mettre 1d (un jour) ou 1w (une semaine) sur un VPS exposé à du bruit constant.

  • findtime = 10m — la fenêtre de temps pendant laquelle on compte les échecs. Avec maxretry = 5, ça veut dire : si une IP fait cinq mauvaises tentatives en moins de dix minutes, elle est bannie. Une IP qui se trompe deux fois aujourd’hui, deux fois la semaine prochaine, ne sera jamais bannie — findtime empêche les faux positifs sur la durée.

  • maxretry = 5 — le nombre d’échecs tolérés dans la fenêtre. Cinq est un bon point d’équilibre. Trop bas (1 ou 2), tu te bannis toi-même au premier mot de passe mal tapé. Trop haut (50), tu laisses des minutes entières d’attaque passer.

  • ignoreip — la liste blanche, et c’est la ligne la plus importante. Les IP listées ici ne seront jamais bannies, même avec mille mauvaises tentatives. On met systématiquement les boucles locales (127.0.0.1/8 en IPv4, ::1 en IPv6), et ton propre réseau d’administration — ici 192.168.70.0/24, ton réseau démo. C’est ton filet : si tu te plantes en testant des connexions, tu ne te bannis pas toi-même depuis ton poste.

  • [sshd] / enabled = true — active explicitement la jail dédiée à SSH. Elle existe déjà dans jail.conf et sait quoi surveiller, il suffit de la déclencher.

Fichier jail.local ouvert dans nano avec son contenu

Le fichier jail.local avec les paramètres de bannissement et la liste blanche.

⚠️ ignoreip n’est pas optionnel. Sur un homelab tu peux presque t’en passer. Sur un VPS, c’est crucial : si tu administres depuis ton domicile avec une IP fixe ou semi-fixe, ajoute-la à ignoreip. Sinon, le jour où tu retapes mal ta passphrase cinq fois de suite à 23h, tu es banni de ton propre serveur pour une heure, sans moyen de te débannir tant que le ban n’expire pas — sauf à passer par la console physique ou le shell web Proxmox (le filet ultime).

Sauvegarde et ferme (Ctrl+O, Entrée, Ctrl+X), puis recharge fail2ban pour qu’il prenne ta config en compte :

sudo systemctl restart fail2ban

Vérifier que la jail SSH tourne

On contrôle que fail2ban a bien chargé la jail SSH :

sudo fail2ban-client status

La sortie doit lister sshd dans les jails actives. Pour voir l’état précis de cette jail :

sudo fail2ban-client status sshd

Tu obtiens un petit tableau : nombre d’échecs détectés, nombre d’IP actuellement bannies (0 pour l’instant), nombre total d’IP bannies depuis le démarrage. Sur un serveur fraîchement installé, tout est à zéro. C’est normal.

Sortie de fail2ban-client status sshd avec la jail active

La jail SSH est active : fail2ban surveille les tentatives d’authentification en temps réel.

💡 Tester sans risque : un faux ban depuis une adresse fictive. Si tu veux voir fail2ban en action sans te bannir toi-même, demande-lui de bannir manuellement une IP qui n’existe pas : sudo fail2ban-client set sshd banip 203.0.113.42. C’est une adresse de documentation officielle, jamais routée. Tu vois alors « Currently banned » passer à 1, et tu peux la débannir avec sudo fail2ban-client set sshd unbanip 203.0.113.42. Bonne façon de constater que la machinerie fonctionne.

Sur ton VPS, fail2ban et le pare-feu Proxmox cohabitent

Petite subtilité utile. La partie 1 a activé le pare-feu intégré de Proxmox sur ton nœud. Fail2ban, lui, manipule iptables directement pour ajouter ses règles de bannissement. Les deux cohabitent généralement sans souci sur Proxmox VE 9, mais ce sont deux couches indépendantes : l’une filtre par IP en réaction aux échecs (fail2ban), l’autre filtre par règles statiques en permanence (le pare-feu). Tu as besoin des deux, l’un ne remplace pas l’autre.

6. Vérifier que tout fonctionne, serrure par serrure

Tu as enchaîné les étapes proprement, mais c’est maintenant qu’il faut prendre cinq minutes pour valider l’ensemble. Le but n’est pas d’ajouter de la sécurité — elle est déjà là — mais de te donner la certitude qu’elle est là. C’est cette certitude qui te permet de fermer la session filet et de dormir tranquille.

On valide dans cet ordre : la connexion par clé fonctionne, le compte root est refusé, le mot de passe est refusé, fail2ban est armé. Pour chacun, je te dis quelle commande lancer et quelle réponse exacte attendre — parce que c’est la formulation précise des messages qui te dit ce qui se passe vraiment.

Vérification 1 — La connexion par clé fonctionne.

ssh HMT@192.168.70.2

Tu dois retomber dans le cas 2 : passphrase de ta clé demandée, puis prompt HMT@HMT:~$. Si oui, ta clé continue d’être acceptée et fail2ban ne te gêne pas.

Vérification 2 — Le compte root est refusé.

ssh root@192.168.70.2

Réponse attendue : Permission denied (publickey). SSH ne te demande même pas de mot de passe — c’est PermitRootLogin no qui parle. Aucun robot ne peut désormais brute-forcer root sur ton serveur.

Vérification 3 — Le mot de passe est refusé.

C’est le test qui prouve que PasswordAuthentication no a pris effet. On force SSH à n’essayer que le mot de passe :

ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no HMT@192.168.70.2

Réponse attendue : Permission denied (publickey). Le serveur te répond qu’il n’accepte que les clés — la voie mot de passe est bel et bien coupée, même pour un client qui la réclame explicitement. Si tu voyais Permission denied (publickey,password), ça voudrait dire que le mot de passe est encore accepté, et qu’il faut revenir à la section 4.

Vérification 4 — Fail2ban est armé.

sudo fail2ban-client status sshd

Tu dois voir la jail active, pointant vers le journal système, avec Currently banned: 0. Tout à zéro, c’est normal — ton serveur n’a pas encore subi d’attaque réelle. Ce qui compte, c’est que la jail apparaisse et tourne.

💡 Pour valider en conditions réelles, si tu veux y aller : depuis un appareil qui n’est pas dans le ignoreip (ton téléphone en 4G, par exemple), tente cinq mauvaises connexions en mot de passe en moins de dix minutes. À la sixième, ton IP doit apparaître dans Currently banned. Sur ton réseau démo, c’est instructif ; sur un serveur en production, c’est inutile, fail2ban attrapera de vrais robots dans la journée.

Tu peux maintenant fermer la session filet

Si les quatre vérifications passent, tu as fini. Tu peux fermer la session SSH gardée depuis la section 3. Le rituel est terminé. À partir de maintenant, ton serveur n’accepte plus que toi, par clé, et il bannit automatiquement tout ce qui essaye autre chose.

7. Ce qu’on a fait, ce qu’on a laissé de côté, et ce qui vient ensuite

Avant cet article, la porte SSH de ton serveur s’ouvrait avec root et un mot de passe — la configuration par défaut, celle de l’écrasante majorité des installations. Cette porte est maintenant barricadée à plusieurs niveaux :

  • Un utilisateur système dédié (HMT) avec sudo, distinct du compte d’interface web — ta clé perso côté Debian.
  • Une paire de clés SSH générée sur ton poste, dont la clé publique seule a été déposée sur le serveur.
  • Un fichier drop-in de durcissement qui désactive le login root, le mot de passe et l’interactive clavier, tout en confirmant l’usage des clés.
  • Un fail2ban actif qui bannit automatiquement toute IP qui multiplie les échecs.

Et rappelle-toi la vraie raison de tout ça. Sur ton homelab, ce durcissement est de l’hygiène, pas une urgence — ton pare-feu te protège déjà. Mais le geste que tu viens d’apprendre se rejoue à l’identique sur ton VPS, et là, il est en première ligne. Tu l’as appris sur une machine où l’erreur ne coûte rien ; tu le rejoueras les yeux fermés là où ça compte.

Trois zones grises qu’on a laissées de côté, et pourquoi

Le changement de port SSH. Beaucoup de tutoriels te disent de déplacer SSH du port 22 vers un port exotique. L’argument : les robots scannent le port 22, donc en bougeant tu deviens invisible. C’est partiellement vrai (tu réduis le bruit dans tes logs) et partiellement faux (un attaquant sérieux scanne tous les ports en quelques secondes avec nmap et trouve immédiatement où tu as déplacé SSH). C’est de la sécurité par obscurité : ça filtre les amateurs, ça n’arrête personne de motivé. Mon avis : si tu veux du calme dans tes logs sur un VPS exposé, déplace SSH. Mais ne le considère pas comme une mesure de sécurité — c’est un nettoyage de bruit, pas une serrure.

Le mode prohibit-password au lieu de no. On a mis PermitRootLogin no. Sur un nœud unique, c’est la bonne valeur. Mais si tu passes un jour en cluster Proxmox, certaines opérations cross-node ont besoin que root puisse se connecter en SSH par clé. La directive officielle dans ce cas, c’est PermitRootLogin prohibit-password. Le jour où tu en arrives là, change simplement la valeur dans ton 99-hardening.conf.

L’accès root résiduel par le shell web Proxmox. Tu as bouché la porte SSH côté root, mais il en reste une, et je préfère que tu la connaisses : le shell intégré de l’interface Proxmox reste un accès root au système. Ce n’est pas un trou — c’est une fonctionnalité, et c’est justement le filet qui te sauvera si SSH se casse un jour. Mais ça veut dire que la sécurité de cet accès dépend entièrement de tout ce que tu as fait en partie 1 : compte dédié, 2FA, pare-feu. La partie 1 et cet article forment un tout ; l’un sans l’autre laisse des trous.

Ce qui vient ensuite

Tu as maintenant un Proxmox sérieusement verrouillé : interface web protégée, SSH durci, bannisseur automatique en place. Mais sécuriser n’est qu’une partie du travail. Un serveur, ça s’observe aussi : qui se connecte, quand, et combien d’IP fail2ban a-t-il bannies cette semaine ? Le prochain article de la série s’intéressera à cette dimension — la lecture et l’analyse des logs SSH et fail2ban — avec, en bout de chaîne, l’idée d’y brancher un peu d’IA locale pour les digérer automatiquement. Exactement le genre de tâche où l’IA souveraine prend tout son sens, parce qu’on ne confie pas ses logs réseau privés à un service cloud.

D’ici là, applique ce que tu viens d’apprendre sur ton VPS si ce n’est pas déjà fait. Le risque y est cent fois plus élevé que sur ton homelab, et la procédure est strictement identique — à un détail près que tu connais désormais : sur le VPS, le shell web Proxmox n’existe pas comme filet. Le « garde une session ouverte pendant que tu en testes une nouvelle » n’y est plus optionnel, il est vital.

FAQ

Faut-il vraiment sécuriser le SSH d’un homelab privé ?

Pas de façon urgente, si ton serveur est derrière un pare-feu et n’est pas exposé à Internet. Sur un homelab, durcir le SSH relève de l’hygiène : un bon réflexe, pas une parade à un danger immédiat. En revanche, c’est exactement le même geste qui devient indispensable sur une machine exposée comme un VPS. L’apprendre sur ton homelab, où une erreur se rattrape en deux clics, te prépare à le faire sans stress là où ça compte vraiment.

Une clé SSH, c’est vraiment plus sûr qu’un mot de passe ?

Oui, et de loin. Un mot de passe peut être deviné, volé dans une fuite de données, ou cassé par force brute. Une clé ed25519 est un secret de 256 bits qu’aucune machine ne trouvera par essai-erreur. Surtout, la clé privée n’est jamais transmise au serveur lors de la connexion : seule la preuve que tu la possèdes circule. Un attaquant qui intercepte la session n’apprend rien d’utilisable.

Que se passe-t-il si je perds ma clé privée SSH ?

Si tu as désactivé le mot de passe, tu ne peux plus te connecter en SSH avec cette clé — mais tu n’es pas bloqué pour autant. Sur Proxmox, le shell web de l’interface reste un accès root au système : tu y déposes une nouvelle clé publique, et tu repars. Sur un VPS, tu passes par la console de secours de ton hébergeur. Dans les deux cas, génère une nouvelle paire et remplace l’ancienne clé dans authorized_keys. C’est aussi pourquoi une passphrase sur la clé privée est utile : si on te vole ton poste, la clé seule ne sert à rien.

Pourquoi j’ai l’erreur « Permission denied (publickey) » ?

C’est SSH qui te dit qu’il n’accepte que les clés et qu’aucune clé valide ne lui a été présentée. Trois causes classiques : ta clé publique n’est pas dans le authorized_keys du compte sur le serveur, les permissions de ~/.ssh ou authorized_keys sont trop ouvertes (SSH refuse alors silencieusement la clé), ou tu te connectes avec le mauvais nom d’utilisateur. La commande ssh -v t’affiche précisément à quelle étape ça coince. Le détail du diagnostic est dans la section 3 de cet article.

Pour finir

Ton serveur ne s’ouvre plus qu’avec ta clé, et il claque la porte au nez de tout le reste. Sur ton homelab, c’est un réflexe propre ; sur ton VPS, c’est une armure. Tu viens d’apprendre le geste là où l’erreur ne coûte rien — il ne te reste qu’à le rejouer là où il compte.

Ta tech, tes règles, ta liberté.