WireGuard : accéder à son LAN domestique
Le handshake WireGuard fonctionne, mais 192.168.20.10 ne répond pas ? Vérifiez les 4 réglages qui déterminent l’aller et le retour des paquets.
Pourquoi votre LAN domestique ne répond pas via WireGuard
Pour accéder à votre LAN domestique via WireGuard, 4 paramètres distincts doivent être cohérents. Avec 3 paramètres corrects sur 4, le tunnel semble parfaitement opérationnel, ce qui rend cette panne particulièrement déroutante. wg show indique un handshake récent, ping 10.8.0.1 répond en quelques millisecondes et ping 192.168.20.10 ne renvoie absolument rien.
Voici la liste complète, dans l’ordre dans lequel un paquet la rencontre. LAN signifie réseau local, c’est-à-dire le réseau privé situé derrière votre routeur domestique.
AllowedIPssur le client doit couvrir le sous-réseau distant, sinon le paquet n’entre jamais dans le tunnel.AllowedIPssur le serveur doit couvrir l’adresse du tunnel du client, sinon le paquet est abandonné dès son déchiffrement.net.ipv4.ip_forwarddoit être1sur le serveur, car Linux abandonne tout paquet qui n’est pas destiné à la machine elle-même.- Le LAN doit connaître une route de retour vers
10.8.0.0/24, via une règle de masquerading sur le serveur ou une route statique sur le routeur domestique.
Chacun de ces paramètres peut être incorrect sans générer de message d’erreur. Rien n’est journalisé, aucun avertissement n’est affiché et le handshake continue de fonctionner. Vérifiez-les dans l’ordre : le paramètre incorrect est généralement identifié en environ une minute.
Le réseau utilisé dans ce guide
Toutes les adresses ci-dessous sont des exemples. Remplacez-les par les vôtres et utilisez les mêmes valeurs partout. Une configuration partiellement mise à jour est la deuxième cause la plus fréquente de ce problème.
- Le LAN domestique est
192.168.20.0/24. Le routeur domestique est192.168.20.1. - Le serveur WireGuard est une machine Linux présente sur ce LAN. Son interface LAN
enp1s0porte192.168.20.5et son interface tunnelwg0porte10.8.0.1. - L’hôte auquel vous voulez accéder est un NAS (stockage en réseau) à l’adresse
192.168.20.10. - Le client est un ordinateur portable situé ailleurs, avec l’adresse
10.8.0.2dans le tunnel.
Le serveur est une machine du LAN, et non le routeur lui-même. C’est le cas habituel, par exemple avec un Raspberry Pi ou un ancien mini-PC. Cela compte pour la règle 4 : votre routeur ne sait pas que le tunnel existe tant que vous ne le lui indiquez pas, et chaque hôte du LAN envoie le trafic destiné à un autre sous-réseau vers ce routeur.
Si votre connexion domestique ne dispose pas d’une adresse IP publique, rien de tout cela ne fonctionne seul, car aucune machine sur Internet ne peut établir de handshake vers votre domicile. La section consacrée à l’utilisation d’un VPS intermédiaire couvre ce cas. Les quatre mêmes règles s’appliquent alors, avec un peer supplémentaire à prendre en compte.
AllowedIPs désigne deux choses différentes
Un même paramètre remplit deux fonctions. Le lire de la même manière aux deux extrémités est à l’origine de la plupart de ces tickets. WireGuard appelle ce mécanisme le routage par clés cryptographiques, décrit plus en détail dans la manière dont WireGuard associe les clés publiques aux plages d’adresses IP.
En sortie, AllowedIPs est une table de routage. wg-quick transforme chaque entrée en route vers wg0. Un paquet destiné à 192.168.20.10 n’est chiffré et envoyé à un pair que si un pair revendique une plage contenant cette adresse. Si vous indiquez uniquement 10.8.0.0/24, votre ordinateur portable envoie le trafic LAN via le Wi-Fi local. Le trafic échoue ou atteint un 192.168.20.10 complètement différent.
En entrée, AllowedIPs est une liste de contrôle d’accès. Après avoir déchiffré un paquet provenant d’un pair, WireGuard compare son adresse source interne à la valeur AllowedIPs de ce pair et abandonne le paquet si elles ne correspondent pas. Aucun message n’est écrit dans les journaux et aucun compteur n’est incrémenté pour cet abandon. Le paquet disparaît simplement.
Les deux fichiers de configuration ne sont donc jamais des images miroir. Le client répertorie les ressources qu’il veut atteindre via le serveur. Le serveur répertorie les adresses sources que ce client est autorisé à utiliser.
La paire de fichiers de configuration correspondants
Client, /etc/wireguard/wg0.conf :
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
[Peer]
PublicKey = <server public key>
Endpoint = home.example.com:51820
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24
PersistentKeepalive = 25Serveur, /etc/wireguard/wg0.conf :
[Interface]
PrivateKey = <server private key>
Address = 10.8.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32Le routeur du domicile doit également rediriger le port UDP 51820 vers 192.168.20.5. Sinon, le handshake ne démarre jamais et les journaux du client affichent Handshake for peer 1 did not complete after 5 seconds, retrying. Ce guide suppose que vous avez déjà franchi cette étape.
Quatre lignes diffèrent d’une configuration classique en full tunnel. Chacune est intentionnelle.
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24sur le client, à la place de0.0.0.0/0, ::/0. Il s’agit d’un split tunnel : le sous-réseau du tunnel et le LAN du domicile passent parwg0, tandis que tout le reste conserve sa route locale. Votre navigation web ne passe pas par votre connexion à domicile. C’est généralement ce qu’il faut lorsque vous avez uniquement besoin d’accéder au NAS.AllowedIPs = 10.8.0.2/32sur le serveur, avec une seule adresse plutôt qu’une plage. Écrivez10.8.0.0/24à cet endroit. Ce client peut alors utiliser n’importe quelle adresse du tunnel. Si vous ajoutez ensuite un second peer avec une plage qui se chevauche, le trafic passe vers le peer configuré en dernier, sans qu’aucune erreur ne soit affichée.PersistentKeepalive = 25sur le client uniquement. Le client se trouve derrière un NAT (network address translation). Son routeur oublie l’association UDP après une ou deux minutes sans trafic. Le serveur ne peut alors plus l’atteindre. Le serveur possède une adresse publique et n’a pas besoin de keepalive.- Pas encore de ligne
DNS =. Son ajout modifie la résolution des noms pour toute la machine cliente. La section suivante consacrée au DNS explique son fonctionnement avant que vous l’activiez.
Un full tunnel permet également d’accéder au LAN, car 0.0.0.0/0 correspond à toutes les adresses. En contrepartie, tout votre trafic passe par le tunnel et vous pouvez rencontrer un conflit de sous-réseaux impossible à corriger depuis le client.
Pourquoi le même sous-réseau aux deux extrémités provoque un échec
Choisissez pour votre réseau domestique un sous-réseau que presque personne n’utilise, comme 192.168.20.0/24 ou 10.44.7.0/24. 192.168.1.0/24 et 192.168.0.0/24 sont les valeurs par défaut d’usine de la plupart des routeurs grand public. Tôt ou tard, votre ordinateur portable se connectera donc à un réseau de café ou d’hôtel utilisant exactement cette plage.
La collision bloque complètement la connexion, mais le comportement diffère selon le cas. Avec un split tunnel, wg-quick tente d’ajouter une route vers un préfixe déjà présent sur l’interface Wi-Fi. ip route add refuse l’opération et l’interface ne s’active pas :
RTNETLINK answers: File existsAvec un full tunnel, wg-quick installe des règles de policy routing qui écartent volontairement les routes plus spécifiques de la table principale. La route locale 192.168.1.0/24 prend le pas sur le tunnel. Tous les paquets destinés au LAN distant sortent donc par la liaison locale. Le tunnel est actif, le handshake fonctionne, mais le NAS est inaccessible. Renuméroter le LAN domestique est la seule véritable solution.
Transformer le serveur en routeur
ip route show default
printf 'net.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forwardLa première commande indique le nom réel de l’interface LAN. Les images actuelles utilisent des noms comme enp1s0 ou ens3, plus rarement eth0, et une règle de masquerade qui désigne la mauvaise interface ne correspond à aucun paquet. La dernière commande doit afficher net.ipv4.ip_forward = 1. Une commande sudo sysctl -w seule définit la même valeur, mais celle-ci est perdue au redémarrage suivant. C’est l’origine classique du message « ça fonctionnait jusqu’à mardi ».
Le forwarding doit également être autorisé par le firewall. Sur Ubuntu avec ufw activé, les paquets forwardés sont rejetés si DEFAULT_FORWARD_POLICY="ACCEPT" n’est pas défini dans /etc/default/ufw. Docker définit la même policy de son côté. Ainsi, si sudo iptables -S FORWARD | head -1 affiche -P FORWARD DROP sur une machine dont vous n’avez jamais configuré le firewall manuellement, cela signifie que Docker l’a fait. Le trafic de votre tunnel nécessite alors une règle d’acceptation explicite.
Pourquoi les réponses ne reviennent jamais
Si les règles 1 à 3 sont correctes, le ping atteint bien le NAS. Vous ne voyez pourtant rien, car la réponse ne peut pas revenir. Le NAS répond à 10.8.0.2, une adresse située en dehors de son propre sous-réseau. Il remet donc le paquet à sa passerelle par défaut, le routeur domestique à l’adresse 192.168.20.1. Ce routeur ne connaît pas 10.8.0.0/24. Il transmet donc la réponse à sa propre passerelle par défaut, votre connexion Internet, où elle est abandonnée. La requête arrive, mais la réponse est rejetée.
Option A : masquerading sur le serveur WireGuard. Le serveur réécrit l’adresse source de chaque paquet transféré avec 192.168.20.5, sa propre adresse LAN. Le NAS voit alors une requête provenant d’un voisin de son propre sous-réseau. Il répond directement au serveur, qui inverse la réécriture et renvoie le paquet dans le tunnel. Aucun autre équipement du LAN ne doit être modifié.
Ajoutez cette configuration au bloc [Interface] du serveur afin que la règle soit ajoutée et supprimée avec l’interface :
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADESur une machine déjà gérée par nftables, ajoutez-la plutôt dans /etc/nftables.conf :
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" counter masquerade
}
}Conservez le mot-clé counter. Sans lui, sudo nft list ruleset affiche la règle sans compteur de paquets. Or ce compteur vous indique précisément si la règle est utilisée.
Le masquerading apporte un second avantage, facile à négliger. De nombreux hôtes utilisent un firewall qui n’accepte que les connexions provenant de leur propre sous-réseau. Le partage de fichiers Windows fonctionne ainsi par défaut, tout comme plusieurs interfaces d’administration de NAS. Un paquet provenant de 10.8.0.2 est rejeté par l’hôte cible, même si le routage est correct. Après le masquerading, l’adresse source est une adresse LAN. Les règles correspondantes s’appliquent donc. En contrepartie, chaque client du tunnel apparaît comme 192.168.20.5 dans les journaux de tous les hôtes du LAN. Vous ne pouvez donc pas distinguer les clients, et les règles par client sur les équipements du LAN ne peuvent pas fonctionner.
Option B : une route statique sur le routeur domestique. Indiquez au routeur que 10.8.0.0/24 est accessible derrière 192.168.20.5. Sur un routeur Linux, une seule commande suffit :
sudo ip route add 10.8.0.0/24 via 192.168.20.5Les routeurs grand public proposent une page appelée Static Routes ou Routing dans les paramètres avancés : destination 10.8.0.0, masque 255.255.255.0, passerelle 192.168.20.5. Enregistrez cette route dans la configuration persistante du routeur, car un ip route add saisi sur une machine Linux disparaît au prochain redémarrage.
Cette solution conserve l’adresse réelle du client. Les journaux et les règles par client de votre LAN restent donc exploitables. Elle nécessite un routeur compatible avec les routes statiques. Elle ne s’applique aussi qu’aux hôtes qui utilisent ce routeur comme passerelle par défaut. Tout hôte doté d’un firewall local limité à son sous-réseau doit encore avoir sa propre règle pour 10.8.0.0/24. Commencez par le masquerading, car il ne nécessite aucune modification en dehors de la machine que vous contrôlez déjà. Passez ensuite à la route statique si vous avez besoin de conserver les adresses réelles des clients.
Comment déterminer laquelle des quatre règles est incorrecte
Remontez le chemin à partir du client. Chaque étape indique jusqu’où le paquet est parvenu.
Le paquet entre-t-il dans le tunnel ? Sur le client :
ip route get 192.168.20.10La réponse doit mentionner dev wg0. Si elle mentionne votre interface Wi-Fi, la règle 1 est incorrecte et le AllowedIPs du client ne couvre pas le sous-réseau LAN. Un ping: connect: Network is unreachable pointe vers la même ligne.
Les paquets arrivent-ils sur le serveur ? Exécutez ceci sur le serveur, puis envoyez un ping au NAS depuis le client :
sudo tcpdump -ni wg0 icmpUn chemin fonctionnel affiche IP 10.8.0.2 > 192.168.20.10: ICMP echo request. ICMP signifie Internet Control Message Protocol, le protocole utilisé par ping. Si rien ne s’affiche alors que le handshake est correct, le problème vient de la règle 2 : le AllowedIPs du serveur pour ce peer n’inclut pas 10.8.0.2. Le paquet a donc été abandonné lors du déchiffrement, avant d’atteindre wg0.
Les paquets repartent-ils vers le LAN ? Sur le serveur, surveillez le côté LAN :
sudo tcpdump -ni enp1s0 icmpDes requêtes visibles sur wg0, mais absentes ici, indiquent un problème lié à la règle 3 : le forwarding est désactivé ou une règle FORWARD a abandonné le paquet. Des requêtes visibles ici avec la source 10.8.0.2, mais sans réponse, indiquent un problème lié à la règle 4 : la réponse n’a pas de route de retour. Des requêtes visibles ici avec la source 192.168.20.5, mais sans réponse, indiquent que votre règle de masquerade fonctionne et que l’hôte cible refuse lui-même la connexion. Vérifiez donc le firewall du NAS. La même méthode fonctionne pour tout autre service : remplacez icmp par port 445, ou par le port concerné.
Je peux joindre l’adresse IP, mais pas le nom
ssh 192.168.20.10 fonctionne et ssh nas.home.arpa échoue :
ssh: Could not resolve hostname nas.home.arpa: Name or service not knownLe tunnel ne présente aucun problème. La résolution de noms suit un chemin distinct, et votre ordinateur portable interroge toujours le resolver appris via le Wi-Fi local. Ce resolver ne connaît pas les noms de votre réseau domestique.
Deux conditions doivent être réunies pour que les noms du réseau domestique fonctionnent. L’adresse du resolver doit se trouver dans AllowedIPs sur le client. Sinon, la requête DNS (domain name system) n’entre jamais dans le tunnel. Le resolver doit également accepter une requête dont l’adresse source est 10.8.0.2. De nombreux resolvers domestiques refusent ces requêtes par défaut : dnsmasq exécuté avec local-service ne répond qu’aux requêtes provenant d’un sous-réseau directement attaché, et Pi-hole est fourni avec un mode d’écoute qui n’autorise que les requêtes locales. Une règle de masquerade masque ce problème, car après la réécriture, la requête arrive depuis 192.168.20.5.
Côté client, une seule ligne suffit :
DNS = 192.168.20.1Sur un client Linux, il faut également définir openresolv ou un équivalent. Sinon, wg-quick s’arrête avec resolvconf: command not found. Avant de le définir sur une machine utilisant systemd-resolved, vérifiez son fonctionnement : wg-quick enregistre ces serveurs exclusivement. Tant que le tunnel est actif, toutes les résolutions effectuées sur l’ordinateur portable passent donc par le resolver domestique, et pas uniquement celles des noms du réseau domestique. Vérifiez le résultat avec resolvectl status wg0. Si vous voulez résoudre les noms domestiques via le réseau domestique et tous les autres noms localement, il s’agit de split DNS. La section Corriger le DNS via un tunnel WireGuard présente la configuration complète.
Pas d’adresse IP publique à domicile ? Placez un VPS au milieu
Si la page d’état de votre routeur affiche une adresse WAN dans 100.64.0.0/10, ou une adresse privée 192.168.x.x, vous êtes derrière un CGNAT (carrier-grade network address translation) et aucun handshake venant d’Internet ne peut atteindre votre domicile. Un handshake sortant fonctionne normalement. La solution consiste donc à utiliser un troisième nœud disposant d’une adresse publique. Un petit VPS héberge le hub, et la machine à domicile s’y connecte en sortie.
Les quatre règles ne changent pas. Elles s’appliquent désormais sur deux sauts, ce qui double le suivi des paramètres.
- Sur le VPS, l’entrée peer de la machine à domicile reçoit
AllowedIPs = 10.8.0.3/32, 192.168.20.0/24: sa propre adresse de tunnel, ainsi que le sous-réseau pour lequel elle est autorisée à communiquer. - Sur le VPS, l’entrée peer de l’ordinateur portable reste
AllowedIPs = 10.8.0.2/32. - Sur l’ordinateur portable, le peer du VPS reçoit
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24, car tout le trafic passe désormais par le hub. - Sur la machine à domicile, le peer du VPS reçoit
AllowedIPs = 10.8.0.0/24, et la machine à domicile portePersistentKeepalive = 25, puisqu’elle se trouve désormais derrière le NAT. - Le VPS a également besoin de
net.ipv4.ip_forward = 1, et sa chaîne de transfert doit autoriserwg0verswg0, car le trafic de l’ordinateur portable arrive et repart par la même interface. Un firewall conçu pour un VPS en full tunnel classique bloque précisément ce trafic.
Si vous n’avez pas encore configuré l’extrémité du VPS, configurer WireGuard sur un VPS couvre la génération des clés, le firewall et l’unité systemd. Le fonctionnement général permettant d’atteindre une machine qui n’accepte pas les connexions entrantes est décrit dans ouvrir un tunnel inverse depuis un réseau derrière CGNAT. Lorsque la gestion manuelle des adresses peer devient fastidieuse, exécuter un routeur de sous-réseau Tailscale fournit le même routage avec une gestion automatisée des adresses.
Faire survivre la configuration à un redémarrage
sudo systemctl enable --now wg-quick@wg0
sudo systemctl enable --now nftables
sudo wg showenable --now est l’étape que beaucoup de personnes ignorent. Un wg-quick up wg0 exécuté manuellement disparaît après la prochaine mise à niveau du kernel et le redémarrage. wg show doit afficher le pair avec une ligne latest handshake récente et des compteurs de transfert non nuls dans les deux sens.
L’ajout ultérieur d’un second client ne nécessite pas de redémarrage. Cela déconnecterait tous les clients actuellement connectés :
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip affiche la configuration sans les clés que seul wg-quick comprend, et syncconf applique la différence à chaud pendant que les sessions existantes continuent de fonctionner. Cette commande ne met à jour que les pairs. La modification d’un Address ou l’ajout d’une nouvelle ligne PostUp nécessite toujours un arrêt et un redémarrage complets.
FAQ
Pourquoi puis-je joindre le serveur WireGuard avec ping, mais rien d’autre sur le LAN domestique ?
Joindre 10.8.0.1 avec ping prouve seulement que le tunnel fonctionne. Le reste du LAN relève du routage. Le AllowedIPs du client doit inclure 192.168.20.0/24, sinon le paquet n’entre jamais dans le tunnel. Le serveur doit avoir net.ipv4.ip_forward défini sur 1, sinon il abandonne les paquets qui ne lui sont pas destinés. Le LAN doit aussi avoir une route de retour vers 10.8.0.0/24. Exécutez sudo tcpdump -ni enp1s0 icmp sur le serveur pendant que vous envoyez les requêtes ping : si les requêtes partent avec 10.8.0.2 comme adresse source et qu’aucune réponse ne revient, le chemin de retour est manquant.
Dois-je ajouter une route statique sur mon routeur domestique ?
Seulement si vous n’utilisez pas la règle de masquerade. Une règle de masquerade sur le serveur WireGuard remplace l’adresse source du trafic du tunnel par l’adresse LAN du serveur. Les hôtes du LAN répondent ainsi à un voisin dont ils connaissent déjà le chemin, et le routeur n’intervient jamais. L’autre solution consiste à ajouter une route statique vers 10.8.0.0/24 via l’adresse LAN du serveur. Cette solution est utile si vous voulez voir les vraies adresses des clients dans les journaux des hôtes du LAN, ou y appliquer des règles de pare-feu par client.
Pourquoi le même sous-réseau aux deux extrémités fait-il échouer le tunnel ?
Votre ordinateur portable ne peut pas avoir deux routes pour le même préfixe. Si le réseau local attribue 192.168.1.0/24 et que votre LAN domestique utilise aussi 192.168.1.0/24, un wg-quick up en split tunnel échoue lorsque ip route add refuse avec RTNETLINK answers: File exists. Un full tunnel s’établit malgré tout, mais wg-quick installe des règles de policy routing qui empêchent les routes plus spécifiques de la table principale d’être utilisées. Le réseau local est donc prioritaire et le LAN distant reste inaccessible. Renumérotez le LAN domestique avec un sous-réseau peu courant, par exemple 192.168.20.0/24. Il n’existe pas de correction côté client.
Je peux joindre le NAS par son adresse IP, mais pas par son nom. Que manque-t-il ?
La résolution de noms ne passe pas automatiquement par le tunnel. Ajoutez DNS = 192.168.20.1, l’adresse de votre resolver domestique, au bloc client [Interface], et vérifiez que cette adresse se trouve dans le AllowedIPs du peer. Sinon, la requête n’entre jamais dans le tunnel. Vérifiez ensuite que le resolver accepte les requêtes provenant de l’extérieur de son propre sous-réseau, car dnsmasq avec local-service et le mode d’écoute local-only de Pi-hole les refusent tous les deux. Une règle de masquerade sur le serveur WireGuard contourne ce problème en remplaçant l’adresse source de la requête.
Ma connexion domestique n’a pas d’adresse IP publique. Puis-je quand même accéder à mon LAN ?
Oui, avec un troisième nœud. Derrière un CGNAT, l’adresse WAN de votre routeur est privée. Aucun peer sur Internet ne peut donc démarrer un handshake avec lui, alors qu’un handshake sortant fonctionne normalement. Installez WireGuard sur un VPS doté d’une adresse publique, faites connecter la machine domestique au VPS avec PersistentKeepalive = 25, puis ajoutez dans l’entrée peer de cette machine sur le VPS un AllowedIPs contenant son adresse de tunnel ainsi que 192.168.20.0/24. Le VPS doit ensuite activer le forwarding et disposer d’une règle forward qui autorise wg0 vers wg0.