SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor

DNS su WireGuard: risolvere 3 errori comuni

Il tunnel WireGuard è attivo ma il DNS non risolve o invia query al router locale? Individua i 3 casi e correggi routing, resolver e wg-quick.

Perché il DNS si interrompe non appena si attiva il tunnel WireGuard

Il DNS su WireGuard non funziona in tre modi distinti, e ciascuno richiede una correzione specifica. Nessun nome viene risolto, oppure i nomi vengono risolti ma le query escono dal computer senza passare nel tunnel, oppure il resolver manager del client sovrascrive l'impostazione pochi secondi dopo l'attivazione dell'interfaccia. Il tunnel è quasi mai la causa. Il problema è la riga che indica al client quale resolver interrogare e il routing che determina il percorso dei pacchetti diretti a quel resolver.

WireGuard trasporta pacchetti IP e non gestisce il DNS (domain name system, il servizio che converte nomi come example.com in indirizzi IP). La riga DNS = in un blocco client [Interface] non è un'impostazione di WireGuard. Viene letta da wg-quick, il wrapper shell che attiva l'interfaccia, e wg-quick modifica quindi la configurazione del resolver del client mentre il tunnel è attivo e la ripristina su wg-quick down. Pertanto, tutti i problemi descritti di seguito sono problemi di routing o problemi di wg-quick, mai problemi crittografici. Se il tunnel non è ancora configurato, inizia da una VPN WireGuard self-hosted sul tuo VPS e torna a questa pagina in seguito.

Verifica che il tunnel funzioni correttamente prima di intervenire sul DNS.

sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1

wg show dovrebbe elencare il peer con un latest handshake recente e entrambi i ping dovrebbero ricevere risposta. Se ping 1.1.1.1 va in timeout, è probabile che il problema riguardi il forwarding o il NAT (network address translation), non il DNS, e nessuna configurazione del resolver potrà risolverlo. Anche un firewall che scarta ICMP produce lo stesso timeout, quindi verifica con curl -sI https://1.1.1.1 prima di modificare le regole di forwarding. Se i ping ricevono risposta ma la velocità crolla quando inizia il traffico reale, si tratta di un problema distinto: la lentezza di WireGuard dipende quasi sempre dall'MTU e non da uno degli aspetti descritti in questa pagina. Tutti gli esempi utilizzano 10.8.0.0/24 come subnet del tunnel e 10.8.0.1 come indirizzo del tunnel del server. Sostituiscili con i tuoi valori.

Primo problema: nulla viene risolto perché il resolver non risponde

Il sintomo è preciso. ping 1.1.1.1 funziona, mentre curl https://example.com restituisce questo:

curl: (6) Could not resolve host: example.com

Interroga direttamente il resolver del tunnel dal client. dig fa parte del pacchetto dnsutils su Ubuntu e Debian.

dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.com

Il primo comando restituisce un indirizzo. Questo dimostra che i pacchetti raggiungono Internet attraverso il tunnel. Il secondo non restituisce nulla e stampa ;; communication timed out; no servers could be reached. La diagnosi è completa: il client usa 10.8.0.1 come resolver e 10.8.0.1 non risponde sulla porta UDP 53.

Le cause possibili sono due. Sul server non è in esecuzione alcun resolver oppure il firewall del server scarta la richiesta prima che arrivi. Verifica entrambe le condizioni sul server.

sudo ss -ulnp | grep ':53'
sudo nft list ruleset

Un resolver in esecuzione e associato correttamente mostra una riga con 10.8.0.1:53 o 0.0.0.0:53. Su Ubuntu il caso inatteso è spesso 127.0.0.53:53: si tratta dello stub listener di systemd-resolved, che si associa a un indirizzo di loopback e non è raggiungibile deliberatamente dalle altre macchine. Configurare un client VPN per usare un server il cui unico resolver è questo stub produce esattamente questo timeout.

La soluzione consiste in un resolver in ascolto sull'indirizzo del tunnel e in una regola del firewall che consenta ai peer di raggiungerlo.

sudo apt update && sudo apt install -y unbound
printf 'server:\n  interface: 10.8.0.1\n  access-control: 10.8.0.0/24 allow\n' \
  | sudo tee /etc/unbound/unbound.conf.d/wireguard.conf
sudo systemctl restart unbound
sudo ss -ulnp | grep '10.8.0.1:53'

Quindi apri la porta solo per il traffico del tunnel. Con nftables, aggiungi queste due righe alla catena input in /etc/nftables.conf e ricarica la configurazione con sudo systemctl reload nftables.

udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" accept

Con ufw, sudo ufw allow in on wg0 to any port 53 svolge la stessa funzione. Non aprire mai la porta 53 a Internet. Un resolver ricorsivo aperto viene individuato dagli scanner nel giro di pochi giorni e usato per amplificare attacchi di denial of service. Il tuo provider rileverà questo traffico prima di te.

Esegui di nuovo dig +short @10.8.0.1 example.com dal client. Un indirizzo nell'output indica che il percorso verso il resolver funziona. A questo punto il client deve solo utilizzarlo. Aggiungi la riga al blocco [Interface] del client e riavvia l'interfaccia con sudo wg-quick down wg0 && sudo wg-quick up wg0.

[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1

Errore due: perdite DNS, perché uno split tunnel non instrada il resolver

Questo problema è peggiore, perché tutto sembra funzionare. I nomi vengono risolti, le pagine si caricano e le query attraversano in chiaro la rete locale che non volevi considerare attendibile.

Lo causano due configurazioni. La prima è un client con AllowedIPs = 0.0.0.0/0, ::/0 e senza una riga DNS =. wg-quick installa la route predefinita nella propria tabella di routing e aggiunge una regola con suppress_prefixlength 0. In questo modo mantiene intenzionalmente attive le route locali più specifiche, così la macchina può continuare a raggiungere la stampante. Il resolver appreso dal client tramite DHCP, in genere il router all'indirizzo 192.168.1.1, corrisponde a una di queste route locali. Il traffico passa attraverso il tunnel. La rete locale riceve comunque l'elenco completo dei nomi che cerchi.

La seconda è uno split tunnel: AllowedIPs = 10.8.0.0/24 con DNS = 9.9.9.9. Poiché 9.9.9.9 non si trova all'interno di AllowedIPs, il client non dispone di una route per raggiungerlo attraverso il tunnel. La query esce quindi dal collegamento locale, esattamente come nel primo caso.

Verifica quale resolver sta realmente rispondendo. whoami.akamai.net è un nome di test pubblico che risponde con l'indirizzo IP del resolver ricorsivo che ha inviato la query. Puoi quindi confrontare la risposta con l'indirizzo pubblico del server.

resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53

resolvectl status stampa un blocco per ogni collegamento. Se il blocco relativo al collegamento Ethernet o wireless mostra ancora Current DNS Server: 192.168.1.1, mentre il blocco wg0 non ne mostra alcuno, si tratta di una perdita. Se dig +short whoami.akamai.net restituisce l'indirizzo della tua connessione a banda larga domestica anziché quello del server, la verifica remota conferma la perdita. La riga tcpdump fornisce la prova definitiva: in una configurazione corretta, ogni pacchetto sulla porta 53 che lascia la macchina passa da wg0; in presenza di una perdita, passa da wlan0 o enp3s0. I pacchetti su lo diretti a 127.0.0.53 o provenienti da 127.0.0.53 sono richieste delle applicazioni allo stub locale di systemd-resolved e non costituiscono una perdita.

La correzione richiede due interventi, entrambi necessari. Imposta DNS su un indirizzo che si trovi all'interno del tunnel e assicurati che tale indirizzo sia incluso in AllowedIPs.

[Interface]
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 10.20.0.0/16

10.8.0.1 si trova all'interno di 10.8.0.0/24, quindi la query viene cifrata e inviata al server. Se vuoi usare un resolver pubblico con uno split tunnel, aggiungilo come host route: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. I pacchetti passeranno quindi attraverso il tunnel, anche se la rete locale potrà ancora capire quale provider hai scelto in base alle sessioni precedenti. Un resolver gestito autonomamente evita questo problema.

L'assegnazione del resolver è una delle differenze osservabili tra WireGuard configurato manualmente e una mesh coordinata. Fa parte del compromesso descritto in WireGuard a confronto con Tailscale. Eseguire un server di controllo Headscale self-hosted offre questo coordinamento senza affidare il materiale delle chiavi a terzi. Se è quest'ultima frase a preoccuparti, considera che Tailscale non conserva mai le chiavi che cifrano il tuo traffico e che la domanda più importante è quali elementi un server di coordinamento compromesso o un account di identità sottratto potrebbero aggiungere alla tua rete.

Fallimento tre: resolvconf e systemd-resolved entrano in conflitto sui client Linux

I client macOS, Windows, iOS e Android applicano DNS = tramite l'app ufficiale e causano pochi problemi. Linux è il caso più complesso, perché l'impostazione viene applicata da uno script shell che deve determinare quale dei diversi gestori dei resolver è in uso.

Il primo fallimento è evidente. sudo wg-quick up wg0 si arresta con:

resolvconf: command not found

wg-quick richiama resolvconf, ma quel binario non è installato. Su Debian 12 e Ubuntu 24.04 o versioni successive, il comando resolvconf che comunica con systemd-resolved è incluso nel pacchetto systemd-resolved. Installare quindi quel pacchetto, poi riattivare l'interfaccia. Ubuntu 22.04 non dispone di un pacchetto separato; in quel caso, creare manualmente il link di compatibilità con sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf e omettere la riga di installazione.

sudo apt install -y systemd-resolved
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0

Il secondo fallimento è silenzioso ed è quello che può far perdere un'intera serata. L'interfaccia si attiva, resolvectl status wg0 mostra correttamente DNS Servers: 10.8.0.1, ma le richieste di risoluzione continuano a raggiungere il resolver precedente. systemd-resolved non ignora wg0. Un nome che non corrisponde ad alcun dominio di routing viene inviato ai server DNS di ogni link considerato come route predefinita; inoltre, per impostazione predefinita, un link con server DNS e senza un dominio valido solo per il routing viene considerato una route predefinita. La query viene quindi inviata contemporaneamente a 10.8.0.1 e al router del link wireless, e la prima risposta valida viene utilizzata. Il router si trova a un solo hop di distanza e di solito risponde per primo; in ogni caso, la query è già uscita in chiaro. I nomi appartenenti al dominio di ricerca del link wireless, come lan, vengono inviati soltanto a quel link, perché prevale il dominio di routing più specifico.

Quando wg-quick applica DNS = tramite il wrapper resolvconf di systemd, richiede già un dominio di routing catch-all. Questo problema si presenta quindi soprattutto quando l'impostazione viene applicata da un'altra implementazione di resolvconf o da un gestore di rete. Impostarla esplicitamente elimina ogni ambiguità.

Impostare il resolver e assegnare il dominio di routing catch-all nello stesso passaggio. %i viene sostituito con il nome dell'interfaccia, quindi questo blocco funziona senza modifiche su qualsiasi interfaccia.

[Interface]
Address = 10.8.0.2/32
PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.
PostDown = resolvectl revert %i

Eliminare la riga DNS = quando si usa PostUp in questo modo. In caso contrario, due meccanismi modificano lo stato del resolver e soltanto uno dei due esegue la pulizia finale. L'argomento ~. è la parte importante: rende wg0 il dominio valido solo per il routing di ogni nome. systemd-resolved smette così di inviare agli altri link i nomi che non corrispondono a un dominio più specifico. I nomi appartenenti al dominio specifico di un altro link, come lan, continuano a essere inviati a quel link, comportamento generalmente corretto per gli host locali. Verificare l'impostazione.

resolvectl status wg0

Un output corretto contiene DNS Servers: 10.8.0.1 e DNS Domain: ~.. Se manca ~., la parte resolvectl domain non è stata eseguita e wg0 ricomincia a competere con gli altri link. Non usare il flag della route predefinita come verifica: mostra yes (oppure +DefaultRoute sulla riga Protocols, a seconda della versione di systemd) anche senza ~., perché un link senza un dominio valido solo per il routing è comunque una route predefinita.

È utile considerare anche un altro caso. Se /etc/resolv.conf è un file reale e non un link simbolico a /run/systemd/resolve/stub-resolv.conf, il file è gestito da un altro componente, in genere NetworkManager o un runtime per container. Eseguire ls -l /etc/resolv.conf prima di procedere con altre verifiche, perché uno strumento che riscrive quel file a ogni modifica della rete annullerà il lavoro nel momento meno opportuno.

L’aggiornamento: il tuo resolver con filtraggio attraverso il tunnel

Quando le query attraversano il tunnel in modo affidabile, il resolver all’estremità opposta diventa un punto di controllo. L’esecuzione di AdGuard Home su quel server fornisce a ogni dispositivo connesso il filtraggio tramite blocklist e un log delle query, senza software client né configurazioni specifiche per ogni dispositivo. Lo script di installazione ufficiale, verificato a ottobre 2026, consiste in una sola riga.

curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

Alla prima esecuzione, la procedura guidata di configurazione resta in ascolto sulla porta 3000. Raggiungila attraverso il tunnel all’indirizzo http://10.8.0.1:3000 invece di esporre pubblicamente quella porta e, nella procedura guidata, imposta sia l’indirizzo di ascolto DNS sia l’indirizzo di ascolto dell’amministrazione su 10.8.0.1. Se unbound del primo problema è ancora in esecuzione sullo stesso indirizzo, arrestalo prima con sudo systemctl disable --now unbound, perché i due processi entrerebbero in conflitto per la porta UDP 53 sullo stesso indirizzo e AdGuard Home terminerebbe con listen udp 10.8.0.1:53: bind: address already in use.

Le configurazioni dei client non richiedono modifiche se contengono già DNS = 10.8.0.1. Il log delle query mostrerà ora ogni lookup che lo raggiunge da ogni peer, a meno che tu non escluda un client dal logging. Si tratta di una scelta concreta in materia di privacy, non di un vantaggio gratuito: trasferisci la fiducia dal provider Internet a te stesso e sei tu a dover mantenere aggiornato quel server. Un server esposto a Internet deve avere prima le misure di base, descritte in i primi dieci minuti su un nuovo VPS.

FAQ

Perché il tunnel WireGuard si connette, ma i nomi non vengono risolti?

Il tunnel trasporta pacchetti e non gestisce affatto i nomi. Un tunnel funzionante con risoluzioni non riuscite indica quindi che il resolver configurato non risponde. Eseguire dig +short @10.8.0.1 example.com sul client. Una risposta communication timed out indica che nessun resolver è in ascolto sull'indirizzo del tunnel, spesso perché lo stub di systemd-resolved è associato soltanto a 127.0.0.53, oppure che il firewall del server scarta il traffico UDP sulla porta 53 in arrivo su wg0. Correggere prima il listener, quindi aprire la porta soltanto per wg0.

Come verifico se il DNS perde traffico al di fuori di WireGuard?

Eseguire sudo tcpdump -ni any -c 10 port 53 sul client e osservare la colonna dell'interfaccia durante la navigazione. Ogni pacchetto in uscita dalla macchina dovrebbe passare su wg0. I pacchetti su lo diretti a 127.0.0.53 o provenienti da 127.0.0.53 corrispondono allo stub locale di systemd-resolved e sono normali. Se compaiono sull'interfaccia wireless o Ethernet, le query stanno uscendo in chiaro. dig +short whoami.akamai.net fornisce una seconda verifica, perché risponde con l'indirizzo pubblico del resolver ricorsivo che ha ricevuto la query. Se la risposta non contiene l'indirizzo del server, la perdita è confermata.

La direttiva DNS = è necessaria se uso uno split tunnel?

Sì. Inoltre, l'indirizzo del resolver deve rientrare in AllowedIPs, altrimenti il client non ha una route per raggiungerlo. Con AllowedIPs = 10.8.0.0/24, un resolver su 10.8.0.1 è incluso e la query viene cifrata. Un resolver pubblico come 9.9.9.9 non è incluso. La query esce quindi sul collegamento locale, anche se la direttiva DNS sembra corretta.

Perché resolvectl mostra il server corretto, ma le query vengono comunque inviate altrove?

systemd-resolved invia un nome che non corrisponde ad alcun routing domain a tutti i link con route predefinita contemporaneamente, incluso wg0. Restituisce la prima risposta corretta, che di solito proviene dal router locale. Aggiungere PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. al blocco [Interface] del client e rimuovere la direttiva DNS =. A quel punto resolvectl status wg0 dovrebbe segnalare DNS Domain: ~.. Ogni nome che nessun altro link gestisce tramite un dominio più specifico verrà inviato soltanto a wg0.

Quale client devo correggere per primo quando più client presentano problemi?

Correggere prima un client Linux, perché è l'unica piattaforma che mostra il meccanismo in modo esplicito. resolvectl status e tcpdump indicano quale resolver ha risposto e quale interfaccia ha trasportato il pacchetto. Le applicazioni per telefoni e desktop applicano gli stessi valori DNS e AllowedIPs senza mostrare i dettagli della configurazione. Quando il client Linux è corretto, è quindi possibile copiare una configurazione già verificata.