ZeroTier, le bateau entier sur votre mobile

ZeroTier a la réputation d'être un fouillis d'adresses, et c'est presque toujours parce qu'on raconte l'histoire à l'envers. Commençons par ce qu'on y gagne, car c'est ce qui fait que ça vaut la peine.

À bord, on atteint Signal K avec l'adresse et le port de la machine où il est installé : l'adresse du LAN du bateau. Tant qu'on est dedans, cela suffit. Dès qu'on sort — le mobile en données, le portable à la maison —, cette adresse cesse d'exister, et sans rien d'autre il faudrait mémoriser une seconde adresse pour chaque appareil, celle que ZeroTier lui attribue, et se souvenir de laquelle correspond à chaque service. Deux jeux d'adresses pour la même chose, et celui de toujours ne sert plus juste au moment où l'on est dehors.

Avec la route gérée, l'adresse habituelle vaut dedans comme dehors. Si à bord vous ouvrez 192.168.1.10:3000 dans le navigateur, cette même adresse — sans rien changer, sans une note avec l'autre — ouvre le même service depuis le port, depuis la maison ou depuis le mobile avec de la couverture.

L'avantage est double, et les deux moitiés comptent :

  • Vous atteignez les appareils qui n'ont pas de client. Le routeur, le moniteur de batteries ou le Raspberry du moteur ne peuvent pas avoir ZeroTier installé ; ils sont sur le LAN et ils y restent. Avec la route, vous les atteignez comme si vous étiez assis dans le cockpit.
  • Vous cessez d'avoir deux adresses pour la même chose. Pas besoin de retenir celle de ZeroTier pour chaque appareil : celle du LAN fera l'affaire, celle qui est déjà dans les signets et dans la tête.

Ce n'est pas une promesse de la documentation : c'est testé sur le bateau. Avec la route en service, le portable et la tablette en 4G ouvrent les services de bord par leur adresse LAN, celle de toujours.

Dans la première partie, nous avons vu comment créer le réseau, installer le client sur la machine Linux du bateau et atteindre ce qui tourne dedans : Signal K, le panneau, l'alarme de mouillage. Il manque le reste du LAN de bord, qui n'a pas de client.

1. Le problème

ZeroTier connecte des appareils qui font tourner son client, un par un. Le routeur, le moniteur de batteries et le Raspberry du moteur ne l'ont pas : ils sont sur le LAN du bateau et ils y restent. Depuis le mobile, j'atteins 10.147.20.10 et je vois le panneau ; 192.168.1.1 ne mène nulle part, parce qu'elle n'existe qu'à l'intérieur du bateau. Comment atteindre tout un réseau avec un outil qui ne connaît que des appareils ?

2. La solution tient en deux pièces

Dans la console, dans les routes gérées, on dit au réseau : « pour atteindre 192.168.1.0/24, passe par 10.147.20.10 ». L'ordre est distribué aux membres : le mobile apprend que ce réseau se trouve derrière la machine Linux et lui envoie ce trafic.

Sur la machine Linux, il faut faire deux choses : transmettre les paquets d'une interface à l'autre et masquer ce qui sort vers le LAN. Transmettre se comprend tout seul : le paquet entre par l'interface de ZeroTier, il est destiné à 192.168.1.50 et, au lieu d'être rejeté, il sort par celle du LAN.

Masquer, c'est ce qui coûte. Le moniteur de batteries ne sait pas que 10.147.20.0/24 existe : si une requête lui arrive avec comme source 10.147.20.10, il y répond, mais la réponse n'a aucun chemin de retour. En masquant, la machine Linux met sa propre adresse du LAN — 192.168.1.10 — comme source et se charge de renvoyer chaque réponse à son propriétaire. Pour le LAN, tout ce qui vient du mobile semble venir de la machine Linux.

Et il faut les deux. Avec la route et sans la transmission, les paquets restent sur la machine Linux ; avec la transmission et sans la route, le mobile essaie de sortir par son réseau de la maison et n'arrive pas ; avec les deux et sans masquage, la réponse se perd.

3. Pourquoi masquer et pas mettre une route sur le routeur

La façon propre serait de dire au routeur du bateau « pour atteindre 10.147.20.0/24, passe par la machine Linux ». S'il l'accepte, le résultat est le même sans toucher à rien d'autre.

En pratique, ce n'est pas toujours possible : beaucoup de routeurs de bord cachent cette option, et d'autres fonctionnent en mode pont parce que c'est un autre appareil qui distribue les adresses. Masquer sur la machine Linux ne touche pas au routeur et ne configure que l'appareil qui avait déjà le client. C'est la pièce que tout le monde essaie d'éviter et que tout le monde comprend ensuite.

4. Comment on configure, pas à pas

Exemple : LAN 192.168.1.0/24 ; la machine Linux, 192.168.1.10 sur le LAN et 10.147.20.10 sur ZeroTier. L'interface de ZeroTier s'appelle zt… et celle du LAN eth0, enp1s0 ou wlan0 ; on la vérifie avec ip -br addr.

La route, dans la console

Dans les routes gérées, on ajoute le sous-réseau avec la machine Linux comme passerelle :

192.168.1.0/24   via   10.147.20.10

Si le LAN est plus large — un /23 —, le préfixe doit le refléter. On enregistre et les membres reçoivent la route.

La route : le sous-réseau du bateau, à travers la machine qui sert de passerelle

Sur la machine Linux elle-même, la route peut ne pas apparaître : elle a déjà ce LAN connecté et cette route l'emporte. On vérifie depuis le mobile.

La transmission et le masquage

sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -I POSTROUTING -s 10.147.20.0/24 -o eth0 -j MASQUERADE
sudo iptables -I FORWARD -i zt+ -o eth0 -s 10.147.20.0/24 -j ACCEPT
sudo iptables -I FORWARD -i eth0 -o zt+ -m state --state RELATED,ESTABLISHED -j ACCEPT

Avertissement : sur les distributions récentes — Debian 13, le Raspberry Pi OS actuel —, le pare-feu d'origine est nftables et le paquet iptables peut ne pas être installé, auquel cas ces commandes échouent dès qu'on les copie-colle. Si cela arrive, on l'installe avec le gestionnaire de paquets de sa distribution avant de continuer. Je ne l'ai pas vérifié sur une machine fraîchement installée, donc prenez-le comme un avertissement et non comme une certitude.

  • La première active la transmission : sans cela, la machine ne fait pas passer de paquets d'une interface à l'autre.
  • La deuxième masque : ce qui sort vers le LAN depuis le sous-réseau ZeroTier porte l'adresse de la machine elle-même.
  • La troisième et la quatrième ouvrent le passage dans les deux sens : du bateau virtuel vers le LAN uniquement depuis ce sous-réseau, et au retour uniquement les réponses de conversations déjà ouvertes.

Elles passent avec -I (en tête) et non avec -A : sur une machine avec Docker, la chaîne FORWARD est en DROP et une règle à la fin ne s'applique jamais. Elles ne survivent pas non plus à un redémarrage : sur le bateau, une unité systemd les remet au démarrage, après ZeroTier. C'est la pièce qui manque pour que tout tienne le lendemain, et vous l'avez entière dans l'annexe de la fin.

5. Le piège du MTU

ZeroTier fonctionne à 2800 par défaut et un LAN normal à 1500. La machine Linux reçoit un paquet de 2800 et le transmet, mais le LAN ne le porte pas : il est rejeté en silence.

Le symptôme ressemble à une panne aléatoire : le ping répond et la page d'accueil du routeur se charge, mais un fichier reste à moitié, une session SSH se fige en listant un gros dossier, le graphique du panneau reste à réfléchir. Au bout d'un moment, ça revient. Ce n'est ni le service ni le wifi : ce sont les transferts volumineux.

On le corrige en réduisant la taille maximale à la sortie vers le LAN :

sudo iptables -t mangle -I FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

À l'ouverture de chaque connexion TCP, les deux extrémités s'accordent sur la taille maximale de segment ; cette règle réécrit cet accord sur ce qui sort vers le LAN pour que cela tienne en 1500. Mesuré sur le bateau : le client annonçait 2740 et sortait à 1460. C'est la ligne qui manque dans presque tous les tutoriels et celle qui transforme un réseau qui « marche parfois » en un réseau qui marche.

6. Les limites, sans fioritures

Cela ne transporte ni broadcast ni multicast. Sur ce réseau, il n'y a pas de diffusion, donc dehors les noms .local — on utilise des adresses IP — et dehors la découverte automatique : le programme qui cherche des imprimantes ou des caméras ne les trouve pas. Pour interroger le routeur par son IP, peu importe.

Depuis le LAN, on n'ouvre pas de connexions vers ZeroTier. Le moniteur ne peut pas appeler le mobile ; l'inverse oui, et c'est ce qu'il faut.

Le masquage cache l'origine. Dans les journaux du routeur, les accès depuis ZeroTier apparaissent avec l'adresse de la machine Linux : bon à savoir avant de chercher un intrus qui est vous.

7. Si votre compte est un des nouveaux

Depuis novembre 2025, il y a deux consoles. L'ancienne, my.zerotier.com, est celle des comptes antérieurs à cette date : elle garde son quota — 25 appareils dans la plupart des cas — et autorise les routes gérées gratuitement. La nouvelle, central.zerotier.com, est celle des nouvelles inscriptions : forfait Personal gratuit avec 10 appareils, un réseau et un administrateur, et sans routes gérées. La machine ne sait pas dans quelle console vit son réseau.

Si vous vous êtes inscrit récemment, la route de la section 4 ne peut pas être mise avec le forfait gratuit : l'écran est là, mais désactivé. Il faut passer à un forfait payant — Essential, 18 $/mois, à titre de référence — ; je n'affirme pas lequel est le minimum exact : le tableau des tarifs et les forums ne disent pas la même chose.

Il n'y a pas non plus de migration automatique : nouveau réseau, nouvel identifiant, chaque machine jointe et autorisée de nouveau, et routes, DNS et règles refaits. Le quota de l'ancien compte ne voyage pas.

Dans la nouvelle console, ce même écran avec le forfait gratuit

8. Pour finir

Avec cela, j'ai le routeur, le moniteur de batteries et le Raspberry du moteur sur le mobile : regarder les batteries sans descendre à la salle des machines, voir qui est connecté, jeter un œil au moteur. La musique aussi, celle qui vit sur le bateau et pas dans une playlist internet : la tablette avec l'application Tempo tire du Navidrome de la machine de bord, et au mouillage à une dizaine de kilomètres, ça joue sans problème. Ce n'est pas de la magie : deux pièces et une règle MTU que presque personne ne mentionne.

Si un jour cela change, il y a un plan B : Tailscale fait la même chose avec un subnet router — une machine qui annonce le sous-réseau —, sur son forfait gratuit et sur Android sans root ; c'est aussi de la couche 3, donc cela ne transporte pas non plus de broadcast ni de multicast. Et Headscale est un plan de contrôle à soi, installable sur un serveur. En attendant, ce qu'il y a fonctionne sans toucher au routeur du bateau.

Le compte, le client sous Linux et l'application mobile sont dans la première partie ; ce qui est ici commence là où celle-là s'arrêtait court.

Annexe : que les règles survivent à un redémarrage

Les règles d'iptables vivent en mémoire : quand on éteint la machine, elles s'en vont et ne reviennent pas toutes seules. Le symptôme trompe, parce qu'il n'y a aucune erreur : la route cesse de fonctionner et on croit que ZeroTier s'est cassé.

Pour que cela ne dépende pas de quelqu'un qui se souvienne des quatre commandes, ils tiennent en deux fichiers : un script qui les pose et une unité systemd qui le lance au démarrage.

Le script

#!/bin/bash
# Remet les règles de la route ZeroTier -> LAN du bateau.
# C'est idempotent : si la règle existe déjà, il ne la duplique pas.
set -u

ZT=10.147.20.0/24      # sous-réseau du réseau ZeroTier
LAN_IF=eth0            # interface du LAN du bateau (à voir avec : ip -br addr)
IPT=/usr/sbin/iptables

# add <table> <chaîne> <règle...> : insère seulement si elle n'y est pas déjà.
add() {
    local t=$1 c=$2; shift 2
    "$IPT" -t "$t" -C "$c" "$@" 2>/dev/null || "$IPT" -t "$t" -I "$c" "$@"
}

add nat    POSTROUTING -s "$ZT" -o "$LAN_IF" -j MASQUERADE
add filter FORWARD     -i zt+ -o "$LAN_IF" -s "$ZT" -j ACCEPT
add filter FORWARD     -i "$LAN_IF" -o zt+ -m state --state RELATED,ESTABLISHED -j ACCEPT
add mangle FORWARD     -o "$LAN_IF" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

On l'enregistre sous zerotier-lan-forward.sh. Il vérifie avec -C avant d'insérer avec -I, donc on peut le lancer autant de fois qu'on veut sans remplir la table de règles en double. La dernière ligne est le clamp de MSS de la section du MTU : sans elle, le même montage se bloque sur les gros téléchargements.

L'unité

[Unit]
Description=Route ZeroTier vers le LAN du bateau
After=network-online.target zerotier-one.service
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/zerotier-lan-forward.sh
ExecStop=-/usr/sbin/iptables -t mangle -D FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
ExecStop=-/usr/sbin/iptables -D FORWARD -i eth0 -o zt+ -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=-/usr/sbin/iptables -D FORWARD -i zt+ -o eth0 -s 10.147.20.0/24 -j ACCEPT
ExecStop=-/usr/sbin/iptables -t nat -D POSTROUTING -s 10.147.20.0/24 -o eth0 -j MASQUERADE

[Install]
WantedBy=multi-user.target

On l'enregistre sous zerotier-lan-forward.service. oneshot avec RemainAfterExit=yes veut dire « exécute ceci une fois et tiens-le pour fait » : il ne reste aucun processus en marche. After= attend que ZeroTier soit levé, parce qu'avant cela l'interface zt… n'existe pas et les règles n'ont pas où aller. Les lignes ExecStop défont exactement ce qui a été fait, pour qu'arrêter l'unité laisse la machine comme elle était.

Pas besoin de connaître systemd pour cela : ce sont trois fichiers et trois commandes.

Le renvoi du noyau

Pour que les paquets passent d'une interface à l'autre, il faut le dire au noyau. Si la machine n'a pas le renvoi activé, les quatre règles sont en place et pourtant rien ne passe : aucun message d'erreur, aucun indice. Certains l'ont activé pour autre chose — Docker, par exemple, le pose au démarrage —, mais c'est un effet de bord et cela peut changer avec une mise à jour : mieux vaut le déclarer.

# /etc/sysctl.d/99-zerotier-lan-forward.conf
net.ipv4.ip_forward = 1

On l'enregistre sous 99-zerotier-lan-forward.conf et on l'applique :

sudo install -m 644 99-zerotier-lan-forward.conf /etc/sysctl.d/
sudo sysctl --system

Et on le vérifie : sysctl net.ipv4.ip_forward doit renvoyer net.ipv4.ip_forward = 1.

Cela fait trois fichiers : le script, l'unité et celui-ci. Les deux autres s'installent et se mettent en marche dans la section suivante.

Le mettre en marche

Avec les deux fichiers enregistrés dans le répertoire de travail :

sudo install -m 755 zerotier-lan-forward.sh      /usr/local/sbin/
sudo install -m 644 zerotier-lan-forward.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now zerotier-lan-forward.service

Le vérifier

systemctl status zerotier-lan-forward.service doit dire active (exited). Les quatre règles se voient ainsi :

sudo iptables -t nat -S POSTROUTING
sudo iptables -S FORWARD
sudo iptables -t mangle -S FORWARD

Mais le vrai test n'est aucune de celles-là : c'est redémarrer la machine et rentrer de nouveau depuis l'extérieur.

La référence officielle de l'unité est celle de freedesktop, en anglais : systemd.service.

Article précédent Article suivant