Le lendemain de l’installation de Proxmox, les logs SSH affichaient déjà des tentatives de connexion. Pas quelques-unes – des centaines. Des scanners automatiques qui testent les mots de passe par défaut sur le port 22 vingt-quatre heures sur vingt-quatre. Ce n’est pas de la paranoïa, c’est de l’hygiène de base.
Ce dernier article de la série couvre la sécurisation du homelab : firewall Proxmox, fail2ban, SSH hardening, et Tailscale pour accéder à tout depuis l’extérieur sans ouvrir un seul port.
Ce qui arrive sans sécurisation
# Voir les tentatives SSH en temps réel
journalctl -f -u sshd | grep "Failed password"
# Jun 12 03:12:45 sshd[12847]: Failed password for root from 185.220.101.32
# Jun 12 03:12:47 sshd[12848]: Failed password for admin from 185.220.101.32
# Jun 12 03:12:49 sshd[12849]: Failed password for ubuntu from 45.142.212.100
# ...
Ces adresses sont des nœuds Tor et des VPS compromis qui scannent Internet en permanence. Le port 22 ouvert sur une IP publique reçoit ces tentatives en moins d’une heure après exposition. Si le mot de passe root est password123 ou proxmox, c’est plié en quelques minutes.
L’interface Proxmox sur le port 8006 est également ciblée – des scripts spécifiques à Proxmox testent les combinaisons root/identifiants courants.
La règle d’or : ne jamais exposer directement le nœud Proxmox sur Internet. Tailscale règle ce problème proprement.
1. Firewall Proxmox : trois niveaux d’isolation
Le firewall Proxmox fonctionne à trois niveaux : Datacenter (règles globales), nœud (règles du nœud lui-même), et VM/LXC (règles individuelles). Les règles s’appliquent en cascade – une règle Datacenter s’applique à tout.
Activer le firewall Datacenter
GUI Proxmox : Datacenter > Firewall > Options
Firewall : Enabled ✅
Input Policy : DROP ← rejeter tout ce qui n'est pas explicitement autorisé
Output Policy : ACCEPT
Log level (in) : info
Ajouter les règles entrantes minimales (VLAN 192.168.0.0/16 = tout le LAN) :
Datacenter > Firewall > Add Rule :
Direction : in
Action : ACCEPT
Protocol : tcp
Dest. port : 22
Source : 192.168.0.0/16
Comment : SSH depuis LAN
Direction : in
Action : ACCEPT
Protocol : tcp
Dest. port : 8006
Source : 192.168.0.0/16
Comment : Proxmox UI depuis LAN
Direction : in
Action : ACCEPT
Protocol : tcp
Dest. port : 3128
Source : 192.168.0.0/16
Comment : SPICE proxy
Direction : in
Action : ACCEPT
Protocol : udp
Dest. port : 5404,5405
Source : 192.168.0.0/16
Comment : Corosync (si futur cluster)
Activer le firewall sur le nœud
GUI : pve > Firewall > Options
Firewall : Enabled ✅
Avant d’activer le firewall avec INPUT POLICY: DROP, vérifier que les règles SSH et Proxmox UI sont bien créées et actives. Activer le firewall sans règle d’accès SSH verrouille l’accès au nœud – il faudra se connecter physiquement pour corriger.
Règles par VM/LXC pour les services exposés
Pour un LXC qui sert du contenu HTTPS public (Nginx Proxy Manager) :
CT 160 > Firewall > Add Rule :
in ACCEPT tcp 80 from any (HTTP → redirect HTTPS)
in ACCEPT tcp 443 from any (HTTPS public)
DROP all
Pour Pi-hole, accessible depuis tous les VLANs mais pas depuis Internet :
CT 150 > Firewall > Add Rule :
in ACCEPT tcp/udp 53 from 192.168.0.0/16 (DNS depuis LAN)
in ACCEPT tcp 80 from 192.168.0.0/16 (interface web depuis LAN)
DROP all
2. fail2ban : bloquer les brute force
fail2ban surveille les logs et bannit temporairement les IPs qui accumulent trop d’échecs d’authentification.
apt install -y fail2ban
# Configuration locale (surcharge /etc/fail2ban/jail.conf sans l'éditer)
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600 # Bannissement 1h
findtime = 600 # Fenêtre de détection 10 min
maxretry = 5 # Nombre d'échecs avant bannissement
banaction = iptables-multiport
[sshd]
enabled = true
port = ssh
logpath = %(sshdlog)s
backend = %(sshdbackend)s
maxretry = 3 # Plus strict pour SSH
[proxmox]
enabled = true
port = https,8006
filter = proxmox
logpath = /var/log/daemon.log
maxretry = 3
bantime = 7200 # Bannissement 2h pour l'interface web
EOF
Créer le filtre pour Proxmox (les logs de pvedaemon ont un format spécifique) :
cat > /etc/fail2ban/filter.d/proxmox.conf << 'EOF'
[Definition]
failregex = pvedaemon[.]: authentication failure; rhost= user=. msg=.
ignoreregex =
EOF
systemctl enable --now fail2ban
# Vérifier que les jails sont actives
fail2ban-client status
# Status
# |- Number of jail: 2
# `- Jail list: proxmox, sshd
fail2ban-client status sshd
# Status for the jail: sshd
# |- Filter
# | |- Currently failed: 1
# | `- Total failed: 847 ← c'est normal
# `- Actions
# |- Currently banned: 3
# `- Total banned: 89
847 tentatives échouées en quelques semaines. 89 IPs bannies. Sans fail2ban, c’est 847 tentatives qui continuent.
Pour débannir une IP légitime qu’on aurait malencontreusement bannie :
fail2ban-client set sshd unbanip 192.168.1.50
3. SSH hardening
L’authentification par clé a été configurée à l’article 2. Voici les durcissements supplémentaires :
# /etc/ssh/sshdconfig - ajuster ou vérifier ces lignes
# Changer le port par défaut (sécurité par l'obscurité, mais ça vide les logs des scanners)
Port 2222
# Désactiver root en password, autoriser en clé uniquement
PermitRootLogin prohibit-password
# Désactiver auth par mot de passe (déjà fait à l'article 2, vérifier)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Limiter les tentatives de connexion
MaxAuthTries 3
MaxSessions 5
# Désactiver les fonctionnalités non nécessaires
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
# Restreindre les algorithmes (prefer modern crypto)
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
KexAlgorithms curve25519-sha256,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
# Timeout de session inactive
ClientAliveInterval 300
ClientAliveCountMax 2
# Tester la config avant de redémarrer
sshd -t
# Si aucun output : config valide
systemctl restart sshd
# Vérifier que la connexion fonctionne avec le nouveau port (dans une autre session)
ssh -p 2222 root@192.168.1.10
Mettre à jour fail2ban si le port SSH a changé :
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222 # ← adapter au nouveau port
4. Tailscale : accès distant sans ouvrir de ports
Tailscale crée un réseau mesh WireGuard entre tous les appareils du compte. La Freebox ne voit aucun port ouvert – Tailscale utilise des connexions sortantes et le NAT traversal pour établir les tunnels. Depuis l’iPhone en 4G, on accède à Proxmox comme si on était sur le LAN.
# Sur le nœud Proxmox
curl -fsSL https://tailscale.com/install.sh | sh
# Démarrer Tailscale et s'authentifier
tailscale up
# Un lien d'authentification s'affiche - l'ouvrir dans le navigateur
# https://login.tailscale.com/a/xxxxxxxxxx
Après authentification, le nœud apparaît dans le dashboard Tailscale avec une IP 100.x.x.x. Installer Tailscale sur les appareils clients (iPhone, Mac, iPad) depuis les apps stores officiels, même compte.
Accès Proxmox depuis l’extérieur :
# Via IP Tailscale
https://100.64.0.10:8006
# Via MagicDNS (si activé dans les settings Tailscale)
https://pve.tail-xxxx.ts.net:8006
MagicDNS assigne des noms DNS automatiquement à chaque machine du réseau Tailscale. Plus besoin de mémoriser les IPs 100.x.x.x.
Tailscale sur le nœud Proxmox permet aussi d’accéder directement aux VMs et LXC via leurs IPs internes, puisque le nœud peut faire office de subnet router :
# Annoncer le sous-réseau homelab entier sur Tailscale
tailscale up --advertise-routes=192.168.0.0/16,10.10.0.0/24
# Approuver les routes dans le dashboard Tailscale admin
# Settings > Machines > pve > Edit route settings > Approuver les sous-réseaux
Avec ça, depuis n’importe où dans le monde avec Tailscale actif, on accède à 192.168.30.162 (Nextcloud), 192.168.30.121 (Grafana), 192.168.1.10:8006 (Proxmox) – sans aucun port ouvert sur la Freebox.
Le plan Tailscale gratuit permet 100 appareils et 3 utilisateurs. Pour un homelab solo, c’est largement suffisant et gratuit indéfiniment.
5. Mises à jour automatiques
La sécurité d’un nœud non mis à jour se dégrade rapidement. Les CVE critiques sur OpenSSH, le kernel Linux et les paquets Debian sont exploités activement quelques jours après publication.
apt install -y unattended-upgrades apt-listchanges
# Configurer les mises à jour automatiques de sécurité uniquement
cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Allowed-Origins {
"${distroid}:${distro_codename}-security";
"https://download.proxmox.com/debian/pve bookworm pve-no-subscription";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false"; // Reboot manuel pour Proxmox
Unattended-Upgrade::Mail "you@arewel.com";
EOF
dpkg-reconfigure -plow unattended-upgrades
Les mises à jour non-sécurité (dist-upgrade) restent manuelles – on ne veut pas que Proxmox redémarre tout seul avec une mise à jour majeure à 2h du matin quand des VMs critiques tournent.
Checklist sécurité finale
Après dix articles, voilà où on en est :
Réseau :
- [ ] VLANs activés (Management / LAN / IoT / Serveurs / Stockage)
- [ ] Firewall Proxmox actif, INPUT POLICY DROP
- [ ] Aucun port exposé directement sur Internet depuis la Freebox
- [ ] Accès distant uniquement via Tailscale
- [ ] SSH par clé uniquement, mot de passe désactivé
- [ ] Port SSH non-standard (ex: 2222)
- [ ] fail2ban actif sur SSH et Proxmox UI
- [ ] Utilisateur non-root créé pour l’interface Proxmox web
- [ ] Compte Tailscale protégé par 2FA
- [ ] Mises à jour sécurité automatiques activées
- [ ]
apt dist-upgradeplanifié manuellement toutes les 2 semaines - [ ] Versions logicielles suivies via Grafana/Uptime Kuma
- [ ] PBS configuré, jobs quotidiens, rétention 7j/4s/3m
- [ ] Backup hors-site actif (Backblaze B2 ou similaire)
- [ ] Test de restauration réalisé et documenté
- [ ] Grafana/Prometheus actifs, alertes Telegram configurées
- [ ] Uptime Kuma surveille tous les services
- [ ] Alerte disque plein à 90% active
Authentification :
Mises à jour :
Sauvegardes :
Surveillance :
Ce qui a coincé
En activant le firewall Datacenter avec INPUT POLICY: DROP, j’ai perdu l’accès SSH et à l’interface web simultanément. Les règles étaient créées mais dans le mauvais ordre – la règle DROP all était listée avant les règles ACCEPT dans l’évaluation.
Le firewall Proxmox évalue les règles dans l’ordre d’affichage. Une règle DROP explicite avant les ACCEPT les court-circuite. La politique INPUT DROP s’applique en dernier resort, après l’évaluation de toutes les règles. Mais si j’avais une règle DROP all explicite dans la liste, elle était évaluée avant les ACCEPT.
Pour récupérer l’accès : brancher un écran et un clavier sur le MS-01, accéder à la console physique, désactiver temporairement le firewall via pve-firewall stop, corriger l’ordre des règles, relancer.
# Désactiver le firewall en urgence depuis la console physique
pve-firewall stop
# Vérifier l'état
pve-firewall status
# Status: stopped
# Après correction des règles dans la GUI
pve-firewall start
Depuis, je teste toujours les changements de firewall depuis une session SSH active sur la console physique (pas via réseau). Si la session SSH existante reste ouverte, même si le firewall bloque les nouvelles connexions, la session active survit – ce qui donne le temps de corriger.
Conclusion de la série
En dix articles, on est partis d’un barebone Minisforum MS-01 à €439 et on est arrivés à un homelab Proxmox complet : hyperviseur installé et configuré, réseau segmenté en VLANs, DNS avec blocage publicitaire, stockage ZFS redondant, sauvegardes automatiques avec PBS, une dizaine de services self-hosted, monitoring Grafana/Prometheus, et une surface d’attaque réduite au minimum.
La consommation électrique totale du setup : ~16W au repos (13W MS-01 + 3W switch). Sur une année, ça fait ~140 kWh, soit ~28€ au tarif EDF actuel. Les abonnements SaaS remplacés représentaient €85/mois, soit €1020/an.
Le homelab s’est amorti en moins d’un an.
Ce qui reste à explorer si on veut aller plus loin : Kubernetes (k3s) sur Proxmox, cluster multi-nœuds avec Ceph, intégration Ansible pour la gestion de configuration, et une instance Loki pour les logs centralisés. Mais ça, c’est pour une autre série.
Série « Homelab Proxmox MS-01 » – Article 10/10