Ce qui attaque vraiment un nouveau serveur — et ce qui vous le fait vraiment perdre
Pointez une adresse IPv4 toute neuve vers Internet, et du trafic non sollicité arrive presque immédiatement. Pas parce que quelqu'un vous a repéré : la totalité de l'espace d'adressage est balayée en continu, et un hôte qui répond sur le port 22 rejoint simplement une file d'attente déjà en cours. Dans sa première journée, un nouveau serveur type enregistre entre quelques centaines et quelques dizaines de milliers de tentatives d'authentification SSH, presque toutes dirigées contre root, admin, ubuntu, test et une douzaine d'autres noms prévisibles, avec des mots de passe tirés d'une liste qui a à peine changé en dix ans. Cela ressemble à une attaque. C'est plus proche de la météo.
Cette distinction compte, parce qu'elle détermine quels contrôles méritent vos quinze minutes. À ce volume, deviner des mots de passe n'est pas réduit en désactivant l'authentification par mot de passe — c'est éliminé. Il ne reste aucun risque résiduel à gérer, aucun réglage, aucun débit à surveiller. Les deux choses qui font vraiment perdre un petit serveur à son propriétaire, elles, n'ont rien de glamour : une application exposée sur Internet qui fait tourner une version dont la faille, publiée, date de plusieurs mois, et un identifiant qui a fuité ailleurs et fonctionnait encore ici. Ni l'une ni l'autre n'est exotique, et ni l'une ni l'autre n'a le moindre rapport avec le port sur lequel SSH écoute.
Il existe un troisième mode d'échec, et sur un hébergeur qui ne vous a jamais demandé qui vous êtes, c'est de loin le plus courant : vous vous verrouillez vous-même dehors. Chez un hébergeur classique, c'est un ticket de support agaçant : vous répondez à quelques questions, vous prouvez que vous êtes bien la personne inscrite sur la facture, et quelqu'un vous rétablit l'accès. Ici, ce chemin n'existe pas — non pas par choix de politique interne appliqué à contrecœur, mais parce que l'identité qu'il faudrait vérifier n'a jamais été collectée. Ce qui existe à la place, c'est un accès rattaché au compte plutôt qu'à une personne : une console d'urgence KVM-over-VNC dans le panneau, des instantanés horaires avec sept jours de rétention, et la possibilité de démarrer autre chose entièrement. C'est suffisant. Mais seulement si vous les avez déjà utilisés une fois avant d'en avoir besoin.
La checklist ci-dessous est donc ordonnée par conséquence plutôt que par mode. Les contrôles qui éliminent toute une classe d'attaque viennent en premier, ceux qui réduisent le bruit viennent en dernier, et l'étape qui ne coûte rien et sauve la soirée — prouver que vous pouvez encore entrer — passe avant toutes les autres.
| Ce qui inquiète | Comment ça arrive vraiment | Ce qui l'arrête | Ce qui ne l'arrête pas |
|---|---|---|---|
| Devinette de mot de passe SSH | Balayages automatisés, des milliers par jour, listes de noms d'utilisateur génériques | PasswordAuthentication no — complètement | Un port non standard ; un mot de passe plus long |
| Service à faille connue | Un scanner reconnaît votre bannière de version des semaines après la sortie du correctif | Mises à jour de sécurité automatiques plus une politique de redémarrage | Un pare-feu, si le port doit de toute façon rester ouvert |
| Identifiant réutilisé ou ayant fuité | Connexion réussie du premier coup, depuis une adresse jamais vue | Authentification par clé uniquement ; identifiants uniques par service | La limitation de débit — une seule tentative n'est pas une rafale |
| Service interne exposé | Base de données, cache ou point de métriques lié à l'adresse générique | Liaison en loopback ; refus par défaut du trafic entrant sur les deux familles | Supposer que c'est privé parce que vous n'y avez jamais fait de lien |
| Perdre son propre accès | Vous durcissez, vous vous déconnectez, et le changement effectué était erroné | Une seconde session ouverte, un instantané, une console testée | La confiance |
Construisez la sortie avant de fermer la porte
Voici la séquence qui laisse les gens en rade, et chacune de ses étapes est correcte prise isolément : modifier la configuration SSH, déplacer le port, désactiver la connexion par mot de passe, activer un pare-feu, recharger le démon, fermer le terminal. L'erreur ne se trouve dans aucun changement pris individuellement. Elle tient au fait que la session dans laquelle vous étiez en train de taper était la seule preuve que l'un de ces changements avait fonctionné, et que vous vous en êtes débarrassé avant de vérifier.
La règle qui rend toute la checklist sûre tient en une phrase : gardez la session qui fonctionne ouverte, et prouvez le changement depuis une seconde. Ouvrez un nouveau terminal, connectez-vous à nouveau depuis zéro, lancez id, élevez vos privilèges avec sudo. Si cela réussit, le changement est réel et la première session devient jetable. Si cela échoue, vous avez encore un shell avec lequel l'annuler. Cela s'applique au pare-feu, à sshd, à la clé d'un nouvel utilisateur, et à tout ce qui peut vous refuser l'entrée.
Avant tout cela, établissez la voie hors bande. Dans le panneau BitVPS, chaque ligne de serveur porte un bouton Console qui ouvre une session KVM-over-VNC sur l'affichage de la machine virtuelle. Ce n'est pas un shell par le réseau — c'est l'écran et le clavier de la machine — donc cela continue de fonctionner quand sshd refuse de démarrer, quand le pare-feu rejette chaque paquet entrant, et quand la configuration réseau est mauvaise. Ouvrez-la une fois, là, tout de suite, sur un serveur que vous n'avez pas cassé. Confirmez que vous obtenez une invite de connexion. Cela prend trente secondes, et c'est toute la différence entre une erreur et une panne.
Prenez aussi un instantané. Ils sont horaires avec sept jours de rétention sur chaque offre et se restaurent en environ trente secondes, ce qui réduit « j'ai collé la mauvaise règle nft » d'une soirée de récupération à un simple retour en arrière. Et pour les changements dont vous n'êtes vraiment pas sûr, un dead-man switch ne coûte qu'une ligne : systemd-run --on-active=10min /usr/sbin/ufw --force disable arme une minuterie qui annule le pare-feu dans dix minutes à moins que vous ne l'annuliez. Si les nouvelles règles fonctionnent, vous annulez. Si elles vous enferment dehors, la machine vous laisse revenir sans que vous ayez besoin d'être là.
SSH : les quatre directives qui font le travail — et le fichier qui les court-circuite
Générez la clé sur votre propre machine, jamais sur le serveur : ssh-keygen -t ed25519 -C "laptop-2026". Ed25519 est le bon choix par défaut en 2026 — clés courtes, vérification rapide, aucun choix de paramètre à rater, et pris en charge par tous les OpenSSH compilés depuis dix ans. RSA en 4096 bits n'est pas non sécurisé, il est simplement plus gros et plus lent sans aucun gain. Définissez une phrase de passe sur la clé privée ; ssh-agent permet de ne la taper qu'une fois par session, et c'est la seule chose qui se dresse entre un ordinateur portable volé et tous les serveurs que vous possédez.
Copiez la moitié publique sur le serveur avec ssh-copy-id et confirmez que vous pouvez vous connecter avec avant de désactiver quoi que ce soit. Les permissions comptent, et OpenSSH est strict à leur sujet par conception : ~/.ssh en 700, authorized_keys en 600, les deux appartenant à l'utilisateur. Une clé qui échoue silencieusement est presque toujours un problème de permissions, et sshd -e -d lancé au premier plan vous le dira en une seule ligne.
Ensuite, quatre directives dans /etc/ssh/sshd_config font l'essentiel du travail. Tout le reste de ce fichier n'est que préférence.
| Directive | Valeur à définir | Ce qu'elle fait | Ce qui casse si vous vous trompez |
|---|---|---|---|
PasswordAuthentication | no | Supprime entièrement le mot de passe comme méthode de connexion — tout le déluge de force brute devient impossible plutôt que simplement lent | Vous êtes enfermé dehors si votre clé n'a en réalité jamais été installée. Testez d'abord, dans une seconde session |
KbdInteractiveAuthentication | no | Ferme la seconde porte de la même pièce — le chemin keyboard-interactive de PAM peut encore accepter un mot de passe quand la première directive est activée | Casse les configurations légitimes à deux facteurs qui reposent sur PAM. Ne la laissez active que si vous en utilisez une |
PermitRootLogin | prohibit-password | Root peut encore se connecter avec une clé (utile pour l'automatisation), mais jamais avec un mot de passe | no est plus strict, et très bien — à condition que votre utilisateur sudo fonctionne. Vérifiez-le d'abord |
PubkeyAuthentication | yes | La méthode que vous conservez. Ici, l'explicite vaut mieux que le défaut | Rien, mais cela vaut la peine de l'affirmer pour qu'une future modification ne puisse pas la faire disparaître silencieusement |
AllowUsers / AllowGroups | votre utilisateur ou groupe | Facultatif, et la ligne facultative la plus précieuse — le démon refuse tout autre compte avant même l'authentification | Une faute de frappe refuse tout le monde. Définissez-la en dernier, et depuis une seconde session |
Voici la partie qui piège les gens expérimentés. Sur Debian 12, Ubuntu 22.04 et tout ce qui est plus récent, /etc/ssh/sshd_config commence par Include /etc/ssh/sshd_config.d/*.conf, et OpenSSH retient la première valeur qu'il obtient pour chaque mot-clé. Les images cloud livrent des fichiers drop-in dans ce répertoire — souvent l'un d'eux positionne PasswordAuthentication yes — et comme l'inclusion se trouve tout en haut, ce drop-in l'emporte sur la ligne que vous avez soigneusement modifiée deux cents lignes plus bas. Le fichier dit une chose, et le démon en fait une autre.
La solution consiste à ne jamais faire confiance au fichier. Demandez au démon ce qu'il a réellement retenu : sshd -T | grep -Ei 'passwordauth|permitrootlogin|kbdinteractive|pubkeyauth' affiche la configuration effective une fois tous les includes, blocs Match et valeurs par défaut appliqués. Cette seule commande vaut plus que le reste de cette section. Associez-la à sshd -t, qui valide la syntaxe et sort en erreur en cas de faute, puis seulement ensuite systemctl reload ssh — un reload ne perturbe pas les sessions existantes, donc même un mauvais reload laisse votre shell actuel bien vivant.
Quant à déplacer SSH hors du port 22 : cela élimine peut-être 95 % de votre volume de logs et zéro pour cent de votre risque. Tout scanner qui mérite qu'on s'en inquiète balaie les 65,535 ports, et ceux qui ne le font pas n'allaient de toute façon jamais passer une authentification par clé uniquement. Déplacez-le si le bruit vous dérange ou s'il permet de faire taire une alerte de supervision — mais ne le comptez pas comme un contrôle de sécurité, et si vous le déplacez, restez sous 1024 : seuls les processus privilégiés peuvent se lier à ces ports-là, donc rien de non privilégié ne peut squatter le port si le démon s'arrête un jour.
Le pare-feu : refus par défaut du trafic entrant, et la moitié que tout le monde oublie
Sur un serveur à vocation unique, le pare-feu a un seul travail : faire coïncider l'ensemble des ports joignables avec l'ensemble des ports dont vous pouvez justifier l'existence. Refusez le trafic entrant par défaut, autorisez le sortant, autorisez le retour des connexions établies et associées, puis ouvrez les deux ou trois services que vous faites réellement tourner. Sur Debian ou Ubuntu, ufw se règle en quatre commandes et fait le travail correctement ; si vous préférez posséder un seul fichier de règles lisible, nftables est la réponse moderne intégrée au noyau, celle-là même vers laquelle ufw écrit en coulisse.
L'ordre ne compte qu'à un seul endroit : autorisez votre port SSH avant d'activer la politique. ufw allow 22/tcp, puis ufw default deny incoming, puis ufw enable. Dans l'ordre inverse, l'étape d'activation coupe la session dans laquelle vous êtes en train de taper — ce qui reste rattrapable si vous avez suivi la section précédente et disposez encore d'une console, et qui est sinon la façon classique de terminer sa soirée en avance.
La moitié qu'on oublie, c'est IPv6. Chaque serveur ici est livré avec un /64 routé, et les services se lient aux deux familles par défaut. Un jeu de règles écrit uniquement avec iptables ne gouverne que l'IPv4 et ne dit strictement rien de l'IPv6, si bien que la base de données que vous avez soigneusement protégée par pare-feu reste joignable depuis tout Internet via l'autre protocole. ufw gère les deux dès lors que IPV6=yes est défini dans /etc/default/ufw (c'est le défaut sur les versions actuelles — vérifiez, ne présumez pas), et nftables évite structurellement le problème : un jeu de règles table inet couvre les deux familles avec un seul ensemble de règles. Si vous ne devez écrire qu'une seule ligne de configuration de pare-feu à la main, que ce soit celle-là.
Vient ensuite Docker, qui ne contourne pas tant votre pare-feu qu'il n'arrive par en dessous. Publier un port avec -p 5432:5432 insère des règles DNAT dans la table nat, qui sont évaluées avant les chaînes de filtrage que gère ufw. Le résultat est un conteneur joignable depuis Internet alors que ufw status affiche le port comme fermé, et chaque année cela surprend des gens qui font tourner une base de données dans un conteneur derrière ce qu'ils croyaient être une politique de refus total. Publiez plutôt en loopback — -p 127.0.0.1:5432:5432 — ou placez le conteneur sur un réseau interne et joignez-le depuis un autre conteneur par son nom.
| Ce que vous croyez | Ce qui est réellement vrai | Comment vérifier en une commande |
|---|---|---|
| « Le pare-feu refuse tout ce que je n'ai pas autorisé » | Vrai pour IPv4 seulement, si vous n'avez écrit que des règles IPv4 | ip6tables -L -n ou nft list ruleset |
| « La base de données n'est pas exposée, elle est dans un conteneur » | Un port publié est joignable quelle que soit la politique de filtrage | docker ps --format '{{.Ports}}' |
| « Rien n'écoute sur ce port » | Quelque chose a redémarré et s'y est relié depuis votre dernière vérification | ss -tulpn |
| « Le port est fermé, mon scan le dit » | Un scan lancé depuis la machine elle-même teste le loopback, pas Internet | Scannez depuis une seconde machine, ou depuis votre ordinateur portable |
Ce qui écoute réellement — la commande qui met fin aux suppositions
Une seule commande répond à la question la plus utile qui soit sur l'exposition d'un serveur : ss -tulpn. Elle liste chaque socket TCP et UDP en écoute avec le processus qui la détient, et la colonne qui compte est l'adresse locale. 127.0.0.1:5432 signifie que seule cette machine peut se connecter. 0.0.0.0:5432 signifie que n'importe quelle adresse qui atteint la machine le peut, sous la seule réserve du pare-feu. [::]:5432 signifie la même chose en IPv6, et c'est la ligne que tout le monde survole sans la lire.
Les coupables habituels sont toujours la même poignée. Redis, qui pendant la majeure partie de son histoire s'est lié largement et livrait sans authentification. PostgreSQL et MySQL, quand un fichier de configuration a été modifié pour accepter les connexions « depuis l'application » et que l'application a déménagé depuis. MongoDB et Elasticsearch, qui à eux deux ont produit une décennie de gros titres sur des fuites de données, exactement pour cette raison. Memcached, dont l'écouteur UDP est devenu un amplificateur de bande passante prisé. Prometheus node_exporter, qui racontera avec plaisir les entrailles de votre hôte à quiconque le lui demande. L'API Docker sur le port 2375, qui est une exécution de code à distance déguisée en interface REST. Et un serveur de développement que quelqu'un a lancé « juste une minute » dans une session tmux il y a onze mois.
La règle est simple et ne demande aucun jugement : si un service n'a pas besoin d'être joignable depuis Internet, liez-le en loopback. Tout ce que vous avez personnellement besoin d'en faire continue de fonctionner — ssh -L 5432:127.0.0.1:5432 user@server vous donne un port local qui tunnelise à travers la connexion à laquelle vous faites déjà confiance, et si plusieurs machines ont besoin d'y accéder, une interface WireGuard privée leur donne une plage d'adresses qui ne touche jamais l'Internet public. Aucune des deux options ne coûte quoi que ce soit, et toutes deux retirent entièrement le service de la surface d'attaque plutôt que de le défendre.
Vérifiez depuis l'extérieur, pas depuis l'intérieur. Un port que votre propre machine signale comme filtré n'est qu'une affirmation sur votre pile loopback ; un port qu'une seconde machine ne peut pas atteindre est une preuve. Connectez-vous depuis votre ordinateur portable, ou depuis un autre serveur, et vérifiez les ports que vous attendez ouverts ainsi qu'un port que vous attendez fermé — un test qui ne peut que réussir n'est pas un test.
| Service | Défaut typique | Ce qu'une instance exposée livre | Le correctif |
|---|---|---|---|
| Redis | Liaison large, historiquement sans mot de passe | Lecture/écriture complète, et un chemin bien connu vers l'écriture de fichiers en tant qu'utilisateur redis | Lier en loopback ; définir requirepass ; garder protected-mode activé |
| PostgreSQL / MySQL | Loopback, jusqu'à ce que quelqu'un le modifie | L'intégralité de vos données, si un identifiant est faible ou réutilisé | Lier en loopback ; y accéder via SSH ou WireGuard |
| API Docker | Désactivée — sauf si un tutoriel l'a activée | Exécution de code équivalente à root sur l'hôte | Ne jamais l'exposer ; utiliser un contexte SSH à la place |
| node_exporter / métriques | Toutes les interfaces, port 9100 | Noms d'hôte, points de montage, versions, processus en cours | Lier en loopback ; scraper via l'interface privée |
| Soumission de mail, panneaux d'administration web | Toutes les interfaces par nécessité | Une surface de devinette de mot de passe que les clés ne peuvent pas protéger | C'est ici que la limitation de débit trouve vraiment sa place |
Fail2ban et CrowdSec : ce qu'ils valent une fois les mots de passe éliminés
C'est l'élément que la plupart des checklists placent tout en haut et que la plupart des opérateurs surestiment, il vaut donc la peine d'être précis. Avec PasswordAuthentication no, une tentative de mot de passe ne peut pas réussir — pas rarement, pas difficilement, mais pas du tout. Bannir une adresse après cinq échecs ne rend pas une authentification impossible plus impossible encore. Ce que fail2ban vous apporte au niveau SSH, c'est un auth.log plus silencieux, un peu moins de CPU dépensé à établir des connexions, et des graphiques de supervision plus faciles à lire. Ce sont de vrais bénéfices. C'est de l'hygiène opérationnelle, pas un contrôle de sécurité, et les classer sous la mauvaise rubrique est exactement comment on se retrouve avec un fichier de log durci et une application jamais corrigée.
Là où cette famille d'outils gagne réellement sa place, c'est à chaque niveau où un mot de passe existe encore : un formulaire de connexion d'application web, un port de soumission de mail, une administration Nextcloud ou WordPress, un serveur IMAP. Ceux-là ne peuvent pas basculer vers une authentification par clé publique, donc deviner reste possible et la limitation de débit est la vraie défense. CrowdSec ajoute par-dessus un flux de réputation partagé, ce qui est réellement utile au niveau HTTP — vous bénéficiez d'adresses que d'autres ont déjà vues mal se comporter — et presque hors de propos au niveau SSH une fois les clés rendues obligatoires.
Deux pièges méritent d'être nommés. Le premier consiste à se bannir soi-même, généralement en testant quelque chose à trois heures du matin ; ajoutez vos propres plages à ignoreip, et gardez à l'esprit qu'une adresse partagée ou en carrier-grade NAT signifie que bannir « un attaquant » peut aussi bannir quelques centaines de personnes qui n'y sont pour rien. Le second est que l'outil doit être capable de lire les logs qu'il filtre : sur les distributions qui ne journalisent que dans le journal systemd, le backend fichier par défaut surveille silencieusement un fichier qui ne reçoit plus rien, et la prison reste là, l'air en bonne santé, à ne rien faire.
La recommandation honnête : installez-le si le bruit vous dérange, configurez-le pour la couche applicative où il compte vraiment, et ne le comptez pas deux fois. Si vous voulez le même calme sans rien à maintenir, MaxStartups et LoginGraceTime dans le démon SSH, combinés à l'authentification par clé uniquement, couvrent l'essentiel du chemin et ne peuvent pas vous enfermer dehors de votre propre machine.
Les correctifs : le contrôle ennuyeux qui évite la plupart des compromissions réelles
Lisez n'importe quel rapport d'incident impliquant un petit serveur exposé sur Internet, et le même schéma se répète : la vulnérabilité avait un correctif, ce correctif était disponible depuis des semaines ou des mois, et personne ne l'avait appliqué. Le catalogue Known Exploited Vulnerabilities de la CISA est la lecture utile ici, précisément parce que ce n'est pas une liste de gravité théorique — c'est une liste de failles activement exploitées en ce moment même, et elle est dominée par des problèmes disposant déjà d'un correctif publié. Les mises à jour de sécurité automatiques sont par conséquent les quinze secondes les plus rentables de toute cette page.
Sur Debian et Ubuntu, installez unattended-upgrades, activez-le avec dpkg-reconfigure -plow unattended-upgrades, et confirmez que l'origine security est bien celle sélectionnée dans /etc/apt/apt.conf.d/50unattended-upgrades. Vérifiez qu'il fait bien quelque chose avec unattended-upgrades --dry-run --debug, qui affiche exactement quels paquets il prendrait et pourquoi — la page du wiki Debian fait référence. Sur Rocky et Alma, dnf-automatic avec apply_updates = yes et le minuteur activé fait le même travail. Se restreindre au dépôt de sécurité est délibéré : c'est la configuration qui ne casse presque jamais rien, le seul type d'automatisation que les gens laissent activé.
Décidez ensuite explicitement de la question des redémarrages, car c'est là que le contrôle fuit généralement. Installer un nouveau paquet de noyau ne remplace pas le noyau en cours d'exécution, et remplacer une bibliothèque partagée ne redémarre pas les démons qui avaient déjà mappé l'ancienne copie en mémoire. needrestart, sur Debian et Ubuntu, liste précisément quels services retiennent encore des bibliothèques supprimées ; une mise à jour du noyau exige un redémarrage, point final, et le live-patching est une fonctionnalité payante qui ne viendra pas sur une instance à $8.50. Réglez soit Unattended-Upgrade::Automatic-Reboot avec un Automatic-Reboot-Time discret, soit un redémarrage mensuel dans votre calendrier, et traitez-le comme de la maintenance plutôt que comme un incident.
Enfin, sachez ce que les mises à jour automatiques ne couvrent pas, car c'est là que les gens se font prendre. Tout ce qui est installé en dehors du gestionnaire de paquets lui est invisible : un binaire récupéré depuis une release GitHub, une dépendance de runtime applicative via pip, npm ou cargo, un thème ou une extension à l'intérieur d'une application web, et surtout les images de conteneurs. Un hôte qui annonce zéro mise à jour en attente peut très bien faire tourner une image de base construite il y a deux ans, et rien sur la machine ne vous le dira. Si vous faites tourner des conteneurs, les reconstruire et les retélécharger selon un calendrier régulier fait partie des correctifs, ce n'est pas une activité à part.
| Système | Mécanisme | Ce qu'il couvre | Ce qu'il rate silencieusement |
|---|---|---|---|
| Debian / Ubuntu | unattended-upgrades, origine security uniquement | Paquets de la distribution, appliqués chaque nuit | Le noyau en cours d'exécution et les bibliothèques mappées, jusqu'au redémarrage |
| Rocky / Alma | dnf-automatic avec apply_updates = yes | Pareil, via un minuteur systemd | Le même écart de redémarrage ; vérifiez avec dnf needs-restarting |
| Conteneurs | Rien, par défaut | Rien | Tout — l'image est figée au moment de sa construction |
| Dépendances applicatives | Rien, par défaut | Rien | La surface d'attaque réelle de votre application |
Utilisateurs, sudo, et les portes qui se rouvrent en silence
Créez un utilisateur ordinaire, ajoutez-le à sudo ou wheel, installez-y votre clé, et cessez de vous connecter en root. Ce n'est pas que root-avec-une-clé soit dangereux en soi — c'est un compte à facteur unique, au nom universellement connu, sans aucune trace de quel humain l'a utilisé. Un compte nommé plus sudo produit une piste d'audit, permet plus d'un opérateur sans partager un identifiant, et rend enfin répondable la question « qui a fait ça ». Ne lui donnez cependant pas NOPASSWD pour tout, pour ensuite réutiliser ce compte pour un service web.
Les services non plus ne devraient pas tourner en root, et systemd rend la version bon marché de cette précaution presque gratuite. Un drop-in créé avec systemctl edit <unit> contenant NoNewPrivileges=yes, ProtectSystem=strict, ProtectHome=yes et PrivateTmp=yes confine un démon compromis à une vue du système de fichiers en lecture seule, sans aucune voie d'escalade — quatre lignes, décrites dans la documentation de systemd.exec, qui transforment de nombreuses failles applicatives d'un « root sur l'hôte » en une simple « erreur dans un log ». systemd-analyze security note chaque unité de la machine et vous indique lesquelles méritent les cinq minutes.
Gardez authorized_keys honnête. Une clé par appareil, chacune avec un commentaire nommant l'appareil, et supprimez tout ce que vous ne pouvez pas identifier. Relisez le fichier après le passage de tout outil de provisionnement, car cloud-init, les pipelines d'imagerie et les panneaux de contrôle y ajoutent tous leurs propres entrées, et une clé que vous n'avez pas ajoutée est une clé que vous ne pouvez pas révoquer. La même chose vaut pour les comptes : un utilisateur par défaut laissé par une image, avec un mot de passe défini par un script et jamais utilisé depuis, est une porte d'accès que vous ne surveillez pas.
L'élément le plus subtil de cette liste est le transfert d'agent. ForwardAgent yes permet à un processus sur l'hôte distant d'utiliser votre clé privée locale tant que vous êtes connecté — c'est exactement ce pour quoi c'est conçu, et c'est pourquoi le root d'une machine que vous ne contrôlez pas entièrement peut emprunter votre clé pour atteindre tous les autres serveurs qui lui font confiance. Utilisez plutôt ProxyJump (ssh -J bastion target) : cela vous donne la même portée en tunnelisant la connexion, et votre clé ne devient jamais utilisable sur l'hôte intermédiaire. Si vous devez absolument transférer un agent, faites-le par hôte dans ~/.ssh/config, jamais globalement.
Les sauvegardes : le contrôle qui transforme une mauvaise journée en un après-midi
Tout ce qui précède réduit la probabilité d'une mauvaise journée. Les sauvegardes décident de ce que cette mauvaise journée vous coûte, et chez un hébergeur qui ne peut délibérément pas vous identifier, elles comptent plus que d'habitude : il n'existe aucun chemin de support qui reconstruit votre machine à partir de la copie de quelqu'un d'autre, aucun flux de récupération de compte qui vous glisse discrètement une restauration, et aucune fiche de facturation avec un numéro de téléphone attaché. Ce que vous avez, c'est ce que vous avez pris vous-même.
La forme qui fonctionne n'a rien d'excitant. restic ou borg, chiffrant sur le serveur pour que les données soient déjà illisibles avant de traverser le réseau, poussées vers une seconde machine — et idéalement une seconde juridiction, ce qui relève de la simple case à cocher plutôt que du projet quand le même panneau propose l'Islande, les Pays-Bas, la Roumanie et la Suisse. Les instantanés horaires inclus avec chaque offre sont une véritable bonne première ligne de défense et couvrent parfaitement le cas « j'ai cassé la configuration il y a une heure ». Ils ne couvrent pas un fichier que vous avez supprimé il y a cinq semaines, et ils vivent sur la même infrastructure que ce qu'ils protègent. Les instantanés et les sauvegardes résolvent des problèmes différents ; faites tourner les deux.
Rendez le dépôt append-only. C'est le détail qui sépare une sauvegarde d'une simple copie : un serveur compromis détient les identifiants de sa propre cible de sauvegarde, et tout ce qui peut supprimer se fera dire de supprimer. borg serve --append-only et le rest-server --append-only de restic imposent tous deux cette règle côté réception, si bien que la machine peut écrire de nouveaux instantanés mais ne peut pas retirer les anciens. Purgez depuis ailleurs, selon un calendrier, avec un identifiant que le serveur n'a jamais vu.
Deux dernières choses, que tout le monde saute. La phrase de passe du dépôt ne doit pas vivre uniquement sur la machine qu'elle protège — notez-la, placez-la dans un gestionnaire de mots de passe ailleurs, traitez-la comme la seed phrase qu'elle est en réalité. Et restaurez-la, une fois. Une sauvegarde que vous n'avez jamais restaurée est une hypothèse ; la façon habituelle de découvrir qu'une règle d'exclusion a discrètement sauté le dump de la base de données, c'est pendant la récupération que vous ne pouvez pas vous permettre de rater. Déployez une instance Starter, restaurez-y vos données, vérifiez-les, puis détruisez-la. Cela coûte $8.50 et une heure, et c'est la seule façon pour que le reste de cette page soit une répétition plutôt qu'un coup d'essai.
Ce contre quoi le durcissement ne vous protège pas
Il ne protège pas le disque de quelqu'un qui tient le disque. Chaque contrôle de cette page suppose que le système d'exploitation tourne et fait appliquer ses propres règles ; aucun ne s'applique à une machine éteinte dont le stockage a été imagé. C'est un problème différent, avec une réponse différente — voir le chiffrement intégral du disque, le déverrouillage à distance, et ce qu'une saisie récupère vraiment — et le résumé honnête est que durcissement et chiffrement défendent contre des menaces disjointes. Faire l'un ne fait pas partiellement l'autre.
Il ne corrige pas l'application que vous faites tourner. Un serveur peut réussir chaque vérification de cette page et se faire quand même perdre à cause d'un forum obsolète, d'un point d'accès d'administration sans authentification, d'un moteur de templates évaluant une entrée utilisateur, ou d'un jeton commité dans un dépôt public. Le durcissement de l'hôte réduit le rayon de destruction d'une compromission applicative — c'est exactement à cela que sert le confinement systemd ci-dessus — mais il n'audite pas votre code, et la façon la plus courante dont un petit serveur se fait prendre reste quelque chose que l'opérateur a lui-même installé.
Il ne change rien à qui sait que vous avez loué la machine. Une machine parfaitement durcie payée depuis un compte d'échange à l'identité vérifiée, commandée avec un e-mail personnel, reste une machine parfaitement durcie qui porte votre nom. La couche sécurité et la couche identité sont indépendantes, et la seconde se décide au moment du paiement, pas dans le shell — à quel point l'hébergement payé en crypto est réellement traçable détaille où les liens se forment vraiment.
Et il ne fait rien contre l'analyse de trafic. Un observateur positionné pour regarder vos connexions en apprend la forme — le timing, le volume, qui parle à qui — quelle que soit la qualité de la configuration des extrémités. Le chiffrement cache le contenu, pas la conversation, et aucun réglage de sshd, si poussé soit-il, n'y change quoi que ce soit.
La checklist, dans l'ordre où l'exécuter
Huit étapes. Les quatre premières sont les quinze minutes du titre et éliminent des catégories entières de risque ; les quatre dernières prennent plus longtemps et font la différence entre un serveur que vous pouvez défendre et un serveur que vous avez simplement configuré. Chaque ligne indique comment vérifier que ça a marché, parce qu'une étape de durcissement que vous n'avez pas vérifiée est une étape de durcissement que vous n'avez pas faite.
| # | Étape | Comment savoir que ça a marché |
|---|---|---|
| 1 | Ouvrir la console du panneau, confirmer une invite de connexion, prendre un instantané | Vous avez vu l'invite sur un serveur qui n'était pas cassé |
| 2 | Créer un utilisateur non-root, installer votre clé ed25519, tester sudo | Un second terminal se connecte en tant que cet utilisateur et élève ses privilèges |
| 3 | Désactiver l'authentification par mot de passe et keyboard-interactive | sshd -T rapporte passwordauthentication no |
| 4 | Pare-feu à refus par défaut du trafic entrant, couvrant IPv4 et IPv6 | nft list ruleset montre les deux familles ; un scan depuis un autre hôte le confirme |
| 5 | Auditer les sockets en écoute, relier les services internes en loopback | ss -tulpn ne montre aucune liaison générique que vous ne puissiez justifier |
| 6 | Activer les mises à jour de sécurité automatiques et une politique de redémarrage | unattended-upgrades --dry-run --debug sélectionne l'origine security |
| 7 | Confiner les services avec systemd, nettoyer authorized_keys | Les scores systemd-analyze security s'améliorent ; chaque clé porte un nom |
| 8 | Sauvegarde chiffrée append-only hors site, restaurée une fois | Vous avez restauré dans une instance jetable et les données étaient là |
Rien de tout cela n'est exotique, et c'est bien le but. Les contrôles qui comptent sur un petit serveur sont ceux qui éliminent une classe de problème plutôt que de la gérer : pas de mots de passe, pas d'écouteurs inattendus, pas de paquets non corrigés, pas de copie unique des données. Tout le reste est affaire de préférence, et la préférence ne coûte rien une fois que les quatre changements porteurs sont en place et vérifiés.
Si vous provisionnez la machine maintenant plutôt que de lire en avance, la séquence tient confortablement dans la première session : déployez une instance, ouvrez la console une fois pour prouver qu'elle fonctionne, et exécutez les huit étapes dans l'ordre avant d'installer quoi que ce soit. Un serveur durci avant d'avoir un service dessus n'a aucun héritage avec lequel composer — c'est le seul avantage qu'a une machine neuve, et il dure environ une journée.