Configurer un subnet router Tailscale sur un VPS
Rendez un réseau privé accessible depuis votre tailnet avec un VPS : approbation de la route, IP forwarding persistant après redémarrage et --accept-routes sous Linux.
Ce que fait un subnet router Tailscale
Un subnet router Tailscale est une machine qui annonce une plage complète d’adresses IP privées à votre tailnet. Tous les appareils du tailnet peuvent ainsi atteindre les adresses de cette plage, même si aucun appareil de cette plage n’exécute Tailscale. Votre tailnet est votre réseau Tailscale privé : l’ensemble des appareils connectés au même compte ou à la même organisation. Un exit node est la fonctionnalité avec laquelle on le confond souvent, mais il remplit la fonction inverse. Il achemine tout le trafic d’un appareil via le VPS, qui devient alors la route de cet appareil vers Internet public.
Une phrase pour chacun. Un subnet router rend un réseau privé accessible depuis le tailnet. Un exit node modifie le point de sortie de votre trafic public. Si c’est ce second fonctionnement que vous recherchez, consultez plutôt comment exécuter un exit node Tailscale sur un VPS. Ce sont des flags distincts. Un même VPS peut utiliser les deux en même temps, mais ils répondent à des problèmes différents et ne rencontrent pas les mêmes pannes.
Quand un VPS doit servir de subnet router
Le cas le plus courant concerne un réseau privé déjà fourni par votre hébergeur. Votre VPS possède une adresse publique et une seconde interface sur un segment privé. Les autres serveurs de ce segment n’ont aucune adresse publique : une base de données à 10.0.0.20 et une cible de sauvegarde à 10.0.0.30. Installez Tailscale sur un VPS et annoncez 10.0.0.0/24. Votre ordinateur portable pourra alors joindre directement ces adresses privées. Rien d’autre ne change sur le segment, et la base de données ne possède toujours aucune adresse publique. Si ce segment ne vous sert qu’à accéder à une seule application web sur un seul port, annoncer toute la plage dépasse le besoin réel. Dans ce cas, Tailscale serve active plutôt HTTPS sur ce seul port. Le même raisonnement s’applique à un daemon qui se lie volontairement à localhost uniquement, comme dsh exécuté sans interface sous systemd. Une adresse du tailnet sur ce VPS remplace alors le tunnel SSH que vous devriez autrement maintenir ouvert pour accéder à son interface.
L’autre cas est celui d’un réseau situé de l’autre côté du VPS. Il peut s’agir d’un LAN domestique ou professionnel (local area network) derrière son propre routeur, ou d’une baie d’équipements qui ne peuvent pas exécuter Tailscale, comme un switch administrable ou un ancien NAS doté d’un firmware verrouillé. Une machine Linux de ce réseau devient le subnet router de tous les autres équipements. Le routeur domestique situé en amont n’a besoin d’aucun port forward pour cela. Le fonctionnement de Tailscale à travers le NAT sans ouvrir de port sur le routeur explique pourquoi cette configuration fonctionne et dans quels cas Tailscale utilise un relay. À domicile, cette machine est souvent une petite VM sur un hyperviseur que vous exploitez déjà. Le calcul du coût d’un hôte Proxmox à domicile par rapport à un VPS loué permet de trancher avant de décider à quelle extrémité du tunnel vos services doivent être hébergés.
Les deux cas ont une exigence commune. Le subnet router doit déjà pouvoir atteindre la plage qu’il annonce, avec sa propre table de routage et son propre firewall. Tailscale ne crée pas cette connexion. Il achemine le trafic jusqu’au routeur, puis le remet au kernel afin qu’il le transmette.
Installer Tailscale et vérifier d’abord la route locale
curl -fsSL https://tailscale.com/install.sh | shLe script détecte la distribution, ajoute le dépôt de paquets de Tailscale, installe la commande tailscale et le daemon tailscaled, puis active le service. Vérifiez-le avec systemctl is-active tailscaled, qui doit afficher active.
Avant toute autre étape, vérifiez que le VPS peut atteindre le réseau que vous prévoyez d’annoncer.
ip route show
ping -c3 10.0.0.20ip route show doit lister la plage privée sur une interface réelle, par exemple 10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5. Si le ping échoue ici, directement vers le routeur, aucun flag Tailscale ne pourra résoudre le problème. Le problème vient de la configuration réseau du VPS ou d’un firewall sur l’hôte cible. Corrigez-le d’abord, car tous les tests suivants en dépendent.
Activer le forwarding IP et le conserver après un redémarrage
Une machine Linux ignore tout paquet qui ne lui est pas destiné, sauf si le forwarding est activé. Transmettre les paquets des autres machines est la fonction principale d’un routeur de sous-réseau. Cette étape est donc obligatoire.
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.confVérifiez-le avec sysctl net.ipv4.ip_forward, qui doit afficher net.ipv4.ip_forward = 1.
Cette étape est souvent configurée à moitié. sudo sysctl -w net.ipv4.ip_forward=1 fonctionne immédiatement, mais sa valeur est perdue au prochain démarrage. Le routeur de sous-réseau peut donc fonctionner pendant des semaines, puis s’arrêter le matin suivant un redémarrage provoqué par une mise à niveau du noyau. Le problème est difficile à repérer, car rien ne semble cassé. tailscale status indique toujours que le nœud est en ligne, la console d’administration indique toujours que la route est approuvée et les clients ont toujours installé la route. Les paquets arrivent sur le VPS, puis le noyau les ignore sans rien journaliser. Écrire les valeurs dans /etc/sysctl.d/99-tailscale.conf permet de les restaurer après un redémarrage.
Si vous annoncez des routes alors que le forwarding est encore désactivé, tailscale up vous avertit immédiatement. Sous Linux, le message ressemble à Warning: IP forwarding is disabled, subnet routing/exit nodes will not work. Lisez la sortie de cette commande au lieu de la faire défiler sans la consulter.
Annoncer les routes
sudo tailscale up --advertise-routes=10.0.0.0/24Sur un VPS déjà connecté à votre tailnet, modifiez directement le paramètre :
sudo tailscale set --advertise-routes=10.0.0.0/24Utilisez tailscale set pour toutes les modifications ultérieures. Relancer tailscale up avec un seul indicateur réinitialise les indicateurs que vous n’avez pas répétés. La CLI s’arrête alors avec une erreur indiquant que cette méthode de modification exige de mentionner tous les indicateurs non définis par défaut. tailscale set modifie un seul paramètre et laisse les autres inchangés.
Plusieurs plages s’inscrivent dans une seule liste séparée par des virgules, sans espaces : --advertise-routes=10.0.0.0/24,192.168.50.0/24. Chaque entrée doit être une adresse réseau en notation CIDR (classless inter-domain routing, au format 10.0.0.0/24). Si vous indiquez par erreur votre propre adresse d’hôte, 10.0.0.5/24, la valeur est rejetée, car les bits situés après le préfixe ne sont pas nuls. L’erreur indique le préfixe que vous vouliez probablement utiliser. Pour arrêter l’annonce, définissez une liste vide avec sudo tailscale set --advertise-routes=.
Approuver la route dans la console d’administration
La publication d’une route est une demande, pas une modification effective. Tant qu’un administrateur ne l’a pas approuvée, aucun client ne reçoit cette route et aucune adresse de la plage n’est accessible. Ce fonctionnement est volontaire : une machine capable de s’ajouter à la table de routage de tout le monde pourrait intercepter le trafic de n’importe quelle plage.
Approuvez-la dans la page Machines de la console d’administration. Le VPS apparaît avec un badge de sous-réseau. Ouvrez sa ligne, repérez la section des sous-réseaux, modifiez les paramètres de la route, cochez la route, puis enregistrez.
L’approbation s’applique à chaque préfixe. Si vous publiez 10.0.0.0/24 aujourd’hui, puis 192.168.50.0/24 le mois prochain, le nouveau préfixe apparaît comme non approuvé, tandis que l’ancien continue de fonctionner. Une route approuvée et une route ignorée sont identiques depuis le VPS. Consultez donc la console avant d’entreprendre tout autre diagnostic.
Vous pouvez éviter cette étape manuelle avec un bloc autoApprovers dans le fichier de règles tailnet :
{
"autoApprovers": {
"routes": {
"10.0.0.0/24": ["tag:subnet-router"]
}
}
}Démarrez ensuite le nœud avec cette étiquette, sudo tailscale up --advertise-routes=10.0.0.0/24 --advertise-tags=tag:subnet-router, et la route est approuvée dès sa publication. L’étiquette doit d’abord exister dans la section tagOwners du même fichier de règles. Cette configuration est utile si vous reconstruisez le VPS à partir d’un script, car un nœud reconstruit est un nouveau nœud et ses routes redeviennent non approuvées.
Pourquoi les clients Linux ignorent la route sans --accept-routes
La route est maintenant annoncée et approuvée. Votre téléphone et votre Mac peuvent atteindre 10.0.0.20. Votre ordinateur portable Linux ne le peut pas, et rien dans la console d’administration n’indique de problème.
Accepter une route de sous-réseau consiste à ajouter des entrées dans la table de routage du client. Sous Android, iOS, macOS, tvOS et Windows, le client Tailscale s’en charge. Sous Linux, ce n’est pas le cas, car une machine Linux est souvent un serveur ou un routeur dont la table de routage a été configurée volontairement. Insérer silencieusement une /24 apprise du réseau pourrait interrompre le trafic déjà géré par cette machine. Sous Linux, vous devez donc activer cette option sur chaque client :
sudo tailscale set --accept-routesVérifiez ensuite où la route a été installée :
ip route show table 52
ip route get 10.0.0.20Sous Linux, Tailscale ne place pas les routes acceptées dans la table de routage principale. Il les place dans la table de routage 52 et installe des règles de policy routing, visibles avec ip rule show dans la plage de priorités 5210 à 5270, qui envoient les paquets ne correspondant à aucune règle vers cette table. Ainsi, ip route show seul ne répertorie jamais 10.0.0.0/24, et un lecteur qui vérifie uniquement cette commande peut conclure que --accept-routes n’a rien fait. ip route show table 52 est la commande qui montre la réalité et doit répertorier la plage annoncée sur tailscale0.
Une exception mérite d’être connue. Si ce nœud Linux est lui-même un second subnet router pour son réseau local, --accept-routes lui fait envoyer le trafic destiné à son propre sous-réseau directement connecté via l’autre routeur, au lieu de l’envoyer par sa propre interface. Sur un routeur de secours dans une paire haute disponibilité, laissez --accept-routes désactivé et contentez-vous d’annoncer la route.
Mode de défaillance : deux routeurs annoncent des plages qui se chevauchent
Deux subnet routers peuvent annoncer des plages identiques. C’est ainsi que fonctionne une paire haute disponibilité : chaque routeur annonce exactement les mêmes préfixes, et les clients peuvent basculer de l’un à l’autre. Les plages qui se chevauchent avec des longueurs de préfixe différentes sont également autorisées, et Tailscale sélectionne la correspondance la plus spécifique. Si le routeur A annonce 10.0.0.0/24 et le routeur B annonce 10.0.0.0/16, le trafic vers 10.0.0.20 passe par A.
Ce qui surprend, c’est le comportement lorsque A passe hors ligne. Tailscale ne bascule pas vers la route moins spécifique, car un routeur hors ligne ne peut pas retirer son annonce et les clients continuent de privilégier le préfixe le plus spécifique. Le trafic vers 10.0.0.20 s’arrête, tandis que le trafic vers 10.0.1.20, couvert uniquement par /16 de B, continue de fonctionner via B. Le symptôme donne l’impression qu’une partie du réseau privé est indisponible. La cause est un nœud hors ligne qui conserve le préfixe le plus spécifique. Pour assurer le basculement, faites également annoncer les préfixes plus étroits par le routeur qui annonce la plage la plus large, afin que les deux couvrent les mêmes adresses.
L’autre chevauchement est plus proche du client. Si vous êtes connecté à un réseau d’hôtel en 192.168.1.0/24 alors que votre subnet router annonce 192.168.1.0/24, les deux routes entrent en concurrence pour les mêmes destinations. La route sélectionnée dépend de la plateforme. Sous Linux, ajoutez une règle avant celle de Tailscale afin que les adresses locales utilisent la table principale :
sudo ip rule add to 192.168.1.0/24 priority 2500 lookup mainCette règle n’est pas persistante et disparaît au prochain démarrage. La véritable solution consiste à choisir une plage privée que vous ne rencontrerez pas sur les réseaux externes. 192.168.0.0/24 et 192.168.1.0/24 sont les valeurs par défaut de la plupart des routeurs domestiques. Choisissez donc une plage dans 10.0.0.0/8 de manière délibérée. Le même conflit perturbe un VPN WireGuard classique configuré manuellement, pour la même raison : la route locale la plus spécifique est sélectionnée et le trafic n’entre donc jamais dans le tunnel.
Mode de défaillance : le DNS renvoie une adresse couverte par aucune route
Ce cas est difficile à diagnostiquer, car rien ne signale d’erreur. Le nom est résolu. La connexion expire.
Supposons que db.internal.example.com soit résolu vers 10.0.5.20 par votre nameserver privé et que vous annonciez 10.0.0.0/24. La résolution réussit, car la résolution DNS (domain name system) et le routage IP sont deux étapes distinctes, et aucune ne vérifie l’autre. Le paquet destiné à 10.0.5.20 ne trouve alors aucune route correspondante sur le tailnet. Il sort par la passerelle par défaut du client, puis disparaît.
Deux commandes permettent de distinguer ces deux étapes :
nslookup db.internal.example.com
ip route get 10.0.5.20Si la résolution renvoie une adresse, mais que ip route get ne répond pas avec dev tailscale0, le nom est correct et la route manque sur ce client. Sur un client Linux, vérifiez d’abord que --accept-routes est activé, car aucune route de sous-réseau n’est installée sans lui, même lorsqu’un préfixe approuvé couvre déjà l’adresse. S’il est activé et qu’aucun préfixe approuvé ne couvre l’adresse, annoncez une plage qui la couvre, soit 10.0.0.0/16, soit un second préfixe explicite, puis approuvez le nouveau préfixe dans la console.
Le nameserver lui-même présente un piège similaire. Si vous configurez un nameserver global dans la console d’administration à une adresse privée telle que 10.0.0.53, cette adresse doit appartenir à une route approuvée. Sinon, les appareils qui ne se trouvent pas sur ce réseau privé ne peuvent pas atteindre le resolver. Activez l’option qui remplace les serveurs DNS locaux alors que le resolver configuré est inaccessible, et tous les appareils du tailnet perdent immédiatement la résolution des noms, y compris ceux qui fonctionnaient une seconde plus tôt. Annoncez et approuvez d’abord la route vers le resolver, puis modifiez le paramètre DNS. Si le DNS dans un tunnel est la partie qui vous pose régulièrement problème, la façon dont le DNS tombe en panne sur un tunnel WireGuard décrit le même mécanisme sans la couche de coordination supplémentaire.
NAT source et liaisons site à site
Par défaut, le subnet router réécrit l’adresse source de chaque paquet transféré avec sa propre adresse privée. Il s’agit du SNAT (source network address translation). Cette fonction permet aux réponses de fonctionner sans modifier le réseau privé : la base de données à 10.0.0.20 répond au VPS, qu’elle sait déjà joindre. En contrepartie, la base de données voit toutes les connexions du tailnet comme provenant du VPS. Les règles de pare-feu par source et les journaux d’accès ne vous fournissent donc aucune information utile.
Désactivez cette fonction sous Linux lorsque vous voulez conserver l’adresse tailnet réelle du client :
sudo tailscale set --snat-subnet-routes=falseLes hôtes du réseau privé ont alors besoin d’une route de retour vers 100.64.0.0/10, la plage que Tailscale attribue aux appareils, en passant par le subnet router. Sans cette route de retour, leurs réponses passent par la passerelle par défaut et n’arrivent jamais à destination. Les connexions restent donc bloquées après le premier paquet. Ajoutez la route statique sur la passerelle du réseau privé, ou laissez le SNAT activé.
Une liaison site à site repose sur deux subnet routers qui effectuent cette opération simultanément. Chacun annonce son propre réseau et accepte celui de l’autre :
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesExécutez la commande correspondante sur l’autre routeur avec sa propre plage. Les deux plages doivent être différentes. Si les transferts volumineux se bloquent alors que ssh et ping fonctionnent, la cause est la MSS (maximum segment size), c’est-à-dire la taille maximale des données transportées par un paquet TCP. La surcharge du tunnel rend les paquets transférés trop volumineux pour une liaison intermédiaire. Le clamping corrige ce problème :
sudo iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuEnregistrez cette règle avec iptables-persistent, sinon elle disparaîtra au prochain démarrage.
Maintenance pour garder le service opérationnel
L’expiration de la clé de cette machine dépend de la manière dont vous l’avez authentifiée. Un nœud configuré avec un tag, comme dans l’exemple autoApprovers ci-dessus, a l’expiration de la clé désactivée par défaut dès son premier ajout et sa première authentification. Cette configuration ne nécessite donc aucune action ici. Un nœud authentifié en tant que vous-même suit la période d’expiration des clés du tailnet. Elle est de 180 jours par défaut sur un nouveau tailnet et peut être définie entre 1 et 180 jours, en octobre 2026. Lorsque la clé d’un subnet router de ce type expire, les connexions vers ce routeur et depuis celui-ci s’arrêtent. Toute la plage annoncée devient alors inaccessible, sans modification de configuration permettant d’expliquer le problème. Pour un routeur authentifié comme un utilisateur, désactivez l’expiration de la clé depuis le menu situé à la fin de sa ligne sur la page Machines de la console d’administration, puis notez que vous l’avez fait.
Tailscale privilégie une connexion directe entre les pairs et utilise ses serveurs relay lorsqu’il ne peut pas en établir une. Les relay fonctionnent, mais ils ajoutent de la latence. Un VPS avec une adresse publique est le cas le plus simple : autorisez les connexions UDP entrantes sur le port 41641 et la plupart des pairs se connecteront directement. Si le trafic vers la plage privée est plus lent que ne devrait le permettre la liaison du VPS, vérifier si votre client atteint directement le subnet router ou passe par un relay est la première chose à contrôler. Si ufw gère le firewall, les règles ufw réellement nécessaires sur un VPS présente la syntaxe.
Les règles d’accès constituent l’autre moitié de la configuration. Sur un tailnet par défaut, chacun de vos appareils peut atteindre tous les autres. Une route approuvée fonctionne donc immédiatement. Dès que vous rédigez une policy ACL, la destination d’une règle doit couvrir la plage privée, soit en la nommant, soit avec le wildcard *. Celui-ci inclut les routes de subnet approuvées, car 10.0.0.20 n’est pas une adresse du tailnet et n’est pas couvert par les règles basées sur des IP ou des tags du tailnet.
Enfin, déterminez si vous acceptez de dépendre d’un serveur de coordination que vous n’administrez pas. Le control plane de Tailscale est un service hébergé. Vos clés restent sur vos machines, mais le compte et le fichier de stratégie sont stockés sur ce service. Avant de lui donner un accès à votre réseau privé, vous devez déterminer ce qu’un control plane compromis ou des identifiants de connexion volés permettraient réellement de faire. Le modèle de confiance de Tailscale précise où se situe cette limite. Le coût pousse rarement les utilisateurs à abandonner ce service, car le forfait gratuit couvre jusqu’à six utilisateurs avec un nombre illimité d’appareils leur appartenant. Toutefois, un subnet router lancé avec un tag est comptabilisé différemment d’un subnet router connecté avec votre compte. Au-delà de cette limite, la facturation dépend du nombre de personnes et non du nombre de machines. Il est donc utile de calculer ce que paieront réellement un foyer ou une équipe de cinq personnes une fois le forfait gratuit épuisé avant d’ajouter le compte qui vous ferait dépasser la limite. L’exécution de Headscale, le serveur de contrôle Tailscale auto-hébergé permet de conserver ce service sur votre propre VPS, mais vous devez en assurer la maintenance. Une autre réponse au même problème consiste à abandonner également les clients Tailscale. L’auto-hébergement du serveur VPN NetBird place la couche de coordination et ses propres clients mesh sur une machine que vous contrôlez. Les deux solutions utilisent WireGuard sous-jacent. En cas de migration, les points qui changent sont donc ceux qu’examine la comparaison entre NetBird et Tailscale : qui administre le control plane, comment rédiger la règle d’accès couvrant votre plage privée et comment le trafic est relayé lorsque les pairs ne peuvent pas se connecter directement. Si vous hésitez encore entre ce modèle et une configuration rédigée manuellement, la comparaison entre WireGuard et Tailscale explique ce que la couche de coordination apporte et ce qu’elle coûte.
FAQ
Quelle est la différence entre un subnet router et un exit node ?
Un subnet router annonce une plage d’adresses privées. Les appareils du tailnet peuvent ainsi atteindre des machines qui n’exécutent pas Tailscale. Un exit node s’annonce comme une route vers l’ensemble d’Internet. Un appareil envoie alors tout son trafic via l’adresse publique de ce nœud. Un même VPS peut remplir les deux rôles. Il s’agit de flags distincts, --advertise-routes et --advertise-exit-node, et chacun nécessite sa propre approbation dans la console d’administration.
Pourquoi mon client Linux ignore-t-il la route de subnet annoncée ?
Les clients Linux n’acceptent pas les routes de subnet tant que vous ne le leur demandez pas. Exécutez sudo tailscale set --accept-routes sur le client. Vérifiez ensuite avec ip route show table 52, et non avec ip route show. Tailscale installe les routes acceptées dans la table de routage 52 et les utilise via des policy rules. La table principale ne les liste donc jamais, ce qui donne l’impression qu’une route fonctionnelle est absente.
Mon subnet ne fonctionne plus après un redémarrage. Que s’est-il passé ?
Il s’agit probablement de l’IP forwarding. Une valeur définie avec sysctl -w ne survit pas à un redémarrage. Écrivez-la donc dans /etc/sysctl.d/99-tailscale.conf et vérifiez-la avec sysctl net.ipv4.ip_forward. Si le forwarding est activé et que la plage reste inaccessible, examinez le nœud dans la console d’administration. Un routeur authentifié comme un utilisateur possède une clé qui expire par défaut après 180 jours. Un subnet router dont la clé a expiré ressemble alors à un problème réseau plutôt qu’à un problème de compte. Pour un routeur configuré avec un tag, l’expiration de la clé est désactivée par défaut. Dans ce cas, cherchez une autre cause.
Deux subnet routers peuvent-ils annoncer la même plage ?
Oui. Des plages identiques permettent de créer une paire à haute disponibilité : les deux routeurs annoncent exactement les mêmes préfixes. Des plages qui se chevauchent avec des longueurs de préfixe différentes fonctionnent également, et le préfixe le plus spécifique est prioritaire. Le failover nécessite toutefois de la prudence : lorsque le routeur qui porte le préfixe le plus spécifique devient indisponible, Tailscale ne bascule pas vers la route plus large. Le trafic concerné s’arrête donc. Pour créer une véritable paire de secours, faites annoncer les mêmes préfixes spécifiques par les deux routeurs.
Le nom d’hôte est résolu, mais la connexion expire. Pourquoi ?
La résolution DNS et le routage sont deux étapes distinctes. Un nom peut être résolu vers une adresse qu’aucune route approuvée ne couvre. Le paquet est alors envoyé via la default gateway du client. Exécutez ip route get <address> sur le client. Si la réponse n’inclut pas dev tailscale0 sur un client Linux, vérifiez d’abord que --accept-routes est activé. Si c’est le cas, annoncez une plage qui couvre cette adresse et approuvez le nouveau préfixe dans la console d’administration.