Architecture WireGuard Mesh
Architecture
Les 3 serveurs forment un mesh : chacun parle directement aux deux autres. Marseille et Albi peuvent communiquent en direct, sans passer par OVH.
VPS OVH (10.0.0.1)1, ←——tunnel——→eth0)
IP publique fixe
Portainer UI · exit node OVH
/ \
tunnel tunnel
/ \
Marseille (Albi
10.0.0.2)2 |10.0.0.3
|enp4s0 |enp2s0
LAN 192.168.1.0/24 LAN 192.168.2.0/24
Portainer Agent Portainer Agent + Home Assistant/Frigate
\ /
tunnel direct P2P
|(sauvegardes |
└——tunnel——→ Albi (10.0.0.3) ←———————┘
rsync)
Les besoins fonctionnelles & techniques
Cette architecture couvre des besoins bien définit qui pourrait résoudre des problématiques de sécurité entre plusieurs serveurs et services.
Voici la liste des besoin couvert par cette architecture :
- 1 - Se connecter au VPN des différents serveur
- Hériter de l'adresse IP publique associé
- Accéder au réseau local associé au serveur
- Ne pas accéder au réseau local des AUTRES serveurs
- Par exemple, je suis connecté au VPN du serveur de Marseille. J'ai alors l'adresse IP public de la box internet de MRS, j'ai accès à tous les appareils du réseau local de la box internet. Par contre je ne peux pas voir les autres appareils du réseau local de la box d'Albi.
- 2 - Faire communiquer les différents serveurs dans un tunnel privé et sécurisé
- Chaque serveur est joignable via ce réseau privé
- Les services Docker peuvent utiliser ce réseau privé pour communiquer avec les services des autres serveurs
- Ce tunnel privé ne doit pas faire partie de la même configuration que le besoin 1
- 3 - Cette architecture doit fonctionner avec un seul et même Wireguard Docker par serveur
Les définitions
Mesh (ou réseau maillé) est une topologie réseau où chaque nœud est connecté directement à plusieurs autres, sans point central obligatoire. Dans notre cas, les 3 serveurs forment un mesh : OVH parle à Marseille, OVH parle à Albi, et Marseille parle directement à Albi. C'est l'opposé du hub-and-spoke où tout passe obligatoirement par le centre. L'avantage : si OVH tombe, Marseille et Albi continuent de se parler directement.
Exit Node est le nœud par lequel votre trafic internet "sort" vers le monde. Quand vous activez un profil exit node, vous dites à votre appareil "fais passer tout ton trafic internet par ce serveur". Le site que vous visitez voit alors l'IP publique du serveur exit node, pas votre vraie IP. C'est exactement le principe d'un VPN classique grand public (NordVPN, ExpressVPN…), sauf qu'ici vous gérez vous-même le serveur de sortie. Le terme vient de Tailscale qui l'a popularisé, mais le mécanisme (AllowedIPs = 0.0.0.0/0 + masquerading) existe depuis les débuts de WireGuard.
Structure des fichiers
wireguard-configs/
├── servers/
│ ├── ovh-wg0.conf # Config WireGuard du VPS OVH
│ ├── marseille-wg0.conf # Config WireGuard de Marseille
│ └── albi-wg0.conf # Config WireGuard d'Albi
├── clients/
│ ├── ovh-exitnode.conf # Client : IP publique = OVH
│ ├── mrs-exitnode.conf # Client : IP publique = Marseille
│ ├── albi-exitnode.conf # Client : IP publique = Albi
│ ├── mrs-lan.conf # Client : accès LAN Marseille (split tunnel)
│ ├── albi-lan.conf # Client : accès LAN Albi (split tunnel)
│ └── private-only.conf # Client : réseau privé uniquement
├── docker-compose-all.yml # docker-compose OVH + Marseille/Albi
└── README.md # Ce fichier
Plan d'adressage complet
Matrice de sécurité
| Profil client | IP mesh | Internet | Mesh 10.0.0.x | LAN MRS 192.168.1.x | LAN Albi 192.168.2.x |
|---|---|---|---|---|---|
| ovh-exitnode | 10.0.0.10 | IP OVH | ✓ | ✗ | ✗ |
| mrs-exitnode | 10.0.0.20 | IP MRS | ✓ | ✓ | ✗ |
| albi-exitnode | 10.0.0.30 | IP Albi | ✓ | ✗ | ✓ |
| private-only | 10.0.0.40 | propre | ✓ | ✗ | ✗ |
| Serveur OVH | 10.0.0.1 | — | ✓ | ✗ | ✗ |
| Serveur MRS | 10.0.0.2 | — | ✓ | — | ✗ |
| Serveur Albi | 10.0.0.3 | — | ✓ | ✗ | — |
Double protection :
- (1) les LAN ne sont dans les AllowedIPs d'aucun peer serveur → WireGuard refuse de les router ;
- (2) UFW pose FORWARD = DROP et n'autorise le LAN qu'à l'IP exit-node du site concerné.
Serveurs (peers fixes)
| Serveur | IP WireGuard | IP publique | LAN local | Commentaire |
|---|---|---|---|---|
| VPS OVH | 10.0.0.1 | <IP_PUBLIQUE_OVH> | — | — |
| Marseille | 10.0.0.2 | <IP_PUBLIQUE_MRS> | 192.168.1.0/24 | — |
| Albi | 10.0.0.3 | <IP_PUBLIQUE_ALBI> | 192.168.2.0/24 | — |
Clients (plagesIP réservées)
| Usage | Profil | Commentaire | |
|---|---|---|---|
| 10.0.0. |
Exit node OVH | ovh-exitnode.conf | IP publique = OVH |
| 10.0.0. |
Exit node |
mrs-exitnode |
IP publique = Marseille + LAN |
| 10.0.0. |
Exit node |
albi-exitnode |
IP publique = Albi + LAN |
| 10.0.0. |
Réseau privé OVH | private-only.conf | Réseau privé seul (Mesh), |
| 10.0.0.41 | Réseau privé MRS | private-only.conf | Réseau privé seul (Mesh), entrée par MRS |
| 10.0.0.42 | Réseau privé Albi | private-only.conf | Réseau privé seul (Mesh), entrée par Albi |
Ordre de déploiement
Étape 1 — Générer toutes les clés
Sur chaque serveur, générer une paire de clés :
# Sur OVH
wg genkey | tee /etc/wireguard/privatekey-ovh | wg pubkey > /etc/wireguard/publickey-ovh
# Sur Marseille
wg genkey | tee /etc/wireguard/privatekey-mrs | wg pubkey > /etc/wireguard/publickey-mrs
# Sur Albi
wg genkey | tee /etc/wireguard/privatekey-albi | wg pubkey > /etc/wireguard/publickey-albi
# Pour chaque client
wg genkey | tee privatekey-client | wg pubkey > publickey-client
Étape 2 — Remplacer les placeholders
Dans tous les fichiers .conf, remplacer :
<CLE_PRIVEE_OVH>→ contenu de privatekey-ovh<CLE_PUBLIQUE_OVH>→ contenu de publickey-ovh<CLE_PRIVEE_MRS>→ contenu de privatekey-mrs<CLE_PUBLIQUE_MRS>→ contenu de publickey-mrs<CLE_PRIVEE_ALBI>→ contenu de privatekey-albi<CLE_PUBLIQUE_ALBI>→ contenu de publickey-albi<IP_PUBLIQUE_OVH>→ IP publique du VPS OVH<IP_PUBLIQUE_MRS>→ IP publique de la box Marseille<IP_PUBLIQUE_ALBI>→ IP publique de la box Albi- (idem pour les clés clients)
Étape 3 — Configuration des box internet
Box Marseille & Box Albi
Effectuer dans l'interface admin de chaque box :
Port forwarding :
Protocole : UDP
Port externe : 51820
IP destination : 192.168.X.100 (IP locale du serveur)
Port destination : 51820
Route statique :
Destination : 10.0.0.0
Masque : 255.255.255.0 (ou /24)
Passerelle : 192.168.X.100Étape 4 — Déployer dans l'ordre
# 1. Démarrer le hub OVH en premier
ssh ovh "cd /opt/wireguard && docker compose up -d"
# 2. Démarrer Marseille
ssh marseille "cd /opt/wireguard && docker compose up -d"
# 3. Démarrer Albi
ssh albi "cd /opt/wireguard && docker compose up -d"
# Une fois wg0 créé, appliquer le pare-feu :
sudo bash ovh-ufw-setup.sh # sur OVH
sudo bash marseille-ufw-setup.sh # sur Marseille
sudo bash albi-ufw-setup.sh # sur Albi
Étape 5 — Vérification de sécurité
Note Docker (importante pour Home Assistant)
Après ufw enable, si les conteneurs (Home Assistant, Frigate…) perdent l'accès réseau, redémarrer Docker une seule fois pour qu'il replace ses règles FORWARD au-dessus de celles d'UFW :
sudo systemctl restart docker
À faire à un moment calme (cela redémarre HA et Frigate). En pratique HA en host n'a pas besoin de la chaîne FORWARD, donc ce redémarrage est rarement nécessaire — mais c'est le réflexe si un conteneur perd le réseau.
Vérifier les règles iptables activesPar-feu
#ufw Surstatus chaqueverbose
serveurufw aprèsroute démarrage de WireGuard
docker exec wireguard-ovh iptables -L FORWARD -n -vstatus # Doit afficher : policy DROP +liste les règles explicitesFORWARD docker exec wireguard-mrsVPN
iptables -t nat -L FORWARDPOSTROUTING -n -v| dockergrep exec wireguard-albi iptables -L FORWARD -n -vMASQUERADE
Vérifier que Portainer n'est PAS exposé sur l'IP publique
# Depuis internet (doit échouer — connexion refusée ou timeout)
curl -v --max-time 5 http://<IP_PUBLIQUE_OVH>:9000
# Depuis le VPN avec private-only actif (doit fonctionner)
curl -v http://10.0.0.1:9000
Tester le cloisonnement LAN
# Avec private-only.conf actif, tenter d'atteindre le LAN de Marseille
ping 192.168.1.1 # doit échouer (pas de route + iptables DROP)
ping 192.168.2.1 # doit échouer
# Avec mrs-exitnode.conf actif
ping 192.168.1.1 # doit répondre (autorisé)
ping 192.168.2.1 # doit échouer (bloqué)
# Avec albi-exitnode.conf actif
ping 192.168.1.1 # doit échouer
ping 192.168.2.1 # doit répondre (autorisé)
Vérifier l'état du mesh
# Sur OVH — doit lister MRS et Albi avec handshake récent
docker exec wireguard-ovh wg show
# Exemple de sortie attendue :
# peer: <CLE_PUBLIQUE_MRS>
# endpoint: <IP_MRS>:51820
# allowed ips: 10.0.0.2/32
# latest handshake: X seconds ago
# transfer: X MiB received, X MiB sent
Vérifier les sauvegardes rsync (tunnel direct P2P)
# Depuis Marseille vers Albi — ne doit PAS faire monter les compteurs OVH
docker exec wireguard-mrs rsync -avz /data/ user@10.0.0.3:/backup/
# Pendant le rsync, vérifier sur OVH que les bytes N'augmentent PAS :
watch -n1 "docker exec wireguard-ovh wg show | grep -A4 'marseille\|albi'"
Étape 6 — Configurer Portainer UI
- Ouvrir http://10.0.0.1:9000 avec le profil private-only.conf actif
- Aller dans Environments > Add environment > Agent
- Ajouter Marseille : URL =
10.0.0.2:9001, nom = "Marseille" - Ajouter Albi : URL =
10.0.0.3:9001, nom = "Albi"
Commandes utiles
Vérifier l'état des tunnels
# Sur n'importe quel serveur
docker exec wireguard-ovh wg show # OVH
docker exec wireguard-mrs wg show # Marseille
docker exec wireguard-albi wg show # Albi
# Résultat attendu : chaque peer doit avoir un "latest handshake" récent
# et un compteur de bytes "received/sent" qui monte
Tester la connectivité mesh
# Depuis OVH, pinger Marseille et Albi
docker exec wireguard-ovh ping 10.0.0.2 -c 3
docker exec wireguard-ovh ping 10.0.0.3 -c 3
# Depuis Marseille, pinger Albi directement (tunnel P2P)
docker exec wireguard-mrs ping 10.0.0.3 -c 3
# Vérifier que le LAN de Marseille est accessible depuis Albi
docker exec wireguard-albi ping 192.168.1.1 -c 3
Vérifier que Portainer UI n'est pas exposé
# Cette commande doit échouer (connexion refusée) depuis internet
curl -v http://<IP_PUBLIQUE_OVH>:9000
# Ceci doit fonctionner uniquement avec le VPN actif
curl -v http://10.0.0.1:9000
Tester une sauvegarde rsync inter-serveurs
# Depuis Marseille vers Albi (tunnel P2P direct)
docker run --rm --network host \
-v /data/backups:/source:ro \
instrumentisto/rsync-ssh \
rsync -avz --progress /source/ user@10.0.0.3:/backup/marseille/
# Vérifier que le trafic ne passe PAS par OVH :
# Sur OVH pendant le rsync : docker exec wireguard-ovh wg show
# → les bytes du peer MRS et Albi ne doivent PAS augmenter
Générer un QR code pour mobile (iOS/Android)
# Installer qrencode
apt install qrencode
# Générer le QR pour le profil OVH exit node
qrencode -t ansiutf8 < clients/ovh-exitnode.conf
# Scanner avec l'app WireGuard sur le téléphone
Redémarrer un tunnel après modification de config
docker compose restart wireguard
# OU
docker exec wireguard-ovh wg syncconf wg0 <(wg-quick strip wg0)
Sécurité — points d'attention
- Ne jamais committer les clés privées dans Git. Ajouter
*privatekey*au.gitignore. - Portainer UI bind sur 10.0.0.1 uniquement — vérifier avec
ss -tlnp | grep 9000. - Portainer Agent bind sur 10.0.0.x uniquement — même vérification sur chaque serveur.
- Les fichiers .conf clients contiennent des clés privées — les traiter comme des mots de passe.
- Renouveler les clés si un appareil client est perdu ou compromis : supprimer son [Peer] de tous les serveurs et faire
docker compose restart wireguard. - Caveat UFW + Docker : Docker contourne UFW pour les ports qu'il publie. Ici Portainer est bindé sur 10.0.0.x → déjà limité au mesh. Pour tout futur conteneur, ne pas supposer qu'UFW protège un
ports: "8080:8080".