WireGuard è veloce. In produzione, però, i problemi arrivano quando usi split routing, DNS interni e più client sulla stessa macchina o sullo stesso profilo utente.
Lo scenario di questo articolo è concreto: un server Debian 12 con accesso a due reti diverse, una VPN per amministrazione e una per traffico applicativo, più client multipli su laptop e telefono. Il sintomo è tipico: alcune destinazioni escono fuori tunnel, il DNS risolve sul resolver del provider, e su certe connessioni compaiono pacchetti frammentati o siti che si aprono a metà.
Qui non facciamo l’installazione base. Partiamo da una configurazione da produzione con policy routing, prevenzione del DNS leak, MTU verificata e controlli per evitare conflitti tra client. L’obiettivo è semplice: il traffico giusto entra nel tunnel giusto, e il resto resta fuori in modo intenzionale.
Prerequisiti
Serve un server Linux con WireGuard già disponibile nel kernel o nei repository. Gli esempi usano Debian 12, ma i concetti valgono anche su Ubuntu LTS e su molte distro enterprise.
- Un server con interfaccia pubblica, ad esempio eth0.
- Due subnet VPN distinte, una per amministrazione e una per applicazioni.
- Accesso root o sudo.
- systemd-resolved attivo, oppure un resolver locale ben definito.
- Un client desktop e almeno un client mobile per testare il comportamento multiplo.
Note: useremo wg-quick per l’avvio, ma con regole di routing e firewall aggiunte a mano. È il compromesso più pratico quando vuoi stabilità e auditabilità.
Step 1: separa i peer per funzione, non per comodità
La prima scelta di produzione è architetturale. Ogni peer deve avere un ruolo chiaro. Un peer amministrativo non deve vedere la LAN applicativa se non serve. Un peer mobile non deve ereditare rotte globali solo perché è il più semplice da configurare.
Usa subnet diverse e AllowedIPs minimali. Questo riduce gli errori e rende il troubleshooting leggibile.
# /etc/wireguard/wg-admin.conf
[Interface]
Address = 10.44.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
SaveConfig = false
[Peer]
PublicKey = ADMIN_CLIENT_PUB
AllowedIPs = 10.44.0.10/32
[Peer]
PublicKey = OPS_LAPTOP_PUB
AllowedIPs = 10.44.0.11/32# Output: ogni client riceve un IP singolo e non può sovrapporsi agli altri.
Per una seconda VPN applicativa, usa un file separato.
# /etc/wireguard/wg-app.conf
[Interface]
Address = 10.45.0.1/24
ListenPort = 51821
PrivateKey = SERVER_PRIVATE_KEY_2
SaveConfig = false
[Peer]
PublicKey = APP_CLIENT_PUB
AllowedIPs = 10.45.0.20/32# Output: due tunnel distinti, due piani di accesso distinti.
Warning: evitare un solo file con troppi peer e rotte ampie. Funziona all’inizio, poi il rischio di collisione cresce molto.
Step 2: imposta split routing con policy routing, non con improvvisazione
Il classico errore è mettere AllowedIPs = 0.0.0.0/0 sul client e sperare che tutto il resto si sistemi. In una rete reale, questo rompe il traffico verso stampanti, servizi locali e alcune VPN parallele.
Meglio definire il traffico che deve passare nel tunnel e applicare una tabella di routing dedicata. Questo è utile anche quando il client ha più interfacce attive.
# /etc/wireguard/wg-client.conf
[Interface]
PrivateKey = CLIENT_PRIVATE_KEY
Address = 10.44.0.10/32
DNS = 10.44.0.1
Table = 51820
PostUp = ip rule add from 10.44.0.10/32 table 51820
PostDown = ip rule del from 10.44.0.10/32 table 51820
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = vpn.example.com:51820
AllowedIPs = 10.44.0.0/24, 203.0.113.0/24
PersistentKeepalive = 25# Output: solo le reti esplicitamente elencate usano il tunnel.
Se vuoi far passare solo una subnet aziendale, lascia il resto al gateway locale.
Note: Table = 51820 evita di sporcare la tabella main. È utile quando il client ha già rotte gestite da Wi-Fi, Docker o un secondo tunnel.
Step 3: elimina il DNS leak con resolver coerente e split DNS
Il leak DNS è subdolo perché spesso il traffico dati è corretto, ma la risoluzione no. Il browser sembra andare, però il nome host viene risolto fuori tunnel. In ambienti sensibili è un problema serio.
La soluzione più robusta è usare un resolver interno raggiungibile solo via VPN e, se serve, fare split DNS per dominio. Su systemd-resolved questo è pulito.
# lato client, con systemd-resolved
resolvectl dns wg0 10.44.0.1
resolvectl domain wg0 '~corp.example.internal'
resolvectl default-route wg0 no# Output: i domini interni passano dal resolver VPN, il resto resta sul resolver locale.
Se usi wg-quick, puoi forzare un resolver dedicato nel file client e poi verificare con il comando sotto.
resolvectl status wg0# Output: l’interfaccia wg0 mostra DNS e domini associati in modo esplicito.
Un controllo pratico, utile anche nei ticket, è questo:
dig +short internal.corp.example @10.44.0.1# Output: un indirizzo interno, non un record pubblico o un timeout.
Warning: se il client usa un browser con DoH attivo, puoi avere leak anche con DNS di sistema corretti. In produzione conviene disabilitare DoH sui dispositivi gestiti o imporre policy aziendali.
Step 4: sistema l’MTU prima che i sintomi diventino casuali
Molti problemi WireGuard non sono “VPN rotta”. Sono MTU sbagliata. La manifestazione cambia: alcune pagine non caricano, SSH funziona ma alcuni download falliscono, oppure il traffico verso certi SaaS si blocca dopo il TLS handshake.
Su reti con PPPoE, overlay o percorsi con overhead aggiuntivo, 1420 può essere ancora troppo alta. In questo scenario partiamo da 1380 e misuriamo.
# lato client e server, se necessario
ip link set dev wg0 mtu 1380
ip link show wg0# Output: wg0 mostra mtu 1380.
Per un test rapido, usa un ping senza frammentazione verso una destinazione interna.
ping -M do -s 1352 10.44.0.1# Output: 0% packet loss, nessun messaggio “Frag needed”.
Se il ping fallisce, abbassa ancora di 20 o 40 byte e riprova. In ambienti con doppio NAT o provider mobile la soglia può sorprendere.
Un controllo più realistico è verificare una connessione HTTPS verso un host interno.
curl --connect-timeout 5 -I https://app.internal.example# Output: HTTP/2 200 oppure un redirect atteso, senza stall lunghi.
Note: MTU e MSS non sono la stessa cosa. Se il problema compare solo su TCP, valuta anche un clamp MSS nel firewall del gateway.
Step 5: gestisci più client con lo stesso nome host senza conflitti AllowedIPs
Il problema dei client multipli emerge quando colleghi due dispositivi allo stesso profilo. Il risultato può essere un conflitto di AllowedIPs o un “flapping” della route, dove il traffico cambia uscita in modo intermittente.
La regola è semplice: ogni dispositivo ha la sua chiave, il suo IP e il suo file. Non riusare un peer tra laptop, telefono e server di test.
# esempio lato server
[Peer]
PublicKey = LAPTOP_A_PUB
AllowedIPs = 10.44.0.10/32
[Peer]
PublicKey = PHONE_A_PUB
AllowedIPs = 10.44.0.11/32
[Peer]
PublicKey = SERVER_BACKUP_PUB
AllowedIPs = 10.44.0.12/32# Output: nessuna sovrapposizione di IP tra dispositivi.
Se vuoi consentire accesso a una sola subnet, mantieni le AllowedIPs sul client strette e non ambigue.
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = vpn.example.com:51820
AllowedIPs = 10.44.0.0/24, 10.45.0.0/24# Output: il client raggiunge solo le reti VPN autorizzate.
Warning: non assegnare lo stesso /32 a più peer “tanto non si usano insieme”. Prima o poi succede, e il debug diventa costoso.
Step 6: aggiungi hardening minimo del gateway
Una VPN di produzione non è solo routing. Serve anche ridurre la superficie d’attacco. Il gateway deve accettare solo ciò che serve.
Su nftables puoi restringere l’ingresso e abilitare forwarding solo verso le reti previste. Mantieni il forwarding esplicito e logga ciò che scarti.
table inet filter {
chain input {
type filter hook input priority 0;
policy drop;
iif lo accept
ct state established,related accept
udp dport 51820 accept
ip protocol icmp accept
}
chain forward {
type filter hook forward priority 0;
policy drop;
ct state established,related accept
ip saddr 10.44.0.0/24 ip daddr 10.10.0.0/24 accept
ip saddr 10.45.0.0/24 ip daddr 10.20.0.0/24 accept
}
}# Output: solo i flussi previsti attraversano il gateway.
Se il server fa anche NAT verso Internet per alcuni client, limita il masquerade alla sola subnet necessaria.
Note: il logging selettivo aiuta molto quando un client “non vede più nulla” ma in realtà viene bloccato da una regola troppo stretta.
Verifica finale
La verifica deve coprire quattro cose: raggiungibilità, routing, DNS e dimensione pacchetti. Se una sola falla, la configurazione non è ancora pronta per produzione.
Controlla lo stato del tunnel.
wg show# Output: latest handshake recente e transfer counters in crescita.
Verifica la route scelta.
ip route get 10.44.0.1# Output: l’uscita mostra dev wg0 o la tabella corretta.
Verifica DNS interno e pubblico separatamente.
dig +short internal.corp.example @10.44.0.1 dig +short example.com @1.1.1.1# Output: il dominio interno risolve solo via VPN, quello pubblico resta normale.
Verifica che il traffico non frammenti.
ping -M do -s 1352 10.44.0.1 curl -I https://app.internal.example# Output: nessun timeout anomalo e nessun errore di frammentazione.
Se usi più client, ripeti i test da laptop e telefono. Il comportamento deve restare coerente su entrambi.
Troubleshooting
1) “RTNETLINK answers: File exists”
Causa: una route o una rule è già presente, spesso perché wg-quick è stato rilanciato senza pulizia.
Fix:
ip rule show
ip route show table 51820
wg-quick down wg-client
wg-quick up wg-client# Output: le regole duplicate spariscono e il tunnel torna su pulito.
2) “AllowedIPs conflict: 10.44.0.10/32”
Causa: lo stesso IP è assegnato a due peer o riusato in un profilo clonatto.
Fix:
wg showconf wg0
# correggi il file server
systemctl restart wg-quick@wg0# Output: ogni peer torna a avere un solo /32 univoco.
3) “Temporary failure in name resolution”
Causa: il DNS del client non punta al resolver VPN o il dominio interno non è instradato su wg0.
Fix:
resolvectl dns wg0 10.44.0.1
resolvectl domain wg0 '~corp.example.internal'
resolvectl flush-caches# Output: i nomi interni tornano a risolversi senza uscire dal tunnel.
Conclusione
Una configurazione WireGuard da produzione non si misura dalla velocità, ma dalla prevedibilità. Se routing, DNS e MTU sono coerenti, il tunnel smette di essere un punto fragile.
Il prossimo passo concreto è automatizzare questi controlli con un timer systemd o con una pipeline di post-deploy. Così intercetti subito regressioni di routing, leak DNS e cambi MTU introdotti da aggiornamenti o nuovi peer.
Commenti (0)
Nessun commento ancora.
Segnala contenuto
Elimina commento
Eliminare definitivamente questo commento?
L'azione non si può annullare.