Contexte :
J’avais installé un VPN Wireguard (voir l’article) dans une VPS Azure pour accéder à mon homelab. Dans cette configuration, j’avais un VPS à payer chaque mois même si je ne l’utilisais pas tout le temps. J’ai découvert la solution Tailscale entre-temps. Tailscale est une solution basée sur Wireguard qui permet de ne plus avoir à installer son propre VPN et de ne pas maintenir une VPS. Tailscale propose une offre gratuite très généreuse dans laquelle on peut connecter jusqu’à 100 clients (téléphones, VPS, …).
Installation
Après avoir créé un compte, il suffit de suivre les instructions selon son client.
Sous Linux :
sudo curl -fsSL https://tailscale.com/install.sh | sh
sudo systemctl start tailscaled
Lorsqu’on se connecte pour la première fois, un réseau privé nommé Tailnet est créé. Chaque machine connectée se voit attribuer une adresse IP dans la plage 100.x.y.z (Carrier Grade NAT du RFC6598). Des ACL (Access Control Lists) peuvent être appliquées pour limiter les accès aux machines. Chaque Tailnet a un nom et un nom d’organisation : tail0ec5d.ts.net dans mon cas. Le nom du Tailnet est utilisé par Tailscale pour associer des noms DNS à toutes les machines ajoutées. C’est ainsi que le nom DNS associé à la machine jumpbox-router est jumpbox-router.tail0ec5d.ts.net.
Ajout d’une machine
Il existe plusieurs manières d’ajouter une machine à son compte Tailscale. Pour ma part, j’utilise une clé d’authentification (auth Key) à générer dans les paramètres.
Une fois la clé récupérée, voici la commande sous Linux :
sudo tailscale up --authkey tskey-auth-kfjJWLbavE11CNTRL-uahnSug9QuGd2jg4efS2uGnGy4jWVRZh
Toutes les machines ajoutées dans notre compte Tailscale reçoivent des adresses IP dans le réseau Tailnet qui nous est attribué.
root@CT109:~# ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0@if49: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link-netnsid 0
inet 192.168.1.66/24 brd 192.168.1.255 scope global eth0
valid_lft forever preferred_lft forever
3: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc pfifo_fast state UNKNOWN group default qlen 500
inet 100.90.231.57/32 scope global tailscale0
valid_lft forever preferred_lft forever
On peut voir nos machines connectées dans le tableau de bord.
Les machines connectées étant dans un même réseau 100.x.y.z, elles peuvent communiquer indépendamment de leur localisation.
root@CT109:~# ping -c1 jumpbox-router
PING jumpbox-router.tail0ec5d.ts.net (100.126.92.82) 56(84) bytes of data.
64 bytes from jumpbox-router.tail0ec5d.ts.net (100.126.92.82): icmp_seq=1 ttl=64 time=1.82 ms
--- jumpbox-router.tail0ec5d.ts.net ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.821/1.821/1.821/0.000 ms
Ajout d’une machine en tant que routeur
En mode routeur, Tailscale permet de connecter plusieurs réseaux. Pour l’illustration, j’ambitionne d’installer une stack d’observabilité (Grafana, Prometheus, Loki, Jaeger, …) dans mon homelab (réseau 192.168.1.0/24). L’idée est de remonter les logs, les traces et les métriques de tout ce que j’installe (VMs Azure, AWS, cluster Kubernetes pour des clients, …) vers la stack d’observabilité dans mon Homelab qui n’a pas d’adresse IP publique. Pour montrer cette configuration, je vais juste installer une VM sur Azure et faire un curl depuis cette VM.
Connecter le conteneur ct109 du homelab à Tailscale comme routeur
Activer le routage d’IP sous Linux :
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.conf
firewall-cmd --permanent --add-masquerade # si firewalld est installé et actif
Spécifier les réseaux vers lesquels le routage est autorisé, celui de mon homelab dans mon cas :
sudo tailscale up --advertise-routes=192.168.1.0/24 --authkey tskey-auth-kfjJWLbavE11CNTRL-uahnSug9QuGd2jg4efS2uGnGy4jWVRZh
Il faut ensuite se connecter dans la console Tailscale pour approuver (cliquer sur View à côté de Subnet) le réseau 192.168.1.0/24 dans les détails de la machine (CT109 dans mon cas).
Connecter la vm Azure et tester la connection vers les IPs du Homelab
Ma VM Azure a été crée dans la zone Virginie (East US) et son IP publique est 57.151.88.211
La commande pour la rajouté à notre réseau Tailscale.
sudo tailscale up --accept-routes --authkey tskey-auth-kfjJWLbavE11CNTRL-uahnSug9QuGd2jg4efS2uGnGy4jWVRZh
Tester un ping vers un serveur web du Homelab qui n’est pas connecté à Tailscale via son IP dans le réseau local du Homelab 192.168.1.62 :
azureuser@demo-tailscale:~$ ping -c1 192.168.1.62
PING 192.168.1.62 (192.168.1.62) 56(84) bytes of data.
64 bytes from 192.168.1.62: icmp_seq=1 ttl=63 time=205 ms
--- 192.168.1.62 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 205.196/205.196/205.196/0.000 ms
Dans ce deuxième cas
Tester un ping vers le conteneur ct109 dans mon homelab
azureuser@demo-tailscale:~$ ping -c1 ct109
PING ct109.tail0ec5d.ts.net (100.90.231.57) 56(84) bytes of data.
64 bytes from ct109.tail0ec5d.ts.net (100.90.231.57): icmp_seq=1 ttl=64 time=89.7 ms
--- ct109.tail0ec5d.ts.net ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 89.651/89.651/89.651/0.000 ms
On peut remarquer que le nom du conteneur a été résolu par Tailscale en son IP. Voici le contenu du fichier /etc/resolv.conf de la VM Azure :
azureuser@demo-tailscale:~$ tail -3 /etc/resolv.conf
nameserver 127.0.0.53
options edns0 trust-ad
search wy1gvedgxzgubguhjaf2spv2kb.bx.internal.cloudapp.net tail0ec5d.ts.net
Tester un ping vers une IP la livebox Orange de mon LAN
Dans les sections précédentes, le conteneur ct109, en plus d’être dans mon réseau local 192.168.1.0/24, a été configuré comme routeur (option –advertise-routes). Ceci implique que toutes les machines connectées à mon réseau Tailscale qui acceptent d’utiliser les routes déclarées (option –accept-routes) peuvent se connecter à tout mon réseau local 192.168.1.0/24. Voici le test d’un ping vers ma Livebox Orange qui n’est pas connectée à Tailscale via son IP 192.168.1.1 depuis la VM Azure :
azureuser@demo-tailscale:~$ ping -c1 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=63 time=342 ms
--- 192.168.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 342.201/342.201/342.201/0.000 ms
Un curl vers l’interface admin de ma Livebox Orange depuis la VM Azure où l’on voit bien “Livebox Configurator” dans la balise meta :
azureuser@demo-tailscale:~$ curl -k https://192.168.1.1
<!DOCTYPE html>
<!-- /ht Paul Irish - http://front.ie/j5OMXi -->
<!--[if lt IE 7 ]> <html class="no-js ie6" lang="fr"> <![endif]-->
<!--[if IE 7 ]> <html class="no-js ie7" lang="fr"> <![endif]-->
<!--[if IE 8 ]> <html class="no-js ie8" lang="fr"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!-->
<html class="no-js" lang="fr">
<!--<![endif]-->
<head>
<title data-translation="common.headtitlelogin"></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="x-dns-prefetch-control" content="off">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="cleartype" content="on">
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="description" content="Livebox Configurator">
<meta name="author" content="SoftAtHome">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<!-- CSS files from wm.min.css -->
<script>
if(!(window.console && console.log)) {
console = {
log: function(){},
debug: function(){},
info: function(){},
warn: function(){},
error: function(){}
};
}
</script>
<script data-main="loader.js?sah=v1.0.20_20240226" src="sdkut/ext/requireJS_2.1.15/require.min.js?sah=v1.0.20_20240226"></script>
</head>
<body>
</body>
</html>
Ajout d’une machine en tant qu’ExitNode
Par défaut, Tailscale agit comme un réseau overlay : il achemine uniquement le trafic entre les appareils utilisant Tailscale, mais ne touche pas votre trafic Internet public, comme lorsque vous visitez Google ou Twitter. En utilisant un ExitNode, tout le trafic Internet sera routé via le nœud Tailscale. Pour l’illustration, je suis parti en vacances à Dakar et je voulais suivre des films et séries sur la plateforme TF1 qui n’est pas accessible au Sénégal. Une connexion de mon Mac à mon réseau Tailscale avec une redirection de tout le trafic Internet via l’ExitNode de mon Homelab en France m’a permis de suivre mes films. Comme autre exemple, vous pouvez utiliser ce système pour télé-travailler depuis n’importe où dans le monde en faisant croire que vous êtes chez vous (mentir, c’est mal 😅) . Pour la pratique nous allons utiliser notre VM Azure créée en Amérique du Nord pour faire sortir notre flux Internet en Virginie (East US).
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.conf
sudo tailscale up --accept-routes --authkey tskey-auth-kfjJWLbavE11CNTRL-uahnSug9QuGd2jg4efS2uGnGy4jWVRZh --advertise-exit-node
Il faut ensuite se connecter dans le tableau de bord Tailscale pour valider le nœud comme ExitNode. Cliquez sur “Edit” dans la section “Routing Settings” et approuvez.
Utiliser l’exitNode avec un client graphique
Avec les clients graphiques Tailscale (Mac, Windows, Android, iOS), il suffit de choisir l’ExitNode dans le menu pour l’activer. Voici un exemple sur mon Mac :
En choisissant la VM Azure (East US) comme ExitNode et en allant sur le site whatismyipaddress.com , on voit:
Utiliser l’exitNode en ligne de commande
Pour utiliser l’ExitNode dans mon conteneur ct109, je le spécifie avec l’option –exit-node :
sudo tailscale up --authkey tskey-auth-kfjJWLbavE11CNTRL-uahnSug9QuGd2jg4efS2uGnGy4jWVRZh --exit-node=demo-tailscale --reset
Pour tester l’ExitNode sur le conteneur ct109 qui est dans mon homelab :
root@CT109:~# curl ipconfig.sh
57.151.88.211
root@CT109:~# curl https://ipinfo.io/57.151.88.211
{
"ip": "57.151.88.211",
"city": "Ashburn",
"region": "Virginia",
"country": "US",
"loc": "39.0437,-77.4875",
"org": "AS8075 Microsoft Corporation",
"postal": "20147",
"timezone": "America/New_York",
"readme": "https://ipinfo.io/missingauth"
Un curl sur ipconfig.sh permet de récupérer l’adresse IP du routeur utilisé vers Internet. Un curl ipinfo.io nous donne des infos sur l’IP récupérée. On peut remarquer que c’est l’IP de ma VM Azure et que selon ipinfo.io mon conteneur ct109 est localisé en Virginie au Etats-unis.
Gestion des erreurs
Description de l’erreur
Ayant un homelab sous Proxmox, à part pour mes clusters Kubernetes, je préfère démarrer des conteneurs LXC pour optimiser les ressources. En utilisant un conteneur LXC avec un template ubuntu-22.04-standard_22.04-1_amd64.tar.zst pour mon nœud Tailscale, j’ai rencontré l’erreur suivante quand je démarre le service tailscaled :
May 14 05:58:03 CT109 systemd[1]: Started Tailscale node agent.
May 14 05:58:03 CT109 tailscaled[1006]: is CONFIG_TUN enabled in your kernel? `modprobe tun` failed with: modprobe>
May 14 05:58:03 CT109 tailscaled[1006]: tun module not loaded nor found on disk
May 14 05:58:03 CT109 tailscaled[1006]: wgengine.NewUserspaceEngine(tun "tailscale0") error: tstun.New("tailscale0>
May 14 05:58:03 CT109 tailscaled[1006]: flushing log.
May 14 05:58:03 CT109 tailscaled[1006]: logger closing down
May 14 05:58:03 CT109 tailscaled[1006]: getLocalBackend error: createEngine: tstun.New("tailscale0"): CreateTUN("t>
May 14 05:58:03 CT109 systemd[1]: tailscaled.service: Main process exited, code=exited, status=1/FAILURE
May 14 05:58:03 CT109 tailscaled[1043]: logtail started
May 14 05:58:03 CT109 tailscaled[1043]: Program starting: v1.66.1-t8494e1762-g270a880d5, Go 1.22.3: []string{"/usr>
May 14 05:58:03 CT109 tailscaled[1043]: LogID: 14f304627d4ad06b1a48516576b30c59eddadaaea4bb024437970376b691f7e6
May 14 05:58:03 CT109 tailscaled[1043]: logpolicy: using $STATE_DIRECTORY, "/var/lib/tailscale"
May 14 05:58:03 CT109 tailscaled[1043]: dns: [resolved-ping=yes rc=unknown ret=direct]
May 14 05:58:03 CT109 tailscaled[1043]: dns: using "direct" mode
May 14 05:58:03 CT109 tailscaled[1043]: dns: using *dns.directManager
May 14 05:58:03 CT109 tailscaled[1043]: flushing log.
May 14 05:58:03 CT109 tailscaled[1043]: logger closing down
tailscaled.service: Failed with result 'exit-code'.
En gros, le module tun n’est pas chargé dans le noyau Linux.
Résolution de l’erreur
Vous pouvez remarquer dans les logs que l’ID (CT109) de mon conteneur est 109. Après quelques recherches sur des forums et connaissant l’ID de mon conteneur, je vous présente ma solution. Connectez-vous en SSH au serveur Proxmox sur lequel le conteneur a été créé, éditez son fichier de description (/etc/pve/lxc/109.conf dans mon cas) puis ajoutez les lignes suivantes à la fin du fichier.
lxc.cgroup.devices.allow: c 10:200 rwm
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net dev/net none bind,create=dir
Sauvegardez le fichier puis redémarrez votre conteneur et le tour est joué.
Conclusion
Tailscale est une solution qui permet d’avoir un VPN basé sur Wireguard sans avoir à gérer un serveur VPN. En adoptant Tailscale, j’ai pu transformer mon homelab en un environnement de travail sécurisé et accessible de n’importe où dans le monde. Que ce soit pour le télétravail, la surveillance à distance ou l’accès transparent à des ressources internes, Tailscale offre une connectivité fluide et sécurisée, libérant ainsi des contraintes traditionnelles liées aux VPN.