Sécuriser un VPS en 2026 nécessite une checklist de 35+ contrôles cumulatifs organisés en 7 phases : provisioning et choix d'OS, accès SSH durci, gestion des comptes utilisateurs, pare-feu et services exposés, patch management et hardening kernel, audit et monitoring, backups et résilience. Sans hardening, un VPS exposé reçoit ses premiers scans automatisés en moins de 60 secondes (sources GreyNoise, Shadowserver) et est compromis en heures pour les configurations par défaut (mot de passe faible, root login activé). Avec un durcissement aligné sur les CIS Benchmarks Linux et le Guide d'hygiène ANSSI, un VPS résiste aux scans automatisés et bloque l'écrasante majorité des tentatives d'intrusion opportunistes. Cet article détaille la checklist phase par phase avec commandes prêtes à coller, les outils d'audit automatique (Lynis, OpenSCAP, Bench Hardening), la stack monitoring 2026 (auditd, journald, fail2ban, Wazuh agent), et les CVE récentes à connaître (CVE-2024-6387 regreSSHion sur OpenSSH, CVE-2024-3094 xz-utils backdoor).
Modèle de menace : pourquoi un VPS est attaqué
Trois caractéristiques font des VPS exposés une cible massive et continue.
IP publique = scan immédiat. Les acteurs malveillants scannent en continu l'intégralité d'IPv4 (4,3 milliards d'adresses) en moins de 24 heures avec masscan ou ZMap depuis 2013. Tout nouveau VPS reçoit ses premiers paquets de reconnaissance en moins d'une minute. Les ports les plus scannés en 2026 selon Shadowserver Foundation : 22 (SSH), 80/443 (HTTP/S), 3306 (MySQL), 5432 (PostgreSQL), 6379 (Redis), 27017 (MongoDB), 23 (Telnet), 2375/2376 (Docker API), 9200 (Elasticsearch).
Configuration par défaut faible. Les images OS standards laissent le compte root accessible par mot de passe via SSH, des services écoutant sur 0.0.0.0, des packages superflus installés. Un attaquant exploite ces défauts en quelques secondes via Hydra, Patator, ou des botnets spécialisés (Mozi, FritzFrog, Outlaw).
Coût d'attaque négligeable. Un VPS compromis est revendu sur les marchés underground à 5 à 50 $ par accès initial, ou utilisé pour cryptomining, proxying malveillant, hosting C2, spam. Le ROI attaquant sur un VPS opportuniste est très élevé.
Phase 1 — Provisioning et choix d'OS
Choix de la distribution
Trois critères : durée de support, qualité des security advisories, écosystème.
| Distribution | Support principal | Use case typique |
|---|---|---|
| Ubuntu Server 24.04 LTS | Avril 2029 (gratuit) + ESM jusqu'en 2034 | Standard polyvalent, stack moderne |
| Debian 12 Bookworm | Juin 2028 (free) + LTS jusqu'en 2030 | Stabilité maximale, reproducibilité |
| Rocky Linux 9 / Alma Linux 9 | Mai 2032 | Clone RHEL gratuit, écosystème enterprise |
| RHEL 9 (souscription) | Mai 2032 + ELS | Production critique avec support officiel |
| Talos Linux | Rolling immutable | Kubernetes nodes uniquement |
Choix par défaut 2026 : Ubuntu Server 24.04 LTS. Stable, support long, écosystème massif, documentation abondante, advisories rapides via Ubuntu Security Notices.
Image OS minimale
Démarrer d'une image minimal ou cloud (sans GUI, sans paquets superflus). Sur AWS : Ubuntu 24.04 minimal AMI. Sur Azure : Ubuntu Server 24.04 LTS - Gen2. Sur OVH/Scaleway/Hetzner : image Ubuntu Server 24.04.
Partitionnement séparé
Séparer /, /home, /var, /tmp, /var/log permet de monter avec options spécifiques (noexec, nosuid, nodev) et limite les impacts d'une saturation disque.
# Vérification du partitionnement actuel
lsblk -f
df -h
# Exemple cible /etc/fstab
# /var/log mounté avec restrictions
UUID=8b3f-1e2a-4c7d /var/log ext4 defaults,nodev,nosuid,noexec 0 2
# /tmp en tmpfs avec restrictions
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,size=1G 0 0Phase 2 — Accès SSH durci
Le vecteur d'attaque numéro un sur tout VPS. Six contrôles cumulatifs.
Authentification par clé Ed25519
# Sur la machine cliente : génération clé Ed25519 (préférée à RSA en 2026)
ssh-keygen -t ed25519 -a 100 -C "naim@laptop-2026" -f ~/.ssh/id_ed25519_vps
# Déploiement sur le VPS (tant que password auth est encore active)
ssh-copy-id -i ~/.ssh/id_ed25519_vps.pub user@vps.example.test
# Test connexion par clé
ssh -i ~/.ssh/id_ed25519_vps user@vps.example.testConfiguration sshd_config durcie
# /etc/ssh/sshd_config — section sécurité 2026
Port 22 # ou port aléatoire entre 49152-65535 si VPS personnel
# Authentification
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
MaxSessions 5
# Restreindre aux utilisateurs autorisés
AllowUsers admin deploy
# ou AllowGroups ssh-users
# Algorithmes cryptographiques modernes (Mozilla Modern Profile)
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256
KexAlgorithms curve25519-sha256@libssh.org,curve25519-sha256,sntrup761x25519-sha512@openssh.com,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# Limites session
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
# Logs détaillés
LogLevel VERBOSE
# Désactivations
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no
GatewayPorts no
PrintMotd no
UseDNS no# Validation syntaxique avant restart
sshd -t
# Redémarrage du service
systemctl restart sshd
# CRITIQUE : tester avec une nouvelle session AVANT de fermer la session courante
# Garder une session admin ouverte pour fixer en cas d'erreurfail2ban actif
# Installation
apt install -y fail2ban
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
backend = systemd
[sshd]
enabled = true
port = 22
filter = sshd
maxretry = 3
bantime = 86400
findtime = 600
# Restart
systemctl enable --now fail2ban
# Vérification statut
fail2ban-client status sshdMFA SSH (optionnel selon contexte)
Pour bastion ou serveurs sensibles : module PAM Google Authenticator ou support FIDO2 natif OpenSSH.
# Approche 1 : Google Authenticator PAM
apt install -y libpam-google-authenticator
# Configuration utilisateur
google-authenticator -t -d -f -r 3 -R 30 -w 17
# /etc/pam.d/sshd : ajouter en début
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# Approche 2 : FIDO2 natif (OpenSSH 8.2+, février 2020)
ssh-keygen -t ed25519-sk -C "yubikey-2026"
# Touch your YubiKey to confirmPhase 3 — Comptes utilisateurs et sudo
Création d'un utilisateur dédié
# Création de l'utilisateur admin avec sudo
adduser admin
usermod -aG sudo admin
# Désactivation root SSH (déjà fait via PermitRootLogin no en phase 2)
# Le password root reste utilisable en console physique pour le rescue
# Lockdown du compte root (optionnel, agressif)
passwd -l rootsudo configuration
# /etc/sudoers.d/admin (jamais modifier /etc/sudoers directement, utiliser visudo)
admin ALL=(ALL:ALL) ALL
# Pour limiter sudo à des commandes précises (least privilege)
deploy ALL=(ALL:ALL) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/journalctl -u myapp
# Configuration globale durcie
Defaults logfile="/var/log/sudo.log"
Defaults log_input,log_output
Defaults timestamp_timeout=15
Defaults passwd_tries=3
Defaults requirettyComptes systèmes sans shell
# Audit des comptes ayant un shell de login
awk -F: '$7 ~ /\/(bash|sh|zsh|fish)$/ {print $1, $7}' /etc/passwd
# Verrouiller les comptes système qui n'ont pas besoin de shell
usermod -s /usr/sbin/nologin nobody-service-accountPolitique de mot de passe
# Installation
apt install -y libpam-pwquality
# /etc/security/pwquality.conf
minlen = 14
minclass = 4 # majuscules, minuscules, chiffres, symboles
maxrepeat = 3
gecoscheck = 1
dictcheck = 1
usercheck = 1
enforcing = 1
# /etc/login.defs
PASS_MAX_DAYS 90
PASS_MIN_DAYS 1
PASS_WARN_AGE 14Phase 4 — Pare-feu et services exposés
UFW (Ubuntu) ou nftables (Debian, RHEL)
# UFW : configuration deny by default
ufw default deny incoming
ufw default allow outgoing
# Autoriser uniquement les services nécessaires
ufw allow 22/tcp comment "SSH"
ufw allow 80/tcp comment "HTTP redirect"
ufw allow 443/tcp comment "HTTPS"
# Restriction par IP source si possible
ufw allow from 203.0.113.0/24 to any port 22 comment "SSH from office"
# Activation
ufw --force enable
ufw status verbose
# Logs
ufw logging onAudit des services exposés
# Listing des sockets en écoute
ss -tunlp
# Mapping ports vers processus
lsof -i -P -n | grep LISTEN
# Désactiver les services superflus identifiés
systemctl list-unit-files --type=service --state=enabled
systemctl disable --now telnet.socket nfs-server rpcbind avahi-daemon cupsRestriction par IP source via cloud provider
En complément du firewall OS, configurer le security group / network ACL au niveau du fournisseur cloud (AWS Security Group, Azure NSG, GCP firewall rules, OVH IP Firewall) pour limiter l'accès SSH aux IPs administratives.
Phase 5 — Patch management et hardening kernel
Mises à jour automatiques
# Ubuntu / Debian : unattended-upgrades
apt install -y unattended-upgrades apt-listchanges
# /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
};
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Unattended-Upgrade::Mail "admin@example.test";
# /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
# Test à blanc
unattended-upgrade --dry-run --debugKernel live patching (Ubuntu Pro, ksplice RHEL)
Pour les VPS avec uptime critique, kernel live patching évite les redémarrages pour les CVE kernel.
# Ubuntu Pro Livepatch (gratuit jusqu'à 5 machines personnelles)
pro attach <token>
pro enable livepatch
# Vérification
canonical-livepatch statusHardening sysctl
# /etc/sysctl.d/99-hardening.conf
# Network
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Process restrictions
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.yama.ptrace_scope = 1
kernel.unprivileged_bpf_disabled = 1
net.core.bpf_jit_harden = 2
# Memory protection
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2
fs.suid_dumpable = 0
kernel.randomize_va_space = 2
# Application
sysctl -p /etc/sysctl.d/99-hardening.confAppArmor ou SELinux
# Ubuntu : AppArmor activé par défaut, vérifier
aa-status
# Activer les profils enforce
aa-enforce /etc/apparmor.d/*Phase 6 — Audit, logs et monitoring
auditd pour audit système
# Installation
apt install -y auditd audispd-plugins
# /etc/audit/rules.d/audit.rules — règles inspirées CIS Benchmark
# Modifications fichiers sensibles
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/group -p wa -k group_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/sudoers.d/ -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
# Commandes privilégiées
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=4294967295 -k privileged_commands
# Modifications horloge
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time_change
# Restart
systemctl restart auditd
# Recherche dans les logs
ausearch -k passwd_changes -ts todayjournald centralisé
# /etc/systemd/journald.conf : persistance et limite
Storage=persistent
SystemMaxUse=500M
ForwardToSyslog=no
# Restart
systemctl restart systemd-journald
# Recherche
journalctl -u sshd --since "1 hour ago"
journalctl _SYSTEMD_UNIT=sshd.service --output=json | jqWazuh agent (SIEM open source)
Pour parc multi-VPS ou monitoring centralisé.
# Installation Wazuh agent (point vers manager Wazuh existant)
curl -sO https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_4.9.0-1_amd64.deb
WAZUH_MANAGER='wazuh-manager.example.test' \
WAZUH_AGENT_GROUP='vps-prod' \
dpkg -i wazuh-agent_4.9.0-1_amd64.deb
systemctl enable --now wazuh-agentFile integrity monitoring (AIDE)
# Installation
apt install -y aide aide-common
# Initialisation de la base de référence
aideinit
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Vérification quotidienne (cron par défaut /etc/cron.daily/aide)
aide --checkPhase 7 — Backups et résilience
Backups réguliers chiffrés
# borgbackup : moderne, déduplication, chiffrement par défaut
apt install -y borgbackup
# Initialisation repo distant
borg init --encryption=repokey ssh://user@backup.example.test/./repo
# Backup quotidien (cron 02:00)
borg create \
--stats --progress \
--exclude '/proc' --exclude '/sys' --exclude '/dev' \
--exclude '/var/cache' --exclude '/tmp' \
ssh://user@backup.example.test/./repo::vps-{now} \
/etc /home /var/log /var/www /opt /root
# Rétention : 7 daily, 4 weekly, 6 monthly
borg prune --keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
ssh://user@backup.example.test/./repo
# Test de restauration mensuel — non négociable
borg extract ssh://user@backup.example.test/./repo::vps-2026-04-01 /tmp/restore-testSnapshots cloud
Activer les snapshots automatiques au niveau du fournisseur (AWS EBS Snapshots, Azure Disk Snapshots, OVH backup, Hetzner snapshots). Stockage hors région idéal pour résilience géographique.
Hardening application stack
Nginx ou autre reverse proxy
# /etc/nginx/conf.d/security.conf
server_tokens off;
# Headers de sécurité
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
# TLS 1.3 uniquement, ciphers modernes
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# Limite de taille body (défense uploads anormaux)
client_max_body_size 10M;
client_body_timeout 30s;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;Bases de données
- PostgreSQL, MySQL :
listen_addressesoubind-addresssur 127.0.0.1 (jamais 0.0.0.0). - Redis :
bind 127.0.0.1,requirepass,protected-mode yes. - MongoDB :
bindIp: 127.0.0.1, authentification activée par défaut depuis 4.0.
# Audit : aucune base ne doit écouter sur Internet
ss -tlnp | grep -E ':(5432|3306|6379|27017|9200|11211)'
# Sortie attendue : uniquement 127.0.0.1, jamais 0.0.0.0Secrets et variables d'environnement
Jamais en clair dans des fichiers déployés. Utiliser un manager (Vault, AWS Secrets Manager, GCP Secret Manager) ou au minimum systemd EnvironmentFile avec permissions strictes.
# Fichier env avec permissions restreintes
chmod 600 /etc/myapp/secrets.env
chown myapp-user:myapp-user /etc/myapp/secrets.env
# /etc/systemd/system/myapp.service
[Service]
EnvironmentFile=/etc/myapp/secrets.env
ExecStart=/usr/bin/myapp
User=myapp-userOutils d'audit automatique
Lynis : audit complet en une commande
# Installation
apt install -y lynis
# Audit complet (durée : 1 à 3 minutes)
lynis audit system
# Audit avec rapport détaillé
lynis audit system --report-file /tmp/lynis-report.txt
cat /var/log/lynis-report.dat | grep -E 'warning|suggestion'
# Hardening index : objectif > 80 sur production
grep "Hardening index" /var/log/lynis.logOpenSCAP avec profils CIS
# Installation
apt install -y libopenscap8 ssg-debian
# Audit avec profil CIS Ubuntu Server
oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis \
--results-arf /tmp/arf.xml \
--report /tmp/report.html \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2404-ds.xml
# Génération de remédiation Ansible automatisée
oscap xccdf generate fix --profile xccdf_org.ssgproject.content_profile_cis \
--output /tmp/remediation.yml \
--fix-type ansible \
/tmp/arf.xmlchkrootkit et rkhunter
Détection de rootkits connus.
apt install -y chkrootkit rkhunter
chkrootkit
rkhunter --update
rkhunter --check --skip-keypressPoints clés à retenir
- Un VPS exposé sans hardening est compromis en heures. Les premiers scans automatisés arrivent en moins d'une minute. La défense par défaut ne suffit pas.
- La checklist 7 phases (provisioning, SSH, comptes, pare-feu, patch, audit, backups) couvre 90 % du périmètre. Application en 4 à 8 heures pour un VPS, 1 à 2 jours pour parfaire.
- SSH durci (clés Ed25519, désactivation root, fail2ban) + pare-feu deny-by-default + patch management automatique = socle minimum non négociable.
- CIS Benchmarks Linux et Guide d'hygiène ANSSI sont les référentiels pivots. Lynis et OpenSCAP automatisent l'audit contre ces référentiels.
- L'incident le plus fréquent en 2026 reste la non-application des patches : CVE-2024-6387 regreSSHion sur OpenSSH (juillet 2024) reste exploitable sur des dizaines de milliers de VPS non patchés.
Pour aller plus loin
- Container escape : techniques et défenses - sécurisation des workloads container sur le VPS.
- Secrets management cloud - éviter les secrets dans /etc et fichiers déployés.
- IAM expliqué simplement - couche identités cloud complémentaire au compte VPS.
- AWS security débutant - sécuriser le compte cloud qui héberge les VPS.





