Accès distant WireGuard aux conteneurs Docker
|
Important
|
Traduction d’un article du site Pro Custodibus Le contenu de cette page est la traduction Française de l’article WireGuard Remote Access to Docker Containers de Justin Ludwig |
Accès à distance aux conteneurs Docker avec WireGuard
Utiliser WireGuard pour accéder aux services en cours d’exécution dans un ensemble de conteneurs sur un hôte distant avec une Topologie Point à Point n’est pas beaucoup différent de l’utilisation de WireGuard pour accéder aux services sur le même hôte en dehors de tout conteneur. Cet article vous montrera comment.
Chaque scénario que nous couvrons fonctionnera de manière similaire au guide Configuration Point à Point , où nous connectons un point de terminaison, “Point de terminaison A”, à un deuxième point de terminaison, “Point de terminaison B”, via WireGuard, permettant au Point de terminaison A d’accéder à un serveur web en cours d’exécution sur le Point de terminaison B. Dans cet article, le serveur web sur le Point de terminaison B sera en cours d’exécution dans un conteneur pas exposé via le réseau de l’hôte.
Si les conteneurs sur le Point de terminaison B étaient en mode de réseau host (c’est-à-dire en utilisant le drapeau --network=host avec docker run, ou la configuration network_mode: host avec docker-compose), ou si leurs services réseau étaient exposés au réseau de l’hôte (en utilisant le drapeau --publish avec docker run, ou la configuration ports avec docker-compose), nous pourrions en fait utiliser simplement le guide Configuration Point à Point au lieu de cet article. Dans ce cas, ces services réseau sont accessibles directement dans l’espace de noms du réseau de l’hôte, comme si ils étaient en cours d’exécution sur l’hôte en dehors de tout conteneur.
Mais au lieu d’utiliser le mode réseau du hôte, dans ce guide nous utiliserons un réseau de pont défini par l’utilisateur pour accéder aux conteneurs sur le Point de terminaison B. C’est la meilleure pratique avec les conteneurs, car elle offre une meilleure sécurité via l’isolation réseau : par défaut, seuls les conteneurs partageant le même réseau de pont peuvent accéder aux services réseau des autres. Ce guide vous montrera comment ajuster ces defaults sécurisées pour permettre un accès limité externe à travers WireGuard dans trois scénarios :
WireGuard sur le Hôte
La façon la plus simple d’activer l’accès d’un hôte à des conteneurs isolés sur un autre est de mettre en place WireGuard sur l’hôte du conteneur. Tout d’abord, configurez un réseau de pont défini par l’utilisateur sur l’hôte du conteneur (Point de terminaison B), et connectez-y les conteneurs que vous souhaitez exposer au hôte distant.
Avec les outils en ligne de commande Docker (CLI), créez un nouveau réseau comme celui-ci, avec une sous-réseau privée que vous ne utilisez pas déjà (comme 192.168.123.0/24 dans cet exemple) et nom du réseau (wg-network) :
$ sudo docker network create \
--opt com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
--subnet 192.168.123.0/24 \
wg-network
Mise à jour le 14 juillet 2025
Docker Engine v28 (sorti en début de 2025) a modifié les règles de pare-feu par défaut qu’il configure pour les conteneurs Docker ; maintenant, un paramètre supplémentaire de configuration Docker est requis pour l’accès distant via l’interface.
e WireGuard du hôte du conteneur. L’option nat-unprotected mode passerelle montrée ci-dessus fait cela (lorsqu’elle est utilisée en conjonction avec la règle iptables DOCKER-USER montrée plus bas).
D’autres alternatives comprennent l’utilisation de l’option com.docker.network.bridge.trusted_host_interfaces lors de la création du réseau (en spécifiant le nom de l’interface WireGuard pour la valeur de l’option) ; ou le retour global à l’ancien comportement en ajoutant l’option allow-direct-routing au fichier de configuration /etc/docker/daemon.json du hôte du conteneur.
Ensuite, démarrez les conteneurs que vous souhaitez exposer, en spécifiant le nom du réseau et une adresse IP disponible dans ce réseau pour chaque. Notez que la première adresse IP du sous-réseau (192.168.123.0) est réservée au sous-réseau lui-même, et l’adresse IP suivante (192.168.123.1) Docker utilisera par défaut comme passerelle pour le réseau — donc nous utiliserons l’adresse IP suivante disponible dans le sous-réseau (192.168.123.2) pour notre conteneur d’exemple :
$ sudo docker run \
--name example-web-server \
--network wg-network \
--ip 192.168.123.2 \
--rm \
nginx
Si vous avez déjà démarré le conteneur sans le connecter au réseau, vous pouvez le connecter plus tard avec la commande suivante :
$ sudo docker network connect \
--ip 192.168.123.2 \
wg-network \
example-web-server
Assurez-vous de spécifier une adresse IP explicite pour chaque conteneur (au lieu de laisser Docker choisir), car vous aurez besoin d’utiliser l’adresse IP du conteneur pour accéder aux services réseau exposés par le conteneur.
Alternativement, vous pouvez utiliser Docker Compose pour configurer le réseau et les conteneurs. Par exemple, en utilisant la syntaxe de Docker Compose version 3.5+, vous pouvez créer un réseau wg-network similaire au-dessus et connecter un conteneur example-web-server similaire à celui-ci :
# /srv/wg-network/docker-compose.yml
version: '3.5'
networks:
wg-network:
driver_opts:
com.docker.network.bridge.gateway_mode_ipv4: nat-unprotected
ipam:
config:
- subnet: 192.168.123.0/24
services:
example-web-server:
image: nginx
networks:
wg-network:
ipv4_address: 192.168.123.2
Démarrez le réseau et le conteneur en exécutant sudo docker-compose up à partir du même répertoire que le fichier docker-compose.yml.
Vous pouvez accéder au conteneur exemple en exécutant la commande suivante sur l’hôte du conteneur (Point de terminaison B) :
$ curl 192.168.123.2:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Le conteneur ne sera pas accessible à l’extérieur de l’hôte du conteneur, cependant. Ainsi, configurons le réseau WireGuard qui permettra d’accéder au conteneur depuis l’hôte distant, Point de terminaison A.
D’abord, sur Point de terminaison A, créez le fichier de configuration WireGuard suivant à /etc/wireguard/wg0.conf :
# /etc/wireguard/wg0.conf
# paramètres locaux pour Point de terminaison A
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
ListenPort = 51821
# paramètres distants pour Point de terminaison B
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51822
AllowedIPs = 10.0.0.2/32, 192.168.123.0/24
Remplacez 203.0.113.2 dans le paramètre Endpoint pour le Point de terminaison B par l’adresse IP réelle du Point de terminaison B à partir du point de vue du Point de terminaison A. Générez également vos propres paires de clés pour le Point de terminaison A et B, et utilisez-les à la place. Consultez le guide Configuration Point à Point.
nt-to-point-config/) pour plus de détails.
La seule différence entre la configuration WireGuard pour le Point de terminaison A ici et dans le guide Configuration Point à Point est que nous incluons également le sous-réseau du réseau Docker que nous venons de configurer, 192.168.123.0/24, comme un paramètre AllowedIPs pour la connexion du Point de terminaison A au Point de terminaison B.
Ensuite, sur le Point de terminaison B, créez le fichier de configuration WireGuard suivant à /etc/wireguard/wg0.conf (en utilisant vos propres clés pour le Point de terminaison A et B pour correspondre à votre configuration pour le Point de terminaison A) :
# /etc/wireguard/wg0.conf
# paramètres locaux pour le Point de terminaison B
[Interface]
PrivateKey = ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
Address = 10.0.0.2/32
ListenPort = 51822
PreUp = iptables -N DOCKER-USER || true
PreUp = iptables -I DOCKER-USER -i wg0 -d 192.168.123.0/24 -j ACCEPT
PostDown = iptables -D DOCKER-USER -i wg0 -d 192.168.123.0/24 -j ACCEPT
# paramètres distants pour le Point de terminaison A
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
AllowedIPs = 10.0.0.1/32
La seule différence entre cela et le guide Configuration Point à Point pour le Point de terminaison B est que nous configurons également une règle iptables pour permettre l’accès à notre réseau Docker personnalisé, 192.168.123.0/24, depuis cette interface WireGuard (wg0) :
iptables -I DOCKER-USER -i wg0 -d 192.168.123.0/24 -j ACCEPT
Docker va généralement configurer la chaîne DOCKER-USER pour nous ; mais au démarrage du système, elle n’a peut-être pas encore fait cela, donc le premier PreUp de la configuration WireGuard pour le Point de terminaison B s’assure que la chaîne DOCKER-USER existe avant que le deuxième PreUp ne l’ajoute à une règle. Notez que nous utilisons également le drapeau -I pour cette règle plutôt que le drapeau -A, afin que la règle soit insérée en haut de la chaîne DOCKER-USER, dans le cas où Docker aurait déjà créé la chaîne et ajouté sa règle par défaut.
Démarrez ces nouvelles interfaces WireGuard sur le Point de terminaison A et B (par exemple, sudo wg-quick up wg0), et vous pourrez accéder au conteneur d’exemple en exécutant la commande suivante sur l’hôte distant (Point de terminaison A) :
$ curl 192.168.123.2:80
<!DOCTYPE html>
<html>
<head>
<title>Bienvenue sur nginx!</title>
...
WireGuard dans un Conteneur
Si vous préférez exécuter WireGuard à l’intérieur d’un conteneur sur le Point de terminaison B, au lieu de l’exécuter en dehors de l’hôte, vous pouvez le faire aussi bien. Lorsque vous exécutez WireGuard dans un conteneur, la méthode la plus simple pour permettre un accès à distance aux autres conteneurs est simplement de connecter les autres conteneurs directement au namespace réseau du conteneur WireGuard (par exemple, en utilisant le drapeau --network container:wg-server avec docker run, ou l’option network_mode: 'service:wg-server' avec docker-compose).
Cette approche est couverte par la section Utilisation pour le réseau de conteneur du guide Construction, utilisation et surveillance des conteneurs WireGuard. Avec cette approche, la configuration de WireGuard pour le Point de terminaison A et B à l’intérieur du conteneur WireGuard sont exactement les mêmes que la configuration dans la section Configuration point à point.
Mais dans ce guide, nous couvrirons une approche légèrement plus complexe, mais plus flexible. Comme la section précédente [WireGuard sur le H
Ensuite, nous allons configurer les conteneurs. Avec les outils de ligne de commande Docker (CLI), créez un nouveau réseau comme suit, avec un sous-réseau privé que vous ne utilisez pas déjà (comme 192.168.123.0/24 dans cet exemple) et un nom de réseau (wg-network) :
$ sudo docker network create \
--subnet 192.168.123.0/24 \
wg-network
Ensuite, démarrez le conteneur WireGuard ainsi que les conteneurs que vous souhaitez exposer via WireGuard. Pour chaque conteneur, spécifiez le nom de réseau que nous venons de créer et une adresse IP disponible dans le réseau :
$ sudo docker run \
--cap-add NET_ADMIN \
--name wg-server \
--network wg-network \
--ip 192.168.123.123 \
--publish 51822:51822/udp \
--rm \
--volume /srv/wg-network/wg-server:/etc/wireguard \
procustodibus/wireguard
$ sudo docker run \
--name example-web-server \
--network wg-network \
--ip 192.168.123.2 \
--rm \
nginx
Nous utilisons l’image de base WireGuard Pro Custodibus image WireGuard pour notre conteneur WireGuard, qui démarre automatiquement une interface WireGuard dans le conteneur pour toutes les fichiers de configuration WireGuard qu’il trouve dans le répertoire /etc/wireguard du conteneur (et dans l’exemple ci-dessus, nous avons mappé /srv/wg-network/wg-server sur l’hôte, où nous avons placé le fichier wg0.conf, vers /etc/wireguard dans le conteneur). Nous exposons le port UDP 51822 de ce conteneur afin que l’hôte distant (Point de terminaison A) puisse se connecter à celui-ci.
L’ordre dans lequel vous démarrez les conteneurs n’a pas d’importance. Si vous aviez déjà démarré un conteneur que vous souhaitez exposer via WireGuard, vous pouvez le connecter avec la commande suivante :
$ sudo docker network connect \
--ip 192.168.123.2 \
wg-network \
example-web-server
Alternativement, vous pouvez utiliser Docker Compose pour configurer le réseau et les conteneurs. Par exemple, en utilisant la syntaxe de Docker Compose version 3 :
version: '3'
services:
wg-server:
image: procustodibus/wireguard
cap_add:
- NET_ADMIN
network_mode: "bridge"
networks:
- wg-network
volumes:
- /srv/wg-network/wg-server:/etc/wireguard
ports:
- "51822:51822/udp"
example-web-server:
image: nginx
network_mode: "bridge"
networks:
- wg-network
ip_address: 192.168.123.2
networks:
wg-network:
driver: bridge
ipam:
config:
- subnet: 192.168.123.0/24
Enregistrez ce fichier sous docker-compose.yml et exécutez-le avec la commande suivante :
$ docker-compose up -d
Cela créera le réseau wg-network, démarrera les conteneurs wg-server et example-web-server, et les connectera au réseau.
Voici le texte corrigé :
Sur Endpoint A (l’hôte distant), créez le fichier de configuration WireGuard suivant à /etc/wireguard/wg0.conf :
# /etc/wireguard/wg0.conf
# paramètres locaux pour Endpoint A
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
ListenPort = 51821
# paramètres distants pour Endpoint B
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51822
AllowedIPs = 10.0.0.2/32
AllowedIPs = 192.168.123.0/24
Notez que cette configuration est exactement la même que celle que nous avons utilisée pour le scénario WireGuard sur l’Hôte ci-dessus. Comme dans ce scénario, assurez-vous de remplacer 203.0.113.2 dans le paramètre Endpoint par l’adresse IP réelle d’Endpoint B (du point de vue d’Endpoint A), et utilisez vos propres paires de clés pour Endpoint A et B. Consultez la Configuration Point à Point pour plus de détails sur ces paramètres.
La seule chose “spéciale” de cette configuration est que, comme dans le scénario WireGuard sur l’Hôte ci-dessus, nous incluons le sous-réseau du réseau Docker que nous venons de configurer, 192.168.123.0/24, en tant qu’option AllowedIPs.
Démarrage de l’interface WireGuard sur le Point de terminaison A (par exemple sudo wg-quick up wg0), et vous pourrez accéder au conteneur d’exemple en exécutant la commande suivante sur le Point de terminaison A :
$ curl 192.168.123.2:80
<!DOCTYPE html>
<html>
<head>
<title>Bienvenue sur nginx!</title>
...
WireGuard dans un Conteneur Sans Masquage
La seule inconvénient du scénario WireGuard dans un Conteneur ci-dessus est que la règle iptables que nous devions ajouter au conteneur WireGuard réécrit l’adresse IP source des paquets qu’il transmet (pour utiliser l’adresse IP propre du conteneur, 192.168.123.123). Les autres conteneurs à lesquels il transmet des paquets verront cette adresse au lieu des adresses sources originales des paquets. Si vous souhaitez maintenir l’adresse source originale pour des raisons de contrôle d’accès ou de journalisation dans les conteneurs, vous pouvez le faire — avec un peu de travail supplémentaire.
Le travail supplémentaire consiste à ajouter manuellement une route (ou plusieurs routes) au namespace réseau de chaque conteneur auquel le conteneur WireGuard transmet des paquets. Dans le scénario ci-dessus, nous avons attribué une adresse IP du conteneur WireGuard 192.168.123.123, et la configuration WireGuard du conteneur permet uniquement les paquets provenant de 10.0.0.1/32 (Point de terminaison A). Donc, pour ce scénario, c’est cette seule route que nous devrions ajouter à chaque (non-WireGuard) conteneur :
ip route add 10.0.0.1/32 via 192.168.123.123
Si la configuration WireGuard du conteneur WireGuard contenait plusieurs blocs d’adresses, vous devriez ajouter une route pour chaque bloc.
J’ai corrigé les erreurs grammaticales et stylistiques tout en conservant la structure Markdown originale.
AllowedIPs`, nous devrions ajouter une route pour chaque bloc. Par exemple, si la configuration contenait les éléments suivants :
...
AllowedIPs = 10.0.0.1/32
...
AllowedIPs = 10.1.2.3/32, 10.1.2.99/32
...
AllowedIPs = 192.168.234.0/24
...
Nous devrions ajouter quatre routes, une pour chaque bloc d’adresse :
ip route add 10.0.0.1/32 via 192.168.123.123
ip route add 10.1.2.3/32 via 192.168.123.123
ip route add 10.1.2.99/32 via 192.168.123.123
ip route add 192.168.234.0/24 via 192.168.123.123
(Même si vous savez que vous ne casserez pas l’accès du conteneur aux autres services en combinant certains blocs, vous pouvez le faire — comme pour combiner 10.1.2.3/32 et 10.1.2.99/32 en 10.1.2.0/24 pour l’exemple ci-dessus.)
Cependant, pour ajouter des routes au conteneur, vous devez exécuter la commande ip route add soit a) depuis l’hôte à l’aide de l’espace de noms du conteneur, soit b) à l’intérieur du conteneur lui-même.
Ajouter la Route à partir de l’Hôte
Pour ajouter la route à partir de l’hôte à l’aide de l’espace de noms du conteneur, commencez d’abord par démarrer le conteneur :
$ sudo docker run \
--name example-web-server \
--network wg-network \
--ip 192.168.123.2 \
--rm \
nginx
Ensuite, identifiez l’PID (identifiant de processus) du conteneur en utilisant le nom du conteneur (example-web-server) :
$ sudo docker container inspect example-web-server -f '{{.State.Pid}}'
12345
Finalement, utilisez l’outil nsenter avec ce PID pour exécuter la commande ip route add :
$ sudo nsenter -t 12345 -n ip route add 10.0.0.1/32 via 192.168.123.123
Notez que si vous arrêtez le conteneur et le redémarrez, vous devrez rechercher le nouveau PID du conteneur et ajouter la route à nouveau, en utilisant le nouveau PID.
Une fois que vous avez ajouté le routage, vous pouvez utiliser les mêmes commandes et la même configuration du scénario WireGuard dans un conteneur ci-dessus — simplement sans la règle iptables dans le conteneur WireGuard (c’est-à-dire en omettant l’option PreUp dans la configuration de WireGuard pour le Point de terminaison B). Tout fonctionnera comme avant, à l’exception des autres conteneurs qui verront l’adresse source originale sur les paquets transférés par le conteneur WireGuard, au lieu de l’adresse IP propre du conteneur WireGuard.
Comme dans le scénario WireGuard dans un conteneur, vous pourrez accéder au conteneur d’exemple en exécutant la commande suivante sur le Point de terminaison A :
$ curl 192.168.123.2:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
Ajouter le Routage à partir du Conteneur
Pour ajouter le routage à l’intérieur même du conteneur, vous devez d’abord installer le package iproute2 dans le conteneur (ou, idéalement, l’image du conteneur), vous assurer que vous démarrez le conteneur avec la capacité NET_ADMIN, et ensuite exécuter la commande ip route add à partir du conteneur. La façon dont vous faites cela exactement dépend de l’image du conteneur, mais généralement vous devez :
- Créer une image personnalisée avec un Dockerfile personnalisé dans lequel vous installez le package
iproute2; - Exécuter l’image avec un script de commande personnalisé qui commence par exécuter la commande
ip route add, et ensuite exécute la commande par défaut de l’image de base de l’image personnalisée.
Pour notre exemple de serveur web, nous créons un Dockerfile personnalisé (placé à /srv/wg-network/example-web-server/Dockerfile) comme suit, qui surcharge l’image de base nginx pour installer le package iproute2 :
# /srv/wg-network/example-web-server/Dockerfile
FROM nginx
RUN apt-get update && apt-get install -y iproute2
Ensuite, nous créons un script de commande personnalisé (placé à /srv/wg-network/example-web-server/command.sh) comme suit :
#!/bin/bash
ip route add 10.0.0.1/32 via 192.168.123.123
exec nginx -g 'daemon off;'
Assurez-vous que le script est exécutable :
$ chmod +x /srv/wg-network/example-web-server/command.sh
Enfin, démarrez le conteneur avec cette image et ce script de commande personnalisé :
$ sudo docker run \
--name example-web-server \
--network wg-network \
--ip 192.168.123.2 \
--rm \
-v /srv/wg-network/example-web-server/command.sh:/usr/local/bin/start.sh \
-e CMD=start.sh \
nginx
Avec cette approche, la route sera ajoutée automatiquement chaque fois que le conteneur démarre.
#!/bin/sh -e
# (/srv/wg-network/example-web-server/command.sh)
ip route add 10.0.0.1/32 via 192.168.123.123
nginx -g 'daemon off;'
Nous construirions l’image personnalisée comme suit, en lui donnant une étiquette de custom-nginx :
docker build \
--tag custom-nginx \
/srv/wg-network/example-web-server
Après avoir configuré notre réseau d’interface utilisateur défini wg-network et démarré le conteneur WireGuard comme dans le scénario WireGuard dans un Conteneur, nous exécuterions notre image personnalisée avec notre commande personnalisée (et en lui accordant également la capacité NET_ADMIN) :
$ sudo docker run \
--cap-add NET_ADMIN \
--name example-web-server \
--network wg-network \
--ip 192.168.123.2 \
--rm \
--volume /srv/wg-network/example-web-server:/custom \
custom-nginx \
/custom/command.sh
Alternativement, nous pourrions construire notre image personnalisée et l’exécuter avec notre commande personnalisée via un fichier docker-compose.yml comme suit :
# /srv/wg-network/docker-compose.yml
version: '3.5'
networks:
wg-network:
ipam:
config:
- subnet: 192.168.123.0/24
services:
wg-server:
image: procustodibus/wireguard
cap_add:
- NET_ADMIN
networks:
wg-network:
ipv4_address: 192.168.123.123
ports:
- 51822:51822/udp
volumes:
- ./wg-server:/etc/wireguard
example-web-server:
build: example-web-server
cap_add:
- NET_ADMIN
command: /custom/command.sh
networks:
wg-network:
ipv4_address: 192.168.123.2
volumes:
- ./example-web-server:/custom
Avec le chemin ajouté, la même configuration WireGuard du scénario WireGuard dans un conteneur ci-dessus fonctionnera — sans avoir besoin de la règle iptables dans le conteneur WireGuard (c’est-à-dire omettre l’option PreUp dans la configuration WireGuard pour le Point de terminaison B). Notre exemple de conteneur verra l’adresse source originale des paquets transférés depuis le conteneur WireGuard, au lieu de l’adresse IP du conteneur WireGuard (et sera en mesure de transmettre les réponses correctement).
Comme dans le scénario WireGuard dans un conteneur, vous pouvez accéder à l’exemple de conteneur en exécutant la commande suivante sur le Point de terminaison A :
$ curl 192.168.123.2:80
<!DOCTYPE html>
<html>
<head>
<title>Bienvenue sur nginx!</title>
...
Mise à jour : 7/14/2025
par Justin Ludwig
by Justin Ludwig translated by: Patrice Le Guyader
