Pubblicare un server domestico su Internet una volta significava soprattutto configurare il router: si assegnava un indirizzo locale stabile alla macchina, si aprivano le porte necessarie con il port forwarding e, se l’IP pubblico cambiava, un servizio DDNS (Dynamic DNS) aggiornava automaticamente l’associazione tra IP e nome di dominio.
Purtroppo, per alcuni utenti, quella procedura oggi non funziona più. La progressiva scarsità degli indirizzi IPv4 ha spinto numerosi operatori a utilizzare il Carrier-Grade NAT o CGNAT, condividendo un singolo indirizzo IPv4 pubblico tra più clienti. Il router di casa non controlla quindi l’indirizzo visibile su Internet e non può decidere dove inoltrare una connessione in ingresso.
Il problema interessa direttamente chi vuole gestire un laboratorio personale, un server Nextcloud, un’istanza Matrix, un servizio SSH o qualsiasi altra applicazione self-hosted.
Una soluzione elegante consiste nel capovolgere il percorso: la macchina dietro CGNAT apre una connessione verso un piccolo VPS (Virtual Private Server) dotato di IP pubblico. Il VPS diventa il punto d’ingresso (ed è oggi possibile attivare un VPS con configurazione hardware modesta per pochi spiccioli…); un tunnel WireGuard trasporta poi il traffico fino alla rete di casa.
Perché il port forwarding smette di funzionare con CGNAT
Con una normale connessione IPv4 pubblica, il router possiede sull’interfaccia WAN un indirizzo raggiungibile da Internet. Una regola NAT può quindi dire, per esempio, che i pacchetti destinati alla porta TCP 443 debbano raggiungere, per esempio, il server locale 192.168.1.20. Il router riceve davvero quei pacchetti e ha la possibilità di modificarne la destinazione.
CGNAT aggiunge invece un ulteriore livello di traduzione all’interno della rete dell’operatore. Il router dell’abbonato riceve un indirizzo non instradabile pubblicamente; molto spesso gli operatori usano lo spazio 100.64.0.0/10, riservato dalla RFC 6598 proprio alle reti condivise di questo tipo. L’indirizzo IPv4 pubblico appartiene al dispositivo CGN (Carrier Grade NAT) del provider Internet e serve contemporaneamente diversi clienti.
In pratica ci sono due NAT: quello del router domestico e quello dell’operatore. Aprire una porta sul primo non modifica il secondo: il traffico proveniente da Internet arriva al CGN del provider, che non possiede una mappatura capace di associarlo al router dell’utente.
RFC 6888 prevede anche meccanismi con cui un CGN può consentire al cliente di controllare determinate mappature, per esempio tramite PCP, Port Control Protocol. Nella pratica, però, non bisogna dare per scontato che l’operatore esponga questa possibilità. Per chi deve pubblicare stabilmente servizi Internet serve quindi un’altra strada.
Il trucco: la connessione parte dalla rete domestica
Come anticipato nell’introduzione, la soluzione alla fine è semplice: CGNAT crea problemi soprattutto alle connessioni iniziate dall’esterno, non a quelle in uscita. Il server di casa può normalmente collegarsi a un VPS pubblico su Internet, esattamente come può raggiungere qualsiasi altro server.
WireGuard sfrutta questa possibilità creando un’interfaccia virtuale, per esempio wg0, attraverso la quale passano pacchetti IP cifrati dentro datagrammi UDP. I due estremi possiedono chiavi pubbliche e private; WireGuard associa inoltre a ciascun peer gli indirizzi che può inviare o ricevere attraverso il tunnel mediante AllowedIPs.
Supponiamo che il VPS utilizzi l’indirizzo WireGuard 10.0.0.1/24, mentre la rete domestica 10.0.0.2/24. Il VPS ascolta sulla porta UDP 51820; la macchina domestica conosce invece l’indirizzo pubblico del VPS e apre autonomamente il tunnel verso quell’endpoint.
WireGuard normalmente evita di generare traffico quando non serve; dietro NAT, però, una lunga inattività può far scadere la mappatura UDP conservata dal router o dal CGN: un keepalive ogni 25 secondi (PersistentKeepalive = 25) mantiene aperta quella associazione.
È utile distinguere l’architettura descritta da soluzioni come nginx Proxy Manager, Traefik o un tradizionale reverse proxy HTTP. Qui il VPS lavora a livello IP: non deve comprendere HTTP, TLS, SSH o il protocollo applicativo trasportato.
Come configurare VPS e server domestico
Passiamo alla parte pratica. Servono due macchine Linux: il VPS, raggiungibile da Internet con un vero indirizzo IPv4 pubblico, e il server domestico collocato dietro CGNAT. Nell’esempio useremo Ubuntu o Debian su entrambi i sistemi, anche se gli stessi concetti valgono per altre distribuzioni.
Supponiamo inoltre che il VPS abbia indirizzo pubblico 203.0.113.10. All’interno del tunnel assegneremo 10.0.0.1 al VPS e 10.0.0.2 al server domestico. Sono indirizzi privati utilizzati soltanto da WireGuard: non devono coincidere con la rete LAN di casa. Se, per esempio, la LAN utilizza già 10.0.0.0/24, è meglio scegliere un’altra sottorete, come 10.200.200.0/24.
Il primo passo consiste nell’installare WireGuard su entrambe le macchine:
sudo apt update && sudo apt install wireguard
Vanno quindi generate una coppia di chiavi sul VPS e una sul server domestico. Su ciascuna macchina si può eseguire:
umask 077
wg genkey | tee privatekey | wg pubkey > publickey
Il file privatekey deve rimanere segreto. Il contenuto di publickey, invece, va copiato sulla macchina opposta. WireGuard usa infatti la chiave pubblica del peer per stabilire quali pacchetti accettare e cifrare.
Configurare WireGuard sul VPS pubblico
Sul VPS si crea il file /etc/wireguard/wg0.conf. Una configurazione minima, adattata all’esempio, può essere la seguente:
[Interface]
Address = 10.0.0.1/24
PrivateKey = CHIAVE_PRIVATA_VPS
ListenPort = 51820
[Peer]
PublicKey = CHIAVE_PUBBLICA_SERVER_CASA
AllowedIPs = 10.0.0.2/32
CHIAVE_PRIVATA_VPS va sostituita con il contenuto del file privatekey generato sul VPS; CHIAVE_PUBBLICA_SERVER_CASA con la chiave pubblica prodotta sulla macchina domestica.
Il significato di AllowedIPs = 10.0.0.2/32 è importante: WireGuard sa che l’indirizzo 10.0.0.2 appartiene a quel peer e deve quindi essere raggiunto attraverso il tunnel.
Prima di inoltrare traffico tra Internet e WireGuard bisogna inoltre abilitare il routing IPv4 nel kernel Linux del VPS. Si può farlo immediatamente con:
sudo sysctl -w net.ipv4.ip_forward=1
Per rendere permanente l’impostazione, conviene creare ad esempio /etc/sysctl.d/99-wireguard-forward.conf con:
net.ipv4.ip_forward=1
e quindi applicarla con:
sudo sysctl --system
Configurare il server di casa dietro CGNAT
Sulla macchina domestica il file /etc/wireguard/wg0.conf assume invece una forma diversa:
[Interface]
Address = 10.0.0.2/24
PrivateKey = CHIAVE_PRIVATA_SERVER_CASA
Table = off
PostUp = ip route add default dev wg0 table 200
PostUp = ip rule add from 10.0.0.2 table 200
PostDown = ip rule del from 10.0.0.2 table 200
PostDown = ip route del default dev wg0 table 200
[Peer]
PublicKey = CHIAVE_PUBBLICA_VPS
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Qui Endpoint contiene l’indirizzo IPv4 pubblico del VPS: come chiarito in precedenza, è il server di casa a iniziare la comunicazione verso Internet. Ed è proprio ciò che permette di superare CGNAT senza configurare alcuna porta in ingresso sul router domestico.
La parte meno intuitiva è invece composta da Table = off, ip route e ip rule. Non vogliamo infatti che tutto il traffico Internet generato normalmente dal server domestico passi attraverso il VPS. Vogliamo soltanto che le risposte alle connessioni ricevute sull’indirizzo WireGuard 10.0.0.2 ritornino attraverso lo stesso tunnel.
La tabella di routing 200 contiene quindi una route predefinita verso wg0; la regola ip rule add from 10.0.0.2 table 200 dice al kernel: “se il pacchetto parte da 10.0.0.2, consulta la tabella 200“. Il traffico normale della macchina, che utilizza per esempio l’indirizzo LAN 192.168.1.20, continua invece a uscire dal router di casa.
Avviare il tunnel e controllare che funzioni
A questo punto WireGuard può essere attivato su entrambe le macchine:
sudo systemctl enable --now wg-quick@wg0
Il comando:
sudo wg show
deve mostrare il peer, la chiave pubblica remota e soprattutto una voce latest handshake aggiornata. Se l’handshake non compare, i primi elementi da controllare sono la porta UDP 51820 sul firewall del VPS, l’indirizzo inserito in Endpoint e le chiavi dei due peer.
Dal VPS dovrebbe inoltre funzionare il comando seguente: ping 10.0.0.2
Così come il seguente dalla macchina domestica: ping 10.0.0.1
Se i due host si raggiungono attraverso questi indirizzi, il tunnel di base funziona. Non abbiamo ancora pubblicato nulla su Internet: abbiamo soltanto creato il collegamento privato VPS-casa.
Far arrivare sul server di casa le richieste di connessione ricevute dal VPS
Sul VPS bisogna a questo punto fare in modo che il traffico proveniente dall’interfaccia pubblica sia girato su 10.0.0.2. Supponiamo che l’interfaccia pubblica del VPS si chiami ens3. Il nome reale si può verificare con il comando ip addr oppure con ip route
La configurazione presentata di seguito mantiene sul VPS due porte: UDP 51820 per WireGuard e TCP 2222 per amministrare il VPS via SSH. Tutto il resto è automaticamente inoltrato al server domestico:
sudo iptables -t nat -A PREROUTING -i ens3 -p udp --dport 51820 -j RETURN
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 2222 -j RETURN
sudo iptables -t nat -A PREROUTING -i ens3 -j DNAT --to-destination 10.0.0.2
sudo iptables -A FORWARD -i wg0 -o ens3 -s 10.0.0.2 -j ACCEPT
sudo iptables -A FORWARD -i ens3 -o wg0 -d 10.0.0.2 -j ACCEPT
La sequenza va letta dall’alto verso il basso. Se arriva un pacchetto UDP sulla porta 51820, rimane sul VPS perché serve a WireGuard. Se arriva una connessione TCP sulla porta 2222, rimane anch’essa sul VPS perché serve per SSH. Qualunque altro pacchetto ricevuto su ens3 subisce invece un DNAT (Destination Network Address Translation): la destinazione cambia dall’IP pubblico del VPS a 10.0.0.2.
Le ultime due regole autorizzano il forwarding tra l’interfaccia pubblica e wg0. Senza queste, il kernel potrebbe conoscere perfettamente la route ma il firewall impedirebbe comunque ai pacchetti di attraversare la macchina.
Un esempio concreto: pubblicare SSH e un server Web
Supponiamo che sulla macchina domestica siano in ascolto SSH sulla porta 22 e nginx sulle porte 80 e 443. Dopo aver applicato le regole precedenti accade qualcosa di molto semplice. Un utente remoto usa il comando seguente:
ssh utente@203.0.113.10
La connessione TCP arriva sulla porta 22 del VPS. Non esiste alcuna eccezione per quella porta, quindi il DNAT cambia la destinazione in 10.0.0.2. Il pacchetto attraversa wg0 e arriva all’SSH server domestico.
Se invece l’amministratore volesse accedere direttamente al VPS tramite SSH userebbe:
ssh -p 2222 utente@203.0.113.10
La regola intercetta la porta 2222 e lascia terminare la connessione sul VPS.
Lo stesso avviene per HTTP e HTTPS. Inserendo nel DNS del proprio dominio un record A che punta a 203.0.113.10, una richiesta a https://server.example.com arriva sulla porta 443 del VPS, entra nel tunnel e raggiunge nginx sulla macchina domestica.
Non occorre configurare alcun port forwarding sul router di casa. Non serve neppure conoscere l’indirizzo IPv4 assegnato dal provider alla connessione domestica: dal punto di vista di WireGuard conta soltanto che il server interno riesca ad aprire una sessione UDP in uscita verso il VPS.
Attenzione: inoltrare tutte le porte non è sempre la scelta migliore
L’esempio inoltra praticamente tutto il traffico del VPS alla macchina domestica. È una configurazione comoda per illustrare il meccanismo, ma in produzione può essere preferibile limitare l’esposizione alle sole porte necessarie.
Se, per esempio, se volessimo esporre solo ed esclusivamente HTTP, HTTPS e SSH, possiamo sostituire il DNAT generale con tre regole specifiche:
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 22 -j DNAT --to-destination 10.0.0.2:22
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:80
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.2:443
È una scelta che rende più evidente quali servizi risultano realmente esposti. Inoltre evita di rendere accidentalmente raggiungibile da Internet un daemon avviato successivamente sulla macchina domestica.
La “regola generale” può quindi diventare questa:
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 22 -j DNAT --to-destination 10.0.0.2:22
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:80
sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.2:443
sudo iptables -A FORWARD -i ens3 -o wg0 -p tcp -d 10.0.0.2 --dport 22 -j ACCEPT
sudo iptables -A FORWARD -i ens3 -o wg0 -p tcp -d 10.0.0.2 --dport 80 -j ACCEPT
sudo iptables -A FORWARD -i ens3 -o wg0 -p tcp -d 10.0.0.2 --dport 443 -j ACCEPT
sudo iptables -A FORWARD -i wg0 -o ens3 -s 10.0.0.2 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Va verificato anche il firewall del server di casa: se ufw, nftables o un altro firewall bloccasse la porta 443 sull’interfaccia wg0, il DNAT del VPS funzionerebbe regolarmente ma il servizio continuerà a risultare irraggiungibile.
Come verificare dove si blocca il traffico
Una volta configurato tutto, conviene procedere per gradi. wg show deve prima confermare l’handshake WireGuard. Poi il VPS deve riuscire a raggiungere 10.0.0.2. Infine si prova una porta dall’esterno, preferibilmente da una rete diversa dalla LAN domestica, per esempio tramite la connessione cellulare dello smartphone.
Se HTTPS non risponde, sul VPS è utile osservare i pacchetti con:
sudo tcpdump -ni ens3 tcp port 443
e contemporaneamente:
sudo tcpdump -ni wg0 tcp port 443
Se i pacchetti compaiono su ens3 ma non su wg0, bisogna controllare DNAT, forwarding e firewall del VPS. Se arrivano su wg0 ma il server non risponde, il problema è invece probabilmente sulla macchina domestica o sul servizio applicativo.
Un ulteriore controllo utile sul server di casa è:
ip rule show
ip route show table 200
Bisogna trovare la regola relativa alla sorgente 10.0.0.2 e una route default dev wg0 nella tabella 200. Senza quel percorso di ritorno la richiesta può arrivare correttamente a casa, ma la risposta prova a uscire dal normale gateway della LAN e la sessione non si completa.
Il VPS diventa una parte critica dell’infrastruttura
Eliminare il problema CGNAT non elimina i punti di guasto, bensì li sposta.
Se cade la macchina domestica, i servizi scompaiono anche se il VPS continua a rispondere. Se invece si guasta il VPS, tutti i servizi pubblicati attraverso quell’indirizzo diventano irraggiungibili. Cloudflare Tunnel segue una filosofia compatibile con le reti sotto CGNAT: il daemon installato internamente apre connessioni soltanto in uscita verso Cloudflare e non richiede un indirizzo pubblico né porte in ingresso aperte sul router.
Tailscale rappresenta un’altra possibilità per l’accesso amministrativo. Prova a creare connessioni dirette tra i peer attraversando i NAT; quando la rete non lo consente, può usare relay DERP per trasportare pacchetti WireGuard già cifrati. Per un canale di soccorso destinato agli amministratori è spesso più semplice di un secondo VPS completo.
Oltretutto, dagli stessi autori di Tailscale, c’è anche Tailcat che collega due sistemi remoti senza VPN e senza aprire alcuna porta.
Prestazioni: il prezzo reale del bridge
Con la configurazione descritta, il traffico compie necessariamente un percorso più lungo: client, VPS, tunnel WireGuard, casa e poi ritorno. La latenza aggiuntiva dipende quindi soprattutto dalla posizione geografica del VPS rispetto alla connessione domestica.
Per un sito Web, Nextcloud o molti servizi amministrativi una manciata di millisecondi in più può essere un costo accettabile; per applicazioni interattive molto sensibili alla latenza, gaming o flussi real-time, la scelta del datacenter (fisicamente molto vicino) diventa più delicata.
Anche la banda merita attenzione: il VPS deve sostenere il traffico pubblico dei servizi e il server di casa deve disporre di sufficiente capacità in upload. Una fibra FTTH simmetrica si presta molto meglio a un uso di questo tipo rispetto a connessioni con upload fortemente limitato.
WireGuard aggiunge cifratura e incapsulamento, ma usa primitive crittografiche progettate anche per prestazioni elevate e su Linux opera tramite un’interfaccia di rete integrata strettamente con lo stack del kernel. Su hardware recente il collo di bottiglia, in molti casi, sarà più probabilmente la connessione Internet o la capacità del VPS che non la cifratura del tunnel.