Introduction #
Quand on déploie Proxmox VE sur un serveur dédié (Scaleway, OVH, Hetzner…), on se retrouve généralement avec une seule adresse IP publique. Dans ce contexte, comment faire tourner plusieurs VMs et conteneurs LXC avec un accès Internet ? Comment exposer vos services via HTTPS avec des certificats SSL valides ? Comment sécuriser tout ça avec le pare-feu intégré ? Et surtout, comment débugger quand ça ne fonctionne pas comme prévu ?
Cet article retrace mon expérience sur un serveur Scaleway Start-2-S-SATA et les pièges que j’ai rencontrés. Vous apprendrez à :
- Configurer un réseau privé avec NAT pour vos VMs/LXC
- Installer et configurer Caddy comme reverse proxy avec SSL automatique
- Paramétrer le pare-feu Proxmox multi-niveaux
- Diagnostiquer les problèmes réseau et de pare-feu
C’est également un complément à l’étape 8 de l’installation de Proxmox VE qui est détaillé sur le premier article de cette série.
Note : Les adresses IP utilisées dans cet article sont issues des plages réservées à la documentation par l’IANA (définies dans les RFC 5737 et 3849). Elles ne correspondent à aucun service réel et doivent être adaptées à votre environnement : IPv4 (RFC 5737)
- 192.0.2.0/24 — TEST-NET-1 : utilisée ici pour le réseau privé des VMs/LXC
- 198.51.100.0/24 — TEST-NET-2 : utilisée ici pour l’adressage public
- 203.0.113.0/24 — TEST-NET-3 : non utilisée dans cet article
Le contexte : une IP publique, plusieurs services #
Mon serveur dédié Scaleway dispose d’une seule adresse IP publique. Considérons ici que c’est cette adresse : 198.51.100.100.
Sur ce serveur, je fais tourner Proxmox VE avec plusieurs conteneurs LXC, dont un qui héberge Garage S3 (une solution de stockage compatible S3).
Objectif de cet article :
- Exposer plusieurs services (Garage S3, interface Proxmox) via HTTPS avec certificats SSL valides
- Utiliser Caddy comme reverse-proxy installé directement sur le nœud Proxmox
- Configurer le pare-feu Proxmox pour sécuriser l’accès aux services
- Automatiser la gestion des certificats SSL via Let’s Encrypt
Architecture réseau : le bridge isolé avec NAT #
Avec une seule IP publique, impossible d’attribuer des IPs publiques à chaque VM ou conteneur. La solution classique : créer un réseau privé interne avec NAT.
Schéma de l’architecture #
graph TB
subgraph Internet
INT[("🌐 Internet
Client HTTPS")]
end
subgraph Proxmox["Nœud Proxmox (198.51.100.100)"]
ENP["Interface physique
enp0s20
198.51.100.100"]
CADDY["🔒 Caddy Reverse Proxy
Ports 80/443
Certificats Let's Encrypt"]
VMBR0["Bridge isolé
vmbr0
192.0.2.1/24"]
end
subgraph VMs["Réseau privé (NAT)"]
LXC1["LXC Garage S3
192.0.2.21
Ports 3900/3909"]
LXC2["LXC Autre service
192.0.2.22"]
VM1["VM Autre
192.0.2.30"]
end
INT -->|"HTTPS :443"| ENP
ENP --> CADDY
CADDY -->|"HTTP interne"| VMBR0
VMBR0 -->|"Proxy pass"| LXC1
VMBR0 -.->|"Autres services"| LXC2
VMBR0 -.->|"Autres services"| VM1
style INT fill:#e0f2fe,stroke:#0284c7,stroke-width:3px,color:#000
style ENP fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#000
style CADDY fill:#dbeafe,stroke:#2563eb,stroke-width:3px,color:#000
style VMBR0 fill:#d1fae5,stroke:#059669,stroke-width:2px,color:#000
style LXC1 fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#000
style LXC2 fill:#f3e8ff,stroke:#9333ea,stroke-width:1px,color:#000,stroke-dasharray: 5 5
style VM1 fill:#ffedd5,stroke:#ea580c,stroke-width:1px,color:#000,stroke-dasharray: 5 5
Légende :
- 🌐 Internet : clients externes accédant aux services via HTTPS
- 🔒 Caddy : reverse proxy avec certificats SSL automatiques (Let’s Encrypt)
- Bridge vmbr0 : réseau privé isolé avec NAT pour les VMs/LXC
- Garage S3 : service de stockage S3 exposé via Caddy
Configuration réseau du nœud Proxmox #
Voici la configuration dans /etc/network/interfaces :
auto lo
iface lo inet loopback
# Interface physique avec l'IP publique
auto enp0s20
iface enp0s20 inet static
address 198.51.100.100/24
gateway 198.51.100.1
# Bridge isolé pour le réseau privé
auto vmbr0
iface vmbr0 inet static
address 192.0.2.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
# Activer le routage
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
# NAT pour la sortie Internet des VMs/LXC
post-up iptables -t nat -A POSTROUTING -s '192.0.2.0/24' -o enp0s20 -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s '192.0.2.0/24' -o enp0s20 -j MASQUERADE
# Gestion des zones conntrack pour le pare-feu Proxmox
post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
Points clés :
bridge-ports none: le bridge est isolé, il n’est pas rattaché à une interface physiqueip_forward: active le routage entre les interfaces- La règle
MASQUERADE: tout le trafic sortant du réseau 192.0.2.0/24 est “naté” derrière l’IP publique - Les règles
conntrack zone: nécessaires pour que le pare-feu Proxmox fonctionne correctement avec les bridges
Avec cette configuration, les conteneurs utilisent 192.0.2.1 comme gateway et peuvent accéder à Internet.
Je vous invite à aller jeter un oeil sur la première partie de cet article où j’ai passé un peu de temps à expliciter la configuration réseau.
Configuration DNS : pointer vers le serveur #
Pour que vos services soient accessibles depuis Internet via des noms de domaine, vous devez créer des enregistrements DNS de type A (pour IPv4) qui pointent vers l’adresse IP publique de votre nœud Proxmox.
Exemple de configuration DNS #
Dans votre fournisseur DNS (Cloudflare, OVH, Gandi, etc.), créez les enregistrements suivants :
| Type | Nom | Valeur | TTL |
|---|---|---|---|
| A | pve.example.com | 198.51.100.100 | 3600 |
| A | garage-dashboard.example.com | 198.51.100.100 | 3600 |
| A | garage-api.example.com | 198.51.100.100 | 3600 |
example.com par votre propre nom de domaine et 198.51.100.100 par l’adresse IP publique réelle de votre serveur Proxmox.
Vérification de la propagation DNS #
Une fois les enregistrements créés, vous pouvez vérifier qu’ils sont correctement configurés :
# Vérifier la résolution DNS
dig +short pve.example.com
dig +short garage-dashboard.example.com
dig +short garage-api.example.com
Résultat attendu : Les commandes doivent retourner 198.51.100.100
Installation et configuration de Caddy #
Pourquoi Caddy comme reverse-proxy ? #
Pour ce projet, j’ai choisi Caddy pour plusieurs raisons :
Avantages de Caddy :
- Simplicité extrême : configuration minimale, syntaxe claire
- HTTPS automatique : génération et renouvellement automatique des certificats Let’s Encrypt
- Léger : faible empreinte mémoire comparé à Nginx ou Apache
- Autonome : pas besoin de certbot ou autre outil externe pour SSL
- Configuration en quelques lignes : pas de fichiers complexes à gérer
Installation de Caddy sur le nœud Proxmox #
Connectez-vous en SSH sur votre nœud Proxmox et installez Caddy :
# Installation des dépendances
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
# Ajout du dépôt officiel Caddy
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
# Installation de Caddy
apt update
apt install caddy
apt upgrade.
Vérification de l’installation #
# Vérifier la version installée
caddy version
# Vérifier le statut du service
systemctl status caddy
Résultat attendu :
● caddy.service - Caddy
Loaded: loaded (/lib/systemd/system/caddy.service; enabled; vendor preset: enabled)
Active: active (running) since ...
Configuration du Caddyfile #
Le fichier de configuration principal de Caddy est /etc/caddy/Caddyfile. Voici la configuration complète pour notre cas d’usage :
# Éditer le fichier de configuration
vi /etc/caddy/Caddyfile
Contenu du fichier /etc/caddy/Caddyfile :
# Configuration globale
{
# Adresse email pour les notifications Let's Encrypt
email user@example.com
}
# Reverse proxy pour l'interface web Proxmox
pve.example.com {
reverse_proxy https://localhost:8006 {
transport http {
tls_insecure_skip_verify
}
}
}
# Reverse proxy pour le dashboard Garage S3
garage-dashboard.example.com {
reverse_proxy http://192.0.2.21:3909
}
# Reverse proxy pour l'API Garage S3
garage-api.example.com {
reverse_proxy http://192.0.2.21:3900
}
user@example.com par votre véritable adresse email. Let’s Encrypt l’utilisera pour vous notifier en cas d’expiration de certificat ou de problème de renouvellement.
Explication de la configuration #
Bloc global {...}
#
{
email user@example.com
}
- email : adresse utilisée par Let’s Encrypt pour les notifications importantes
- Caddy utilisera automatiquement cette adresse pour tous les certificats générés
Configuration Proxmox VE #
pve.example.com {
reverse_proxy https://localhost:8006 {
transport http {
tls_insecure_skip_verify
}
}
}
Explications :
pve.example.com: domaine pour accéder à l’interface web Proxmoxreverse_proxy https://localhost:8006: redirige vers le port HTTPS local de Proxmoxtls_insecure_skip_verify: nécessaire car Proxmox utilise un certificat auto-signé
tls_insecure_skip_verify désactive la vérification du certificat SSL de Proxmox. Cette configuration est acceptable pour une communication localhost, mais ne devrait jamais être utilisée pour des connexions externes non sécurisées.
Configuration Garage S3 #
garage-dashboard.example.com {
reverse_proxy http://192.0.2.21:3909
}
garage-api.example.com {
reverse_proxy http://192.0.2.21:3900
}
Explications :
- Communication en HTTP simple entre Caddy et le conteneur LXC (réseau interne sécurisé)
- Caddy termine le SSL/TLS côté externe, le trafic interne reste en clair
192.0.2.21: adresse IP du conteneur LXC Garage S3 sur le réseau privé
Validation de la configuration #
Avant de redémarrer Caddy, il est recommandé de valider la syntaxe du fichier de configuration :
# Valider la configuration
caddy validate --config /etc/caddy/Caddyfile
Résultat attendu :
Valid configuration
Redémarrage de Caddy #
Une fois la configuration validée, redémarrez le service :
# Redémarrer Caddy pour appliquer la configuration
systemctl restart caddy
# Vérifier que le service est bien actif
systemctl status caddy
active (running), Caddy est correctement configuré et en cours d’exécution.
Génération automatique des certificats SSL #
Dès le premier accès à vos domaines, Caddy va automatiquement :
- Détecter les noms de domaine configurés dans le Caddyfile
- Contacter Let’s Encrypt pour demander un certificat
- Valider le domaine via le challenge HTTP-01 (port 80)
- Installer le certificat SSL/TLS (valide 90 jours)
- Configurer le renouvellement automatique (30 jours avant expiration)
sequenceDiagram
participant Client as Client Web
participant Caddy as Caddy
participant LE as Let's Encrypt
participant Backend as Service Backend
Note over Caddy,LE: Première requête HTTPS
Client->>Caddy: Connexion HTTPS initiale
Caddy->>LE: Demande de certificat
LE->>Caddy: Challenge HTTP-01
Caddy->>LE: Réponse au challenge
LE->>Caddy: Certificat SSL délivré
Note over Caddy: Installation du certificat
Caddy->>Backend: Requête HTTP backend
Backend-->>Caddy: Réponse
Caddy-->>Client: Réponse HTTPS sécurisée
Vérification des certificats générés #
# Lister les certificats générés par Caddy
ls -la /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/
# Afficher les logs de Caddy pour voir la génération des certificats
journalctl -u caddy -f
Logs attendus lors de la génération d’un certificat :
[INFO] [pve.example.com] acme: Obtaining bundled SAN certificate
[INFO] [pve.example.com] AuthURL: https://acme-v02.api.letsencrypt.org/...
[INFO] [pve.example.com] acme: authorization already valid; skipping challenge
[INFO] [pve.example.com] acme: Validations succeeded; requesting certificates
[INFO] [pve.example.com] Server responded with a certificate
Test d’accès aux services #
Ouvrez votre navigateur web et testez l’accès aux services :
- Interface Proxmox :
https://pve.example.com - Dashboard Garage S3 :
https://garage-dashboard.example.com - API Garage S3 :
https://garage-api.example.com
Ouverture des ports nécessaires #
Pour que Caddy fonctionne correctement, vous devez vous assurer que les ports suivants sont accessibles depuis Internet :
| Port | Protocole | Usage |
|---|---|---|
| 80 | HTTP | Validation Let’s Encrypt (challenge HTTP-01) et redirection HTTPS |
| 443 | HTTPS | Trafic HTTPS sécurisé |
Ces règles seront à ajouter au niveau du pare-feu Proxmox (voir section suivante).
Le pare-feu Proxmox : trois niveaux de protection #
Proxmox intègre un pare-feu qui peut s’activer à trois niveaux : datacenter, nœud et VM/conteneur. Chaque niveau a son propre fichier de configuration.
Niveau Datacenter #
Fichier : /etc/pve/firewall/cluster.fw
C’est ici qu’on définit les règles globales et les IPSET (groupes d’adresses IP réutilisables).
[OPTIONS]
policy_in: DROP
policy_out: ACCEPT
enable: 1
[IPSET ip-whitelist]
# IPs autorisées pour l'administration
# Renseignez ici les adresses IP aptes à se connecter à votre noeud Proxmox
203.0.113.50
203.0.113.51
[IPSET scaleway-pve]
# IP publique du nœud
198.51.100.100
[RULES]
# Accès SSH depuis les IPs whitelistées uniquement
IN ACCEPT -i enp0s20 -source +dc/ip-whitelist -p tcp -dport 22 -log nolog
# Caddy : ports HTTP et HTTPS (nécessaires pour Let's Encrypt et le reverse proxy)
IN ACCEPT -i enp0s20 -p tcp -dport 80 -log nolog
IN ACCEPT -i enp0s20 -p tcp -dport 443 -log nolog
# Interface web Proxmox (accessible via Caddy sur pve.example.com)
# Ce port peut être fermé si vous accédez uniquement via le reverse proxy
IN ACCEPT -i enp0s20 -source +dc/ip-whitelist -p tcp -dport 8006 -log nolog
La syntaxe +dc/nom-ipset permet de référencer un IPSET défini au niveau datacenter.
Info - Configuration avec Caddy :
- Les ports 80 et 443 sont ouverts sans restriction d’IP source car Caddy doit être accessible depuis Internet pour servir le reverse proxy
- Le port 80 est indispensable pour que Let’s Encrypt puisse valider vos domaines (challenge HTTP-01)
- Le port 8006 (interface web Proxmox) reste protégé par l’IPSET
ip-whitelistpour un accès direct sécurisé - Une fois Caddy configuré, vous pouvez fermer complètement le port 8006 et accéder à Proxmox uniquement via
https://pve.example.com
Niveau Conteneur/VM #
L’ID de mon conteneur LXC est le 321. Le fichier a modifié est donc le suivant : /etc/pve/firewall/321.fw
On peut bien entendu le faire via l’interface web de Proxmox dans le conteneur 321.
Chaque VM ou conteneur peut avoir ses propres règles. Pour mon conteneur Garage S3 (VMID 321) :
[OPTIONS]
enable: 1
[RULES]
IN ACCEPT -p tcp -dport 3900 -log nolog # GARAGE S3 API
IN ACCEPT -p tcp -dport 3909 -log nolog # GARAGE S3 Dashboard
Ces règles autorisent l’accès aux ports de Garage depuis n’importe quelle source. Ça fonctionne… mais ce n’est pas idéal niveau sécurité.
Le piège : restreindre la source des connexions #
Naturellement, j’ai voulu restreindre l’accès à Garage S3 : seul Caddy (sur le nœud Proxmox) devrait pouvoir s’y connecter. J’ai donc modifié les règles :
[RULES]
IN ACCEPT -source +dc/scaleway-pve -p tcp -dport 3900 -log nolog
IN ACCEPT -source +dc/scaleway-pve -p tcp -dport 3909 -log nolog
Avec l’IPSET scaleway-pve contenant 198.51.100.100 (l’IP publique du nœud).
Résultat : ça ne fonctionne plus !
Les connexions depuis Caddy sont bloquées.
Diagnostic : tracer les paquets réseau #
Pour comprendre ce qui se passe, il faut voir quelle adresse IP source est réellement utilisée.
Étape 1 : identifier la route #
Sur le nœud Proxmox, on vérifie comment les paquets sont routés vers le conteneur :
ip route get 192.0.2.21
Résultat :
192.0.2.21 dev vmbr0 src 192.0.2.1 uid 0
cache
Révélation : le nœud utilise 192.0.2.1 comme adresse source, pas 198.51.100.100 !
Étape 2 : confirmer avec tcpdump #
On installe tcpdump si nécessaire :
apt install tcpdump
Puis on capture le trafic sur le bridge :
tcpdump -i vmbr0 -nn port 3909
En parallèle, on tente d’accéder au service via Caddy. Résultat :
09:36:10.506947 IP 192.0.2.1.38114 > 192.0.2.21.3909: Flags [S], seq 845540706, ...
09:36:11.530378 IP 192.0.2.1.38114 > 192.0.2.21.3909: Flags [S], seq 845540706, ...
09:36:12.554384 IP 192.0.2.1.38114 > 192.0.2.21.3909: Flags [S], seq 845540706, ...
Observations :
- La source est bien
192.0.2.1, pas l’IP publique - Les paquets SYN (
[S]) sont répétés sans réponse : le pare-feu les bloque - Pas de SYN-ACK en retour : la connexion TCP ne peut pas s’établir
Comprendre le flux réseau #
Le schéma du flux aide à visualiser pourquoi l’IP source n’est pas celle attendue :
flowchart LR
subgraph Internet
CLIENT["👤 Client
Internet"]
end
subgraph Proxmox["Nœud Proxmox"]
ENP["enp0s20
198.51.100.100"]
CADDY["Caddy
(reverse proxy)"]
VMBR0["vmbr0
192.0.2.1"]
end
subgraph LXC["Conteneur LXC"]
GARAGE["Garage S3
192.0.2.21"]
end
CLIENT -->|"HTTPS
:443"| ENP
ENP --> CADDY
CADDY -->|"HTTP interne
192.0.2.1 → 192.0.2.21"| VMBR0
VMBR0 -->|":3900 / :3909"| GARAGE
style CLIENT fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#000
style ENP fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#000
style CADDY fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#000
style VMBR0 fill:#d1fae5,stroke:#059669,stroke-width:2px,color:#000
style GARAGE fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#000
Quand Caddy ouvre une connexion vers le conteneur :
- Il utilise l’interface
vmbr0(car c’est la route vers 192.0.2.0/24 - cf. Diagnostic : tracer les paquets réseau > Étape 1 : identifier la route) - L’adresse source est celle de l’interface
vmbr0:192.0.2.1 - L’IP publique
198.51.100.100n’est jamais impliquée dans ce flux interne
C’est logique : pourquoi le noyau utiliserait-il l’IP d’une autre interface ?
La solution : adapter les IPSET #
Il faut ajouter l’adresse IP interne du nœud à notre IPSET, ou mieux, créer un IPSET dédié au trafic interne.
Rappel : La configuration se réalise dans le fichier
/etc/pve/firewall/cluster.fwou sur l’interface Web de Proxmox :Datacenter>Firewall>IPSet
Option 1 : enrichir l’IPSET existant #
[IPSET scaleway-pve]
198.51.100.100
192.0.2.1
Option 2 : séparer les contextes (recommandé) #
[IPSET scaleway-pve]
198.51.100.100
[IPSET pve-vmbr0]
192.0.2.1
Et dans le pare-feu du conteneur :
[RULES]
IN ACCEPT -source +dc/pve-vmbr0 -p tcp -dport 3900 -log nolog
IN ACCEPT -source +dc/pve-vmbr0 -p tcp -dport 3909 -log nolog
Cette approche est plus explicite :
- Les ports de Garage ne sont accessibles que depuis le réseau interne via le reverse-proxy, jamais directement depuis Internet.
Option 3 : utiliser directement les adresses IP (sans IPSET) #
Cette approche est particulièrement utile pour :
- Configurations simples avec peu d’adresses à gérer
- Tests rapides sans créer de nombreux IPSET
- Règles temporaires ou ponctuelles
Syntaxe avec adresse IP unique :
[RULES]
IN ACCEPT -source 192.0.2.1 -p tcp -dport 3900 -log nolog
IN ACCEPT -source 192.0.2.1 -p tcp -dport 3909 -log nolog
Syntaxe avec plage réseau (CIDR) :
[RULES]
IN ACCEPT -source 192.0.2.0/24 -p tcp -dport 3900 -log nolog
IN ACCEPT -source 192.0.2.0/24 -p tcp -dport 3909 -log nolog
Syntaxe avec plusieurs sources (séparées par virgule) :
[RULES]
IN ACCEPT -source 192.0.2.1,198.51.100.100 -p tcp -dport 3900 -log nolog
IN ACCEPT -source 192.0.2.1,198.51.100.100 -p tcp -dport 3909 -log nolog
198.51.100.100 est purement fictive dans cet exemple. Elle illustre simplement la possibilité de spécifier plusieurs sources séparées par des virgules. Comme on l’a vu plus haut, affecter l’IP publique de notre serveur Proxmox n’aura pas d’effet sur la règle de parefeu pour exposer les ports 3900 ou 3909 à Caddy.
Attention : Si vous avez de nombreuses adresses ou si celles-ci changent fréquemment, les IPSET restent préférables car :
- Plus facile à maintenir (modification en un seul endroit)
- Meilleure lisibilité (nom explicite plutôt qu’une IP)
- Réutilisable dans plusieurs règles
Comparaison des approches :
| Critère | IPSET | IP directe |
|---|---|---|
| Simplicité | ⚠️ Nécessite déclaration séparée | ✅ Tout dans la règle |
| Maintenance | ✅ Modification centralisée | ⚠️ Modification dans chaque règle |
| Lisibilité | ✅ Nom explicite (+dc/pve-vmbr0) |
⚠️ IP anonyme (192.0.2.1) |
| Réutilisation | ✅ Utilisable dans plusieurs règles | ⚠️ Répétition nécessaire |
| Configuration temporaire | ⚠️ Plus lourd | ✅ Plus rapide |
Activer les logs du pare-feu pour débugger #
En cas de doute, on peut activer les logs sur les règles du pare-feu :
[RULES]
IN ACCEPT -source +dc/pve-vmbr0 -p tcp -dport 3900 -log info
IN DROP -p tcp -dport 3900 -log info # Log les paquets refusés
Les logs apparaissent dans syslog :
journalctl -f | grep -E "REJECT|DROP|pve-fw"
On y voit l’adresse source des paquets bloqués, ce qui aide à identifier les IPSET manquants.
Dépannage Caddy #
Problème 1 : Les certificats Let’s Encrypt ne se génèrent pas #
Symptômes :
- Erreur
acme: error: 403dans les logs de Caddy - Message
could not get certificate from cacheouobtaining certificate - Le site affiche une erreur de certificat SSL dans le navigateur
Causes probables :
- DNS non configuré ou non propagé : Les enregistrements DNS ne pointent pas vers votre IP publique
- Port 80 fermé : Le pare-feu bloque le port 80 nécessaire au challenge HTTP-01
- Trop de tentatives : Let’s Encrypt limite les demandes (rate limiting)
Solutions :
# 1. Vérifier la résolution DNS
dig +short pve.example.com
# Doit retourner 198.51.100.100 (votre IP publique)
# 2. Vérifier que le port 80 est accessible depuis Internet
# Sur le nœud Proxmox
ss -tlnp | grep :80
# Doit afficher : LISTEN 0 ... *:80 ... users:(("caddy",...))
# 3. Tester l'accès au port 80 depuis l'extérieur
# Depuis un autre ordinateur
curl -I http://pve.example.com
# Doit retourner : HTTP/1.1 308 Permanent Redirect
# 4. Consulter les logs détaillés de Caddy
journalctl -u caddy -n 100 --no-pager
Vérifier les règles de pare-feu :
# Vérifier que le port 80 est autorisé
iptables -L INPUT -n -v | grep 80
# Si le port est bloqué, vérifier la configuration du pare-feu Proxmox
cat /etc/pve/firewall/cluster.fw | grep -A 5 "\[RULES\]"
Astuce : Si vous avez dépassé les limites de Let’s Encrypt (5 certificats par domaine par semaine), vous pouvez temporairement utiliser l’environnement de staging de Let’s Encrypt pour tester votre configuration :
{
email user@example.com
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
N’oubliez pas de retirer cette ligne une fois les tests terminés !
Problème 2 : Erreur 502 Bad Gateway #
Symptômes :
- Le navigateur affiche “502 Bad Gateway”
- Les logs Caddy montrent
dial tcp ... connect: connection refused
Causes probables :
- Le service backend n’est pas démarré (Garage S3, Proxmox)
- Mauvaise adresse IP ou port dans la configuration Caddy
- Pare-feu du conteneur LXC bloque la connexion
Solutions :
# 1. Vérifier que le service backend est accessible depuis le nœud Proxmox
curl -I http://192.0.2.21:3909
# Doit retourner une réponse HTTP 200 ou 30x
# 2. Tester la connexion réseau
ping 192.0.2.21
# Doit répondre
# 3. Vérifier que le port est ouvert sur le conteneur
# Depuis le nœud Proxmox
nc -zv 192.0.2.21 3909
# Doit afficher : Connection to 192.0.2.21 3909 port [tcp/*] succeeded!
# 4. Entrer dans le conteneur LXC et vérifier le service
pct enter 321
systemctl status garage # (ou le nom de votre service)
ss -tlnp | grep :3909
Vérifier le pare-feu du conteneur :
# Vérifier les règles du pare-feu du conteneur (VMID 321)
cat /etc/pve/firewall/321.fw
# S'assurer que les ports 3900 et 3909 sont bien autorisés
# depuis l'IP du bridge (192.0.2.1)
192.0.2.1 (IP du bridge), pas depuis l’IP publique !
Problème 3 : Caddy ne démarre pas #
Symptômes :
systemctl status caddyaffichefailedouinactive (dead)- Message d’erreur dans les logs :
address already in useouadapting config
Causes probables :
- Port 80 ou 443 déjà utilisé par un autre service (Apache, Nginx)
- Erreur de syntaxe dans le Caddyfile
- Permissions insuffisantes sur les fichiers de configuration
Solutions :
# 1. Vérifier quel processus utilise le port 80/443
ss -tlnp | grep ':80\|:443'
# Si un autre service utilise ces ports, l'arrêter d'abord
# 2. Valider la syntaxe du Caddyfile
caddy validate --config /etc/caddy/Caddyfile
# Doit retourner : Valid configuration
# 3. Consulter les logs d'erreur
journalctl -u caddy -n 50 --no-pager
# Chercher les messages commençant par [ERROR]
# 4. Redémarrer Caddy en mode debug pour plus d'informations
caddy run --config /etc/caddy/Caddyfile
# CTRL+C pour arrêter, puis relancer normalement si ça fonctionne
Si un autre service utilise les ports :
# Exemple : arrêter Apache ou Nginx
systemctl stop apache2
systemctl disable apache2
# OU
systemctl stop nginx
systemctl disable nginx
# Puis redémarrer Caddy
systemctl restart caddy
Problème 4 : Interface Proxmox inaccessible via Caddy #
Symptômes :
- Erreur SSL/TLS ou timeout lors de l’accès à
https://pve.example.com - Les logs Caddy montrent
tls handshake errorouEOF
Cause probable :
L’option tls_insecure_skip_verify est manquante ou mal placée dans la configuration.
Solution :
Vérifiez que votre configuration Caddy pour Proxmox contient bien :
pve.example.com {
reverse_proxy https://localhost:8006 {
transport http {
tls_insecure_skip_verify
}
}
}
Tester manuellement la connexion :
# Vérifier que Proxmox répond sur localhost:8006
curl -k https://localhost:8006
# L'option -k ignore les certificats auto-signés
# Si ça fonctionne, le problème vient de la config Caddy
caddy validate --config /etc/caddy/Caddyfile
systemctl restart caddy
Problème 5 : Les certificats ne se renouvellent pas automatiquement #
Symptômes :
- Les certificats expirent après 90 jours
- Pas de logs de renouvellement dans
journalctl -u caddy
Causes probables :
- Le service Caddy a été arrêté au moment du renouvellement
- Le port 80 était temporairement fermé lors de la tentative de renouvellement
- Problème de connectivité réseau
Solutions :
# 1. Vérifier que Caddy est bien actif
systemctl status caddy
# Doit afficher : active (running)
# 2. Vérifier que le renouvellement automatique est activé (par défaut il l'est)
# Caddy renouvelle automatiquement les certificats 30 jours avant expiration
# 3. Forcer manuellement le renouvellement (si nécessaire)
systemctl restart caddy
# Caddy vérifiera et renouvellera les certificats au démarrage si besoin
# 4. Consulter les logs pour vérifier les tentatives de renouvellement
journalctl -u caddy --since "30 days ago" | grep -i "renew\|certificate"
journalctl -u caddy -f pour vérifier que tout se passe bien.
Diagnostic général : commandes utiles #
Voici un ensemble de commandes pour diagnostiquer rapidement les problèmes Caddy :
# État du service
systemctl status caddy
# Logs en temps réel
journalctl -u caddy -f
# Derniers logs (100 lignes)
journalctl -u caddy -n 100 --no-pager
# Tester la configuration sans redémarrer
caddy validate --config /etc/caddy/Caddyfile
# Vérifier les ports ouverts
ss -tlnp | grep caddy
# Vérifier les certificats générés
ls -lah /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/
# Tester la résolution DNS
dig +short pve.example.com
# Tester l'accès HTTP depuis l'extérieur (port 80)
curl -I http://pve.example.com
# Tester l'accès HTTPS depuis l'extérieur (port 443)
curl -I https://pve.example.com
Bonnes pratiques :
- Toujours valider la configuration avec
caddy validateavant de redémarrer le service - Surveillez les logs lors des modifications :
journalctl -u caddy -f - Testez la résolution DNS avant de configurer un nouveau domaine
- Gardez le port 80 ouvert en permanence pour le renouvellement des certificats
Points à retenir #
Réseau et NAT #
-
Sur un bridge isolé, le nœud Proxmox utilise son IP de bridge, ici
192.0.2.1, pour communiquer avec les VMs/LXC, pas son IP publique. -
Cette IP sert deux fonctions : c’est à la fois l’adresse du nœud sur le réseau interne et la gateway par défaut des conteneurs.
Diagnostic et pare-feu #
-
tcpdumpetip route getsont vos amis pour diagnostiquer les problèmes de pare-feu liés aux adresses source. -
Séparez les IPSET par contexte : IP publiques d’un côté, IP internes de l’autre. Ça rend la configuration plus lisible et maintenable.
-
Le pare-feu Proxmox fonctionne à plusieurs niveaux : pensez à vérifier les règles au niveau datacenter, nœud ET VM/conteneur.
Caddy et reverse proxy #
-
Caddy simplifie drastiquement la gestion SSL : avec quelques lignes de configuration, vous obtenez des certificats Let’s Encrypt automatiques et leur renouvellement.
-
Le port 80 doit rester ouvert même si vous n’utilisez que HTTPS : Let’s Encrypt en a besoin pour valider vos domaines (challenge HTTP-01).
-
Centralisez la terminaison SSL : Caddy gère le SSL côté externe, les services backend communiquent en HTTP simple sur le réseau privé.
-
Configuration DNS préalable : assurez-vous que vos enregistrements DNS pointent vers l’IP publique du serveur avant de démarrer Caddy, sinon la validation Let’s Encrypt échouera.
Ressources complémentaires #
Documentation officielle :
- Documentation Proxmox VE - Pare-feu
- Documentation Caddy - Reverse Proxy
- Documentation Let’s Encrypt - Challenge Types
Outils de diagnostic :
- DNSChecker - Vérifier la propagation DNS mondiale
- SSL Labs - Tester la configuration SSL de vos domaines
Articles de la série “Proxmox on Scaleway” :
Conclusion #
Dans cet article, nous avons couvert plusieurs aspects essentiels :
Configuration réseau :
- Création d’un bridge isolé avec NAT pour les VMs/LXC
- Routage et masquerading pour donner accès à Internet
Reverse proxy avec Caddy :
- Installation simple et rapide
- Configuration minimale (quelques lignes)
- Génération automatique et renouvellement des certificats SSL Let’s Encrypt
- Exposition sécurisée de services internes (Garage S3, Proxmox)
Pare-feu Proxmox :
- Configuration multi-niveaux (datacenter, nœud, VM/conteneur)
- Utilisation des IPSET pour une configuration maintenable
- Diagnostic avec
tcpdumpetip route get
Le piège principal : sur un bridge isolé, Proxmox utilise son IP de bridge (192.0.2.1) pour communiquer avec les VMs/LXC, pas son IP publique. Un simple ip route get aurait révélé ce comportement immédiatement.
J’espère que ce retour d’expérience vous évitera de perdre du temps sur les mêmes pièges et vous permettra de déployer rapidement vos services avec Caddy !