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
- Mettre à jour le système.
- Créer un utilisateur non root avec sudo.
- Passer SSH en authentification par clé uniquement.
- Activer un pare-feu en refus par défaut.
- Installer fail2ban.
- Activer les mises à jour de sécurité automatiques.
- Réduire la surface : services inutiles et paramètres noyau.
- Maîtriser Docker s’il est présent.
- Journaliser et auditer.
- 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 fichier50-cloud-init.confqui réactivePasswordAuthentication yes. Le vôtre doit passer avant. sshd -tvé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_filteren 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-USERquand 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.