DNS przez WireGuard: 3 typowe problemy i naprawa
Tunel WireGuard działa, ale DNS nie rozwiązuje nazw lub zapytania trafiają do routera. Rozpoznaj 3 tryby awarii i popraw routing oraz ustawienia resolwera.
Dlaczego DNS przestaje działać natychmiast po uruchomieniu tunelu WireGuard
DNS przez WireGuard nie działa na 3 sposoby. Każdy z nich wymaga innego rozwiązania. Żadna nazwa nie jest rozwiązywana, nazwy są rozwiązywane, ale zapytania opuszczają komputer poza tunelem albo własny menedżer resolwera klienta nadpisuje ustawienie kilka sekund po uruchomieniu interfejsu. Niemal nigdy problemem nie jest sam tunel. Problem stanowi wiersz, który wskazuje klientowi, z którego resolwera ma korzystać, oraz routing określający sposób przesyłania pakietów do tego resolwera.
WireGuard przesyła pakiety IP i nie obsługuje DNS (domain name system, usługi tłumaczącej nazwy, takie jak example.com, na adresy IP). Wiersz DNS = w bloku klienta [Interface] nie jest ustawieniem WireGuard. Odczytuje go wg-quick, czyli wrapper powłoki uruchamiający interfejs, a następnie wg-quick modyfikuje konfigurację resolwera klienta, gdy tunel jest aktywny, i przywraca ją po wg-quick down. Dlatego każdy z opisanych poniżej problemów dotyczy routingu albo wg-quick, a nie kryptografii. Jeśli tunel nie został jeszcze utworzony, należy rozpocząć od własnej sieci VPN WireGuard na własnym VPS i wrócić do tej strony.
Przed rozpoczęciem konfiguracji DNS należy potwierdzić, że tunel działa prawidłowo.
sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1wg show powinno wyświetlać peera z aktualnym latest handshake, a oba polecenia ping powinny otrzymać odpowiedź. Jeśli ping 1.1.1.1 przekroczy limit czasu, problem dotyczy przekazywania pakietów lub NAT (network address translation), a nie DNS. Żadna zmiana konfiguracji resolwera go nie rozwiąże. Wszystkie przykłady używają 10.8.0.0/24 jako podsieci tunelu oraz 10.8.0.1 jako adresu serwera w tunelu. Należy zastąpić je własnymi wartościami.
Awaria pierwsza: nic nie jest rozwiązywane, ponieważ resolver nie odpowiada
Objaw jest jednoznaczny. ping 1.1.1.1 działa, a curl https://example.com zwraca:
curl: (6) Could not resolve host: example.comNależy bezpośrednio odpytać resolver tunelu z klienta. dig pochodzi z pakietu dnsutils w systemach Ubuntu i Debian.
dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.comPierwsze polecenie zwraca adres, co potwierdza, że pakiety docierają do Internetu przez tunel. Drugie nie zwraca niczego i wyświetla ;; communication timed out; no servers could be reached. Diagnoza jest jednoznaczna: klient korzysta z 10.8.0.1, a 10.8.0.1 nie odpowiada na porcie UDP 53.
Przyczyną może być jeden z dwóch problemów. Na serwerze nie działa resolver albo zapora serwera odrzuca zapytanie, zanim ono dotrze do serwera. Należy sprawdzić oba przypadki na serwerze.
sudo ss -ulnp | grep ':53'
sudo nft list rulesetDziałający resolver, poprawnie przypisany do adresu, wyświetla wiersz zawierający 10.8.0.1:53 lub 0.0.0.0:53. W systemie Ubuntu przyczyną jest zwykle 127.0.0.53:53. Jest to nasłuchujący lokalnie interfejs systemd-resolved, przypisany do adresu loopback i celowo niedostępny z innych komputerów. Skierowanie klienta VPN do serwera, na którym jedynym resolverem jest ten interfejs, powoduje dokładnie takie przekroczenie limitu czasu.
Należy użyć resolvera nasłuchującego na adresie tunelu oraz dodać regułę zapory umożliwiającą komunikację z nim przez peery.
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'Następnie należy otworzyć port wyłącznie dla ruchu tunelowego. W przypadku nftables należy dodać te dwa wiersze do łańcucha input w /etc/nftables.conf i przeładować konfigurację za pomocą sudo systemctl reload nftables.
udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" acceptW przypadku ufw sudo ufw allow in on wg0 to any port 53 wykonuje to samo zadanie. Nie należy otwierać portu 53 dla publicznego Internetu. Otwarty rekursywny resolver zostanie wykryty przez skanery w ciągu kilku dni i wykorzystany do wzmacniania ataków typu denial of service, a dostawca zauważy ten ruch, zanim zostanie wykryty przez administratora.
Należy ponownie uruchomić dig +short @10.8.0.1 example.com na kliencie. Adres w wynikach oznacza, że ścieżka do resolvera działa, więc klient musi już tylko z niego korzystać. Należy dodać ten wiersz do bloku klienta [Interface] i ponownie uruchomić interfejs za pomocą 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.1Awaria druga: wycieki DNS, ponieważ tunel dzielony nie kieruje zapytań do resolvera
Ta awaria jest poważniejsza, ponieważ wszystko wygląda na działające. Nazwy są rozpoznawane, strony się wczytują, a zapytania przesyłane są jawnym tekstem przez lokalną sieć, której nie zamierzano ufać.
Powodują ją dwie konfiguracje. Pierwsza to klient z AllowedIPs = 0.0.0.0/0, ::/0 i bez wiersza DNS =. wg-quick instaluje trasę domyślną we własnej tablicy routingu i dodaje regułę z suppress_prefixlength 0. Celowo zachowuje to bardziej szczegółowe trasy lokalne, aby komputer nadal mógł łączyć się na przykład z drukarką. Resolver, którego klient nauczył się przez DHCP, zwykle router pod adresem 192.168.1.1, pasuje do jednej z tych tras lokalnych. Ruch jest przesyłany przez tunel. Lokalna sieć nadal otrzymuje pełną listę wyszukiwanych nazw.
Druga konfiguracja to tunel dzielony: AllowedIPs = 10.8.0.0/24 z DNS = 9.9.9.9. Ponieważ 9.9.9.9 nie znajduje się w AllowedIPs, klient nie ma do niego trasy przez tunel. Zapytanie opuszcza więc system przez lokalne łącze, tak jak w pierwszym przypadku.
Należy sprawdzić, który resolver rzeczywiście odpowiada. whoami.akamai.net to publiczna nazwa testowa, która odpowiada adresem IP resolvera rekurencyjnego wysyłającego zapytanie. Umożliwia to porównanie odpowiedzi z publicznym adresem serwera.
resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53resolvectl status wyświetla jeden blok dla każdego łącza. Jeśli blok dotyczący łącza Ethernet lub bezprzewodowego nadal pokazuje Current DNS Server: 192.168.1.1, a blok wg0 nie pokazuje żadnego wpisu, oznacza to wyciek. Wynik dig +short whoami.akamai.net zawierający adres domowego łącza szerokopasmowego zamiast adresu serwera potwierdza to ze zdalnego punktu końcowego. Wiersz tcpdump stanowi rozstrzygający dowód: prawidłowe dane wyjściowe kierują każdy pakiet do portu 53 przez wg0, a w przypadku wycieku pakiety są kierowane przez wlan0 lub enp3s0.
Naprawa składa się z dwóch części. Obie są wymagane. Należy ustawić DNS na adres znajdujący się wewnątrz tunelu i upewnić się, że ten adres znajduje się w 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 znajduje się w 10.8.0.0/24, dlatego zapytanie jest szyfrowane i wysyłane do serwera. Jeśli na tunelu dzielonym wymagany jest publiczny resolver, należy dodać go jako trasę hosta: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. Pakiety będą wtedy przesyłane przez tunel, ale lokalna sieć nadal może rozpoznać, że wybrano tego dostawcę na podstawie wcześniejszych sesji. Resolver uruchomiony samodzielnie eliminuje ten problem.
Przypisywanie resolvera jest jedną z widocznych różnic między ręcznie konfigurowanym WireGuard a koordynowaną siecią mesh. Jest to część kompromisu opisanego w WireGuard w porównaniu z Tailscale. Uruchomienie samodzielnie hostowanego serwera sterującego Headscale zapewnia taką koordynację bez przekazywania materiału kluczowego podmiotowi trzeciemu.
Trzeci problem: resolvconf i systemd-resolved konkurują ze sobą na klientach Linux
Klienci macOS, Windows, iOS i Android stosują DNS = za pośrednictwem oficjalnej aplikacji i zwykle nie powodują problemów. W systemie Linux ustawienie jest stosowane przez skrypt powłoki, który musi ustalić, z którego z kilku menedżerów resolvera korzysta system.
Pierwszy problem jest wyraźny. sudo wg-quick up wg0 kończy działanie z komunikatem:
resolvconf: command not foundwg-quick wywołuje resolvconf, ale ten plik binarny nie jest zainstalowany. Należy zainstalować implementację współpracującą z systemd-resolved, a następnie ponownie aktywować interfejs.
sudo apt install -y openresolv
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0Drugi problem jest niewidoczny i to on może zająć wiele godzin. Interfejs zostaje aktywowany, resolvectl status wg0 poprawnie wyświetla DNS Servers: 10.8.0.1, ale zapytania nadal są kierowane do starego resolvera. systemd-resolved przechowuje osobną listę resolverów dla każdego łącza i wybiera łącze dla każdego zapytania. Jeśli żadne łącze nie jest oznaczone jako trasa domyślna dla nazw, nadal używany jest resolver łącza bezprzewodowego, ponieważ to łącze ma domenę wyszukiwania, a interfejs użytkownika jej nie ma.
Resolver i trasę domyślną należy ustawić w tym samym kroku. %i jest rozwijane do nazwy interfejsu, dlatego ten blok działa bez zmian dla dowolnego interfejsu.
[Interface]
Address = 10.8.0.2/32
PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.
PostDown = resolvectl revert %iNależy usunąć wiersz DNS = podczas używania PostUp w ten sposób, ponieważ w przeciwnym razie dwa mechanizmy zapisują stan resolvera, a później tylko jeden z nich wykonuje czyszczenie. Argument ~. jest kluczowym elementem: oznacza wg0 jako domenę routingu dla każdej nazwy, dzięki czemu systemd-resolved wysyła tam wszystkie zapytania zamiast wybierać łącze dla każdego zapytania. Należy to zweryfikować.
resolvectl status wg0Prawidłowy wynik zawiera DNS Servers: 10.8.0.1 i Default Route: yes. Jeśli Default Route odczytuje no, część resolvectl domain nie została wykonana i system ponownie wybiera łącze.
Warto wskazać jeszcze jeden przypadek. Jeśli /etc/resolv.conf jest zwykłym plikiem, a nie dowiązaniem symbolicznym do /run/systemd/resolve/stub-resolv.conf, zarządza nim inny komponent, zwykle NetworkManager albo środowisko uruchomieniowe kontenera. Przed rozpoczęciem dalszego debugowania należy uruchomić ls -l /etc/resolv.conf, ponieważ narzędzie, które przepisuje ten plik przy każdej zmianie sieci, cofnie wprowadzone zmiany w najmniej odpowiednim momencie.
Ulepszenie: własny resolver filtrujący przez tunel
Gdy zapytania niezawodnie przechodzą przez tunel, resolver na drugim końcu staje się punktem kontroli. Uruchomienie AdGuard Home w tym miejscu zapewnia każdemu podłączonemu urządzeniu filtrowanie na podstawie list blokowania oraz rejestrowanie zapytań, bez instalowania oprogramowania klienckiego i bez konfiguracji poszczególnych urządzeń. Oficjalny skrypt instalacyjny, sprawdzony w lipcu 2026, jest jednowierszowy.
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -vPodczas pierwszego uruchomienia kreator konfiguracji nasłuchuje na porcie 3000. Należy uzyskać do niego dostęp przez tunel pod adresem http://10.8.0.1:3000 zamiast otwierać ten port publicznie. W kreatorze należy ustawić zarówno adres nasłuchiwania DNS, jak i adres nasłuchiwania interfejsu administracyjnego na 10.8.0.1. Jeśli unbound z pierwszego przypadku awarii nadal używa tego samego adresu, należy najpierw zatrzymać ten proces za pomocą sudo systemctl disable --now unbound, ponieważ dwa procesy nie mogą powiązać portu UDP 53 z jednym adresem, a drugi zakończy działanie z błędem listen udp 10.8.0.1:53: bind: address already in use.
Konfiguracje klientów nie wymagają zmian, jeśli już zawierają DNS = 10.8.0.1. Dziennik zapytań będzie odtąd wyświetlać każde wyszukanie wykonane przez każdego peera. Jest to rzeczywista decyzja dotycząca prywatności, a nie korzyść bez kosztów: zaufanie zostaje przeniesione z dostawcy internetu na użytkownika, który musi samodzielnie aktualizować ten serwer. Serwer udostępniony w internecie wymaga wcześniejszego wdrożenia podstawowych zabezpieczeń, a pierwsze dziesięć minut na nowym VPS obejmuje te czynności.
FAQ
Dlaczego tunel WireGuard łączy się, ale nazwy nie są rozpoznawane?
Tunel przenosi pakiety i w ogóle nie obsługuje nazw. Działający tunel przy niesprawnych zapytaniach oznacza, że wskazany resolver nie odpowiada. Na kliencie należy wykonać test za pomocą dig +short @10.8.0.1 example.com. Odpowiedź communication timed out oznacza, że na tym adresie tunelu nie nasłuchuje żaden resolver albo zapora serwera odrzuca pakiety UDP na porcie 53 przychodzące przez wg0. Częstą przyczyną jest to, że stub systemd-resolved nasłuchuje wyłącznie na 127.0.0.53. Najpierw należy naprawić nasłuchiwanie, a następnie otworzyć port wyłącznie dla wg0.
Jak sprawdzić, czy zapytania DNS wyciekają poza WireGuard?
Na kliencie należy wykonać sudo tcpdump -ni any -c 10 port 53 i podczas przeglądania obserwować kolumnę interfejsu. Każdy pakiet powinien być przesyłany przez wg0. Jeśli pakiety pojawiają się na interfejsie bezprzewodowym lub ethernetowym, zapytania opuszczają system w postaci jawnej. dig +short whoami.akamai.net zapewnia dodatkową weryfikację, ponieważ odpowiada publicznym adresem resolvera rekurencyjnego, który wykonał zapytanie. Odpowiedź zawierająca adres inny niż adres serwera potwierdza wyciek.
Czy linia DNS = jest potrzebna w przypadku tunelu dzielonego?
Tak. Adres resolvera musi również znajdować się w zakresie AllowedIPs, w przeciwnym razie klient nie ma do niego trasy. Przy użyciu AllowedIPs = 10.8.0.0/24 resolver pod adresem 10.8.0.1 jest objęty zakresem, a zapytanie jest szyfrowane. Publiczny resolver, taki jak 9.9.9.9, nie jest objęty tym zakresem. Zapytanie opuszcza więc system przez lokalne łącze, mimo że linia DNS wygląda prawidłowo.
Dlaczego resolvectl pokazuje właściwy serwer, ale zapytania nadal są kierowane gdzie indziej?
systemd-resolved przechowuje osobną listę resolverów dla każdego łącza i wybiera łącze dla każdego zapytania. Dlatego prawidłowy wpis na wg0 jest ignorowany, gdy inne łącze ma trasę domyślną dla nazw. Należy dodać PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. do bloku klienta [Interface] i usunąć linię DNS =. Następnie resolvectl status wg0 powinno zgłosić Default Route: yes.
Którego klienta należy naprawić najpierw, gdy kilka z nich jest niesprawnych?
Najpierw należy naprawić jednego klienta Linux, ponieważ tylko ta platforma pokazuje mechanizm działania. resolvectl status i tcpdump wskazują, który resolver odpowiedział oraz który interfejs przeniósł pakiet. Aplikacje na telefonach i komputerach stosują te same wartości DNS i AllowedIPs, ale nie pokazują szczegółów konfiguracji. Po poprawnym skonfigurowaniu klienta Linux można skopiować konfigurację, której działanie zostało już potwierdzone.