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 rompe non appena il tunnel WireGuard entra in funzione
Il DNS su WireGuard non funziona in tre modi, e ciascun caso richiede una soluzione specifica. A volte nessun nome viene risolto. In altri casi i nomi vengono risolti, ma le query escono dalla macchina senza passare nel tunnel. In altri ancora, il resolver manager del client sovrascrive l'impostazione pochi secondi dopo l'avvio dell'interfaccia. Il tunnel è quasi mai la causa. Il problema è la riga che indica al client quale resolver utilizzare e il routing che determina come i pacchetti raggiungono 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 direttiva DNS = in un blocco client [Interface] non è un'impostazione di WireGuard. Viene letta da wg-quick, il wrapper shell che attiva l'interfaccia, mentre wg-quick modifica la configurazione del resolver del client quando il tunnel è attivo e la ripristina su wg-quick down. Di conseguenza, tutti i problemi descritti di seguito riguardano il routing o wg-quick, non la crittografia. 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.1wg show dovrebbe mostrare il peer con un latest handshake recente, ed entrambi i ping dovrebbero ricevere risposta. Se ping 1.1.1.1 va in timeout, il problema riguarda il forwarding o il NAT (network address translation), non il DNS, e nessuna modifica alla configurazione del resolver sarà utile. Se i ping ricevono risposta ma il throughput crolla quando inizia il traffico reale, si tratta di un problema distinto: un WireGuard lento dipende quasi sempre dall'MTU, non da uno degli aspetti descritti in questa pagina. Tutti gli esempi usano 10.8.0.0/24 come subnet del tunnel e 10.8.0.1 come indirizzo del server nel tunnel. 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.comInterroga 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.comIl 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 rulesetUn 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" acceptCon 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.1Errore 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 viaggiano in chiaro sulla rete locale di cui non volevi fidarti.
Sono due le configurazioni che lo causano. La prima è un client con AllowedIPs = 0.0.0.0/0, ::/0 e senza una direttiva DNS =. wg-quick installa la route predefinita nella propria tabella di routing e aggiunge una regola con suppress_prefixlength 0, che 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 nel tunnel. La rete locale riceve comunque l'elenco completo dei nomi che cerchi di risolvere.
La seconda è uno split tunnel: AllowedIPs = 10.8.0.0/24 con DNS = 9.9.9.9. Poiché 9.9.9.9 non rientra in AllowedIPs, il client non ha una route verso quell'indirizzo 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 eseguito 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 53resolvectl 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 mostra alcun resolver, questa è la perdita DNS. Se dig +short whoami.akamai.net restituisce l'indirizzo della tua connessione Internet domestica invece di quello del server, la verifica è confermata anche dal lato remoto. La riga tcpdump è la prova definitiva: in una configurazione corretta, ogni pacchetto diretto alla porta 53 passa da wg0; in presenza di una perdita, passa da wlan0 o enp3s0.
La correzione richiede due interventi, ed entrambi sono necessari. Imposta DNS su un indirizzo appartenente al tunnel e verifica che tale indirizzo rientri 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/1610.8.0.1 rientra in 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 route host: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. I pacchetti passeranno quindi nel tunnel, ma la rete locale potrà comunque dedurre che hai scelto quel provider osservando le sessioni precedenti. Usare un resolver gestito direttamente da te evita il problema.
L’assegnazione del resolver è una delle differenze visibili tra WireGuard configurato manualmente e una mesh coordinata; rientra nel compromesso descritto in WireGuard a confronto con Tailscale. L’esecuzione di un server di controllo Headscale self-hosted offre questo coordinamento senza affidare il materiale delle chiavi a terzi. Se è proprio quest’ultimo aspetto a preoccuparti, considera che Tailscale non conserva mai le chiavi che cifrano il tuo traffico; la domanda più rilevante è quali capacità potrebbero aggiungere alla rete un server di coordinamento compromesso o un account di identità sottratto.
Errore 3: 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. Su Linux l’impostazione viene applicata da uno script shell che deve determinare quale tra diversi gestori dei resolver è in uso.
Il primo errore è evidente. sudo wg-quick up wg0 si interrompe con:
resolvconf: command not foundwg-quick richiama resolvconf, ma quel binario non è installato. Installa l’implementazione che comunica con systemd-resolved, quindi riattiva l’interfaccia.
sudo apt install -y openresolv
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0Il secondo errore non produce messaggi ed è quello che può richiedere ore di diagnosi. L’interfaccia viene attivata, resolvectl status wg0 mostra correttamente DNS Servers: 10.8.0.1, ma le richieste di risoluzione continuano a essere inviate al resolver precedente. systemd-resolved mantiene un elenco di resolver separato per ogni link e seleziona un link per ogni query. Se nessun link è contrassegnato come route predefinita per i nomi, continua a usare il resolver del link wireless, perché quel link contiene un dominio di ricerca mentre il tuo non lo contiene.
Imposta il resolver e assegna la route predefinita nella stessa operazione. %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 %iElimina la riga DNS = quando usi PostUp in questo modo, perché altrimenti due meccanismi modificano lo stato del resolver e solo uno dei due esegue la pulizia. L’argomento ~. è la parte fondamentale: contrassegna wg0 come dominio di routing per ogni nome, quindi systemd-resolved invia tutte le query a quel link invece di sceglierne uno per ogni query. Verifica il risultato.
resolvectl status wg0Un output corretto contiene DNS Servers: 10.8.0.1 e Default Route: yes. Se Default Route restituisce no, la parte resolvectl domain non è stata eseguita e il sistema è tornato alla selezione basata sui link.
È utile citare un altro caso. Se /etc/resolv.conf è un file reale anziché un link simbolico a /run/systemd/resolve/stub-resolv.conf, significa che un altro componente lo gestisce, in genere NetworkManager o un runtime per container. Esegui ls -l /etc/resolv.conf prima di analizzare qualsiasi altra cosa, perché uno strumento che riscrive quel file a ogni modifica della rete annullerà il lavoro nel momento meno opportuno.
L'upgrade: il resolver di filtraggio personale attraverso il tunnel
Quando le query viaggiano in modo affidabile attraverso il tunnel, il resolver all'altra estremità diventa un punto di controllo. Eseguendo AdGuard Home su quel server, ogni dispositivo connesso dispone di un filtro basato su blocklist e di un log delle query, senza software client né configurazioni per singolo dispositivo. Lo script di installazione ufficiale, verificato a luglio 2026, consiste in una sola riga.
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -vAlla 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 amministrativo su 10.8.0.1. Se unbound del primo problema è ancora in ascolto sullo stesso indirizzo, arrestalo prima con sudo systemctl disable --now unbound, perché due processi non possono associare la porta UDP 53 allo stesso indirizzo e il secondo termina 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 richiesta effettuata da ogni peer. Questa è una scelta concreta in materia di privacy, non un vantaggio senza costi: trasferisci la fiducia dal tuo provider Internet a te stesso e sei tu a dover mantenere aggiornato quel server. Prima di esporre un server a Internet devi applicare le misure di base; i primi dieci minuti su un nuovo VPS le descrivono.
FAQ
Perché il tunnel WireGuard si connette, ma i nomi non vengono risolti?
Il tunnel trasporta pacchetti e non gestisce i nomi. Un tunnel funzionante con lookup non riusciti indica quindi che il resolver configurato non risponde. Eseguire dig +short @10.8.0.1 example.com sul client. Una risposta communication timed out significa che non è in ascolto alcun resolver su quell'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 ricevuto tramite wg0. Correggere prima l'ascolto, quindi aprire la porta soltanto per wg0.
Come posso verificare se il DNS viene esposto 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 dovrebbe passare tramite wg0. Se i pacchetti compaiono sull'interfaccia wireless o Ethernet, le query vengono inviate in chiaro. dig +short whoami.akamai.net fornisce un'ulteriore verifica, perché risponde con l'indirizzo pubblico del resolver ricorsivo che ha eseguito la query. Se la risposta non contiene l'indirizzo del server, la perdita è confermata.
La direttiva DNS = è necessaria se uso un tunnel parziale?
Sì. Anche 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 all'indirizzo 10.8.0.1 è incluso e la query viene cifrata. Un resolver pubblico come 9.9.9.9 non è incluso. La query viene quindi inviata tramite il collegamento locale, anche se la direttiva DNS sembra corretta.
Perché resolvectl mostra il server corretto, ma le query vengono comunque inviate altrove?
systemd-resolved mantiene un elenco di resolver per ogni collegamento e sceglie un collegamento per ogni query. Di conseguenza, una voce corretta su wg0 viene ignorata se un altro collegamento contiene la route predefinita per i nomi. 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 restituire Default Route: yes.
Quale client devo correggere per primo quando diversi client presentano problemi?
Correggere prima un client Linux, perché è l'unica piattaforma che mostra il meccanismo in modo diretto. resolvectl status e tcpdump indicano quale resolver ha risposto e quale interfaccia ha trasportato il pacchetto. Le applicazioni su telefoni e desktop applicano gli stessi valori DNS e AllowedIPs senza mostrare i dettagli della configurazione. Dopo aver corretto il client Linux, sarà quindi possibile copiare una configurazione già verificata.