← Blog
DurcissementLinuxVPSSSHDockerHardening

Sécuriser un VPS après installation, le guide d'un ingénieur sécurité

13 octobre 2026 · 9 min de lecture · Niveau : Intermédiaire

Un serveur neuf, exposé sur Internet, est scanné en quelques minutes. Ce ne sont pas des attaques ciblées : des robots parcourent en permanence toutes les adresses IP à la recherche de mots de passe faibles, de services oubliés et de failles connues. Vous pouvez le constater vous-même avec journalctl -u ssh quelques heures après la mise en ligne.

Ce guide est la procédure que j’applique après chaque installation. Il vise Debian et Ubuntu, mais les principes valent pour toute distribution. L’objectif n’est pas la sécurité parfaite, qui n’existe pas, mais de réduire la surface d’attaque, détecter les anomalies et pouvoir se remettre d’un incident.

Le modèle de menace

Avant de configurer quoi que ce soit, il faut savoir de quoi l’on se protège.

Menace Exemple Réponse dans ce guide
Accès non autorisé Brute force SSH, mot de passe réutilisé Clés SSH, pas de mot de passe, fail2ban
Service exposé par erreur Tableau de bord, base de données, port de test Pare-feu, audit des ports, attention à Docker
Faille connue non corrigée Vulnérabilité d’un paquet système Mises à jour automatiques
Élévation de privilèges Compte compromis qui devient root Utilisateur dédié, sudo, durcissement du noyau
Compromission non détectée Intrusion qui dure des semaines Journalisation, auditd, supervision
Perte de données Suppression, rançongiciel, erreur humaine Sauvegardes hors machine, testées

Checklist rapide

  1. Mettre à jour le système.
  2. Créer un utilisateur non root avec sudo.
  3. Passer SSH en authentification par clé uniquement.
  4. Activer un pare-feu en refus par défaut.
  5. Installer fail2ban.
  6. Activer les mises à jour de sécurité automatiques.
  7. Réduire la surface : services inutiles et paramètres noyau.
  8. Maîtriser Docker s’il est présent.
  9. Journaliser et auditer.
  10. Sauvegarder, puis vérifier de l’extérieur.

Avant de commencer : ouvrez toujours une seconde session SSH et gardez-la ouverte pendant que vous modifiez SSH ou le pare-feu. Si vous vous trompez, vous pourrez corriger. Sinon, il ne restera que la console de secours de votre hébergeur.

1. Mettre à jour et créer un utilisateur dédié

apt update && apt full-upgrade -y

adduser deploy
usermod -aG sudo deploy

Travailler en root en permanence est une mauvaise habitude : une seule erreur de frappe ou un seul compte compromis donne tous les droits. Un utilisateur normal qui passe par sudo laisse une trace de chaque action privilégiée dans les journaux.

Depuis votre poste, créez une clé moderne et copiez-la sur le serveur :

ssh-keygen -t ed25519 -a 100 -C "vps-deploy"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@IP_DU_SERVEUR

Protégez la clé privée par une passphrase : si votre poste est volé, la clé seule ne suffit pas. Testez la connexion avec la clé avant de passer à l’étape suivante.

2. Durcir SSH

SSH est la porte d’entrée de l’administration, donc la cible numéro un. Plutôt que de modifier le fichier principal, utilisez un fichier dédié dans sshd_config.d.

cat > /etc/ssh/sshd_config.d/10-hardening.conf <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
LoginGraceTime 20
AllowUsers deploy
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
EOF

sshd -t && systemctl reload ssh

Trois détails qui comptent :

  • Le préfixe 10- : OpenSSH applique la première valeur rencontrée. Les images cloud déposent souvent un fichier 50-cloud-init.conf qui réactive PasswordAuthentication yes. Le vôtre doit passer avant.
  • sshd -t vérifie la syntaxe avant le rechargement. Une faute de frappe ne vous enferme pas dehors.
  • Vérifiez la configuration réellement appliquée, pas celle que vous croyez avoir écrite :
sshd -T | grep -E 'permitrootlogin|passwordauthentication|allowusers|maxauthtries'

Changer le port SSH réduit le bruit dans les journaux, mais ce n’est pas une mesure de sécurité : un scan de ports le retrouve en quelques secondes. La vraie protection est l’authentification par clé.

3. Pare-feu : tout refuser, ouvrir le strict nécessaire

apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'SSH'
ufw allow 80,443/tcp comment 'Web'
ufw enable
ufw status verbose

ufw limit refuse temporairement les adresses qui tentent plus de six connexions en trente secondes. Vérifiez que IPV6=yes figure dans /etc/default/ufw : un pare-feu qui ne filtre que l’IPv4 laisse l’IPv6 grand ouvert.

Le principe est celui du moindre privilège réseau : si un service n’a pas besoin d’être joignable depuis Internet, il ne l’est pas.

4. Bloquer le brute force avec fail2ban

Avec l’authentification par clé, le brute force ne peut plus réussir. fail2ban reste utile : il coupe le bruit, limite la charge et fournit un signal de détection.

apt install -y fail2ban

cat > /etc/fail2ban/jail.d/sshd.local <<'EOF'
[sshd]
enabled   = true
backend   = systemd
maxretry  = 4
findtime  = 10m
bantime   = 1h
bantime.increment = true
EOF

systemctl enable --now fail2ban
fail2ban-client status sshd

bantime.increment allonge la durée des bannissements pour les récidivistes. Pensez à ajouter votre propre adresse dans ignoreip si elle est fixe, pour ne pas vous bannir vous-même.

5. Mises à jour de sécurité automatiques

Une grande part des compromissions exploite des failles déjà corrigées mais non appliquées.

apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
unattended-upgrade --dry-run --debug

Par défaut, seules les mises à jour de sécurité sont installées automatiquement, ce qui est le bon compromis. Décidez en connaissance de cause du redémarrage automatique (Unattended-Upgrade::Automatic-Reboot) : un correctif du noyau n’est actif qu’après redémarrage.

6. Réduire la surface d’attaque

Inventorier ce qui écoute. C’est la commande la plus utile de tout ce guide :

ss -tulpn
systemctl list-unit-files --state=enabled

Chaque port ouvert doit avoir une justification. Pour un service inutile : systemctl disable --now nom_du_service.

Durcir le noyau avec quelques paramètres réseau et système :

cat > /etc/sysctl.d/99-hardening.conf <<'EOF'
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
EOF

sysctl --system

Ne copiez jamais des paramètres noyau sans les comprendre. Par exemple, rp_filter en mode strict peut casser des flux sur un hôte qui utilise un VPN comme WireGuard ou Tailscale. Testez après chaque changement.

7. Docker : le piège du pare-feu contourné

C’est l’erreur la plus fréquente, et elle a de quoi surprendre. Docker modifie directement les règles iptables et publie les ports avant que UFW ne les évalue. Un conteneur lancé avec -p 8080:80 est donc joignable depuis Internet, même si ufw status n’affiche aucune règle pour ce port.

Lors d’un audit de l’un de mes propres serveurs, ce défaut m’a fait retrouver un tableau de bord d’administration et un conteneur de test publiés sur Internet, que j’avais oubliés. Un scan externe les a tout de suite révélés.

Les parades :

  • Lier les ports à l’interface locale quand un service n’a pas à être public. Dans docker-compose.yml :

    ports:
      - "127.0.0.1:8080:80"
  • Ne publier que 80 et 443, via un reverse proxy (Traefik, Nginx, Caddy), et laisser tous les autres conteneurs sur un réseau Docker interne.

  • Filtrer dans la chaîne DOCKER-USER quand une règle doit s’appliquer aux conteneurs.

Et pour les conteneurs eux-mêmes :

services:
  app:
    security_opt:
      - no-new-privileges:true
    cap_drop: [ALL]
    read_only: true
    user: "10001:10001"

Montrer /var/run/docker.sock dans un conteneur revient à lui donner les droits root sur l’hôte. Le suffixe :ro n’y change rien : un socket monté en lecture seule permet toujours d’appeler toute l’API Docker. Si un reverse proxy doit lire les conteneurs, passez par un proxy de socket (docker-socket-proxy) qui n’expose que les routes nécessaires.

8. Journaliser et auditer

Un durcissement sans détection, c’est une porte blindée sans alarme. Trois gestes simples :

Rendre les journaux persistants : vérifiez que /var/log/journal existe et que Storage=persistent est défini dans /etc/systemd/journald.conf.

Auditer les fichiers sensibles avec auditd :

apt install -y auditd

cat > /etc/audit/rules.d/hardening.rules <<'EOF'
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /etc/ssh/sshd_config.d/ -p wa -k sshd_config
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
EOF

augenrules --load
ausearch -k identity

Centraliser les journaux hors du serveur. Un attaquant qui obtient les droits root efface d’abord les traces locales. Un agent comme Wazuh envoie les événements vers une plateforme de supervision (SIEM) qui alerte sur les connexions inhabituelles, les modifications de fichiers critiques et les élévations de privilèges. C’est exactement le rôle des laboratoires que je documente dans mes projets.

9. Sauvegardes : la dernière ligne de défense

Quand tout le reste échoue, la sauvegarde décide de ce que vous perdez. Respectez la règle 3-2-1 : trois copies, sur deux supports, dont une hors site.

restic -r sftp:backup@hote-distant:/srv/restic init
restic -r sftp:backup@hote-distant:/srv/restic backup /etc /opt
restic -r sftp:backup@hote-distant:/srv/restic snapshots

Une sauvegarde jamais restaurée n’est pas une sauvegarde : planifiez un test de restauration. Les instantanés de l’hébergeur sont un complément appréciable, pas un remplacement, puisqu’ils dépendent du même compte.

10. Vérifier de l’extérieur

La seule configuration qui compte est celle que voit un attaquant. Depuis une autre machine :

nmap -sS -sV -p- IP_DU_SERVEUR

Vous devez voir uniquement les ports que vous avez décidé d’ouvrir (22, 80 et 443 dans ce guide). Tout autre port est une erreur à comprendre.

Contrôlez ensuite la solidité de SSH et l’état général du système :

ssh-audit IP_DU_SERVEUR        # algorithmes, bonnes pratiques SSH
lynis audit system             # audit local, avec un score et des suggestions

Lynis ne remplace pas une revue manuelle, mais il repère rapidement ce qui a été oublié.

Aller plus loin

  • Cacher SSH derrière un VPN (WireGuard, Tailscale) : SSH n’écoute plus que sur l’interface privée, avec ufw allow in on tailscale0 to any port 22 proto tcp, puis suppression de la règle publique. Le port 22 disparaît d’Internet.
  • Authentification à deux facteurs pour SSH avec libpam-google-authenticator.
  • CrowdSec comme alternative collaborative à fail2ban.
  • Référentiels : le guide de l’ANSSI Recommandations de configuration d’un système GNU/Linux et les CIS Benchmarks donnent des listes de contrôle détaillées.

En résumé

Aucune de ces mesures n’est difficile, et c’est tout l’intérêt : la sécurité d’un serveur repose bien plus sur la rigueur de l’ensemble que sur un outil sophistiqué. Un serveur qui n’expose que le nécessaire, s’authentifie par clé, se met à jour tout seul, journalise et se sauvegarde est déjà hors de portée de l’écrasante majorité des attaques opportunistes.

La prochaine étape est la supervision : savoir quand quelque chose d’anormal se produit. Elle fera l’objet d’un prochain article.