{"id":9,"date":"2026-03-14T22:57:00","date_gmt":"2026-03-14T22:57:00","guid":{"rendered":"https:\/\/rpi.temporiti.net\/wordpress\/blog\/vpn-mesh-tailscale-infrastruttura\/"},"modified":"2026-05-30T14:05:51","modified_gmt":"2026-05-30T12:05:51","slug":"vpn-mesh-tailscale-infrastruttura","status":"publish","type":"post","link":"https:\/\/rpi.temporiti.net\/wordpress\/?p=9","title":{"rendered":"VPN mesh con Tailscale per la mia infrastruttura"},"content":{"rendered":"<p>Tailscale \u00e8 uno di quegli strumenti che ti fanno cambiare modo di pensare alla rete. Da quando l&#8217;ho introdotto nel mio homelab, ho smesso di configurare port forwarding sul router, certificati Wireguard a mano, regole NAT e DDNS. \u00e8 diventato il tessuto connettivo invisibile fra tutti i miei device, dal portatile in casa fino a un piccolo server ARM su un tier gratuito di un provider cloud.<\/p>\n<h2>Cos&#8217;\u00e8 davvero<\/h2>\n<p>Tailscale \u00e8 una VPN mesh costruita sopra Wireguard. La parte interessante: il traffico non passa per un server centrale (il control plane fa solo coordinamento, distribuendo chiavi e aiutando il NAT traversal). I device si parlano peer-to-peer, cifrati, anche quando sono dietro NAT che normalmente sarebbero impenetrabili.<\/p>\n<p>Ogni device riceve un IP nel range CGNAT <code>100.64.0.0\/10<\/code>, persistente, raggiungibile da ogni altro device della tua tailnet. Niente DNS dinamico, niente &#8220;qual era l&#8217;IP pubblico oggi?&#8221;. Negli esempi user\u00f2 indirizzi generici tipo <code>100.64.1.10<\/code> e <code>100.64.1.20<\/code>, non quelli della mia tailnet.<\/p>\n<h2>Installare<\/h2>\n<p>Su Linux Debian\/Ubuntu\/Raspberry Pi OS:<\/p>\n<pre><code class=\"language-bash\">\ncurl -fsSL https:\/\/tailscale.com\/install.sh | sh\nsudo tailscale up\n<\/code><\/pre>\n<p>Su macOS via brew o dall&#8217;App Store:<\/p>\n<pre><code class=\"language-bash\">\nbrew install --cask tailscale\n<\/code><\/pre>\n<p>Su Mac la app gira come menu bar item e si autentica con SSO al primo lancio.<\/p>\n<p>Su server headless preferisco il flag <code>--ssh<\/code>, cos\u00ec posso usare l&#8217;auth Tailscale invece di gestire chiavi SSH separate:<\/p>\n<pre><code class=\"language-bash\">\nsudo tailscale up --ssh --advertise-tags=tag:server\n<\/code><\/pre>\n<p>Al primo <code>up<\/code>, apre un URL nel browser che porta alla pagina di Tailscale per autenticarsi (Google, Microsoft, GitHub o passkey). Da quel momento il device \u00e8 nella tailnet.<\/p>\n<h2>ACL e tag<\/h2>\n<p>Tutti i device nella stessa tailnet di default possono parlarsi tutti con tutti. Per un homelab serio non basta, e l&#8217;ACL via tag \u00e8 la prima cosa che configuro. Esempio di policy nella console web:<\/p>\n<pre><code class=\"language-json\">\n{\n  \"tagOwners\": {\n    \"tag:server\":   [\"autogroup:admin\"],\n    \"tag:laptop\":   [\"autogroup:admin\"],\n    \"tag:iot\":      [\"autogroup:admin\"]\n  },\n  \"acls\": [\n    { \"action\": \"accept\", \"src\": [\"tag:laptop\"], \"dst\": [\"tag:server:22,80,443,8086\"] },\n    { \"action\": \"accept\", \"src\": [\"tag:server\"], \"dst\": [\"tag:server:*\"] }\n  ]\n}\n<\/code><\/pre>\n<p>I device taggati <code>tag:iot<\/code> non possono parlare con i server, ma solo con il gateway IoT dedicato. Il portatile arriva ai server solo su SSH, HTTP, HTTPS e InfluxDB. I server si parlano fra loro liberamente. Una compromissione su un device IoT \u00e8 contenuta entro il suo segmento logico.<\/p>\n<h2>Subnet router e exit node<\/h2>\n<p>Quando ho un dispositivo che non posso o non voglio mettere in tailnet (vecchio NAS, stampante, switch), faccio annunciare la sua subnet a un Pi che \u00e8 in tailnet:<\/p>\n<pre><code class=\"language-bash\">\nsudo tailscale up --advertise-routes=198.51.100.0\/24 --advertise-tags=tag:server\n<\/code><\/pre>\n<p>Dalla console web approvo la route. Da quel momento ogni device in tailnet pu\u00f2 raggiungere quelle macchine direttamente per IP, senza che abbiano installato Tailscale loro stessi. Stesso meccanismo per l&#8217;exit node: un nodo in cloud annunciato come exit, e il portatile in viaggio ci dirige tutto il traffico per uscire da IP fissi.<\/p>\n<h2>Un caso reale<\/h2>\n<p>Una sera tardi, intorno alle 22:00, un familiare a 800 km mi ha chiesto aiuto con il suo piccolo homelab: un Raspberry Pi che faceva da Home Assistant non rispondeva pi\u00f9 dall&#8217;esterno. Per anni avevamo gestito quel collegamento con port forwarding e DDNS, e ovviamente qualcosa nel router gli era cambiato dopo un update firmware. Invece di guidarlo passo passo in chiamata, gli ho installato Tailscale dal terminale con un <code>curl ... | sh<\/code> e un <code>tailscale up<\/code>. In tre minuti il suo Pi era nella tailnet, ho potuto fare SSH come se fosse nella mia stessa LAN, e ho fixato la sua configurazione in altri cinque. Da quel giorno il port forwarding sul suo router \u00e8 disattivato e tutte le interazioni passano per la tailnet, cifrate e segmentate dalle ACL.<\/p>\n<h2>Cosa funziona bene<\/h2>\n<p>Tailscale taglia gli ostacoli di rete in modo sorprendente: NAT, CGNAT del provider, IPv6 incompatibile fra peer, non sono pi\u00f9 problemi. MagicDNS d\u00e0 nomi friendly ai device. Il free plan personale ha limiti pi\u00f9 che generosi per un homelab (100 device, ACL illimitate). L&#8217;integrazione con SSO permette di legare l&#8217;identit\u00e0 a Google\/Microsoft, e si revoca un device con un click se finisce per terra in tram.<\/p>\n<h2>Limiti<\/h2>\n<p>\u00e8 un servizio proprietario centralizzato per il control plane: se Tailscale Inc. va offline, le nuove connessioni non partono (quelle stabilite continuano per un po&#8217;). Per chi vuole sovranit\u00e0 totale c&#8217;\u00e8 <a href=\"https:\/\/github.com\/juanfont\/headscale\">Headscale<\/a>, un control plane open source compatibile, che valuto periodicamente. Le ACL sono potenti ma vanno scritte con cura, perch\u00e9 un errore pu\u00f2 aprire flussi non voluti fra tag.<\/p>\n<h2>In pratica<\/h2>\n<p>Tailscale \u00e8 il sistema nervoso del mio homelab distribuito. Senza di lui dovrei gestire VPN site-to-site, certificati Wireguard, regole iptables a mano e DDNS. Con lui, la rete \u00e8 semplicemente &#8220;tutto vede tutto secondo ACL&#8221;, e posso concentrarmi sul resto. \u00e8 uno dei pochi servizi che, se sparisse domani, mi creerebbe pi\u00f9 problemi che la perdita di un server fisico.<\/p>\n<hr>\n<blockquote>\n<p>Immagine generata con ComfyUI Mac M1 \/ RealVisXL V5 Lightning.<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Tailscale \u00e8 uno di quegli strumenti che ti fanno cambiare modo di pensare alla rete. Da quando l&#8217;ho introdotto nel mio homelab, ho smesso di configurare port forwarding sul router, certificati Wireguard a mano, regole NAT e DDNS. \u00e8 diventato il tessuto connettivo invisibile fra tutti i miei device, dal portatile in casa fino a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":188,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-9","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vpn-e-networking"],"_links":{"self":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/9","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9"}],"version-history":[{"count":10,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/9\/revisions"}],"predecessor-version":[{"id":358,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/9\/revisions\/358"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/media\/188"}],"wp:attachment":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}