SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor

DNS через WireGuard: 3 причини збою та виправлення

Тунель WireGuard працює, але DNS не відповідає або запити витікають до локального маршрутизатора. Визначте одну з 3 причин і виправте її.

Чому DNS перестає працювати одразу після встановлення тунелю WireGuard

DNS через WireGuard не працює у 3 випадках, і для кожного потрібне окреме виправлення. Або нічого не дозволяється розв’язати, або імена розв’язуються, але запити виходять із вашої машини поза тунелем, або власний менеджер резолвера клієнта перезаписує налаштування через кілька секунд після запуску інтерфейсу. Тунель майже ніколи не є проблемою. Проблема полягає в одному рядку, який вказує клієнту, до якого резолвера звертатися, і в маршрутизації, що визначає шлях пакетів до цього резолвера.

WireGuard передає IP-пакети й нічого не знає про DNS (систему доменних імен, сервіс, який перетворює імена, наприклад example.com, на IP-адреси). Рядок DNS = у клієнтському блоці [Interface] не є налаштуванням WireGuard. Його читає wg-quick — shell-обгортка, яка запускає інтерфейс, а wg-quick потім змінює конфігурацію резолвера клієнта, поки тунель працює, і відновлює її під час wg-quick down. Отже, кожна описана нижче проблема пов’язана з маршрутизацією або з проблемою wg-quick, а не з криптографією. Якщо тунель ще не налаштований, почніть зі сторінки самостійно розгорнута VPN WireGuard на власному VPS і після цього поверніться до цієї сторінки.

Перш ніж змінювати DNS, переконайтеся, що тунель працює справно.

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

Команда wg show має показати peer із недавнім значенням latest handshake, а обидва ping мають отримати відповідь. Якщо ping 1.1.1.1 завершується тайм-аутом, найімовірніше, проблема пов’язана з forwarding або NAT (перетворенням мережевих адрес), а не з DNS. Жодні налаштування резолвера не допоможуть. Брандмауер, який відкидає ICMP, спричиняє такий самий тайм-аут, тому перед зміною правил forwarding перевірте це за допомогою curl -sI https://1.1.1.1. Якщо ping отримує відповіді, але пропускна здатність різко падає після початку передавання реального трафіку, це окрема проблема. Низька швидкодія WireGuard майже завжди пов’язана з MTU, а не з будь-яким параметром на цій сторінці. Усі приклади тут використовують 10.8.0.0/24 як підмережу тунелю, а 10.8.0.1 — як адресу сервера в тунелі. Замініть їх на власні значення.

Помилка 1: нічого не визначається, тому що resolver не відповідає

Симптом однозначний. ping 1.1.1.1 працює, а curl https://example.com повертає таке:

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

Безпосередньо запитайте tunnel resolver із клієнта. dig входить до складу пакета dnsutils в Ubuntu і Debian.

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

Перша команда повертає адресу. Це доводить, що пакети проходять до інтернету через tunnel. Друга команда нічого не повертає та виводить ;; communication timed out; no servers could be reached. Це і є повний діагноз: клієнт налаштований на 10.8.0.1, а 10.8.0.1 не відповідає на UDP-порті 53.

Це спричиняють дві причини. Або на сервері не запущено resolver, або firewall сервера відкидає запит до його надходження. Перевірте обидва варіанти на сервері.

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

Запущений і правильно прив’язаний resolver показує рядок із 10.8.0.1:53 або 0.0.0.0:53. В Ubuntu проблема зазвичай пов’язана з 127.0.0.53:53: це stub listener systemd-resolved, який прив’язаний до loopback-адреси та навмисно недоступний з інших машин. Якщо вказати VPN-клієнту сервер, єдиним resolver якого є цей stub, виникає саме такий тайм-аут.

Потрібен resolver, який слухає tunnel-адресу, а також одне правило firewall, що дозволяє peers звертатися до нього.

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'

Потім відкрийте порт лише для tunnel-трафіку. У nftables додайте ці два рядки до ланцюжка input у /etc/nftables.conf та перезавантажте конфігурацію за допомогою sudo systemctl reload nftables.

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

В ufw ту саму функцію виконує sudo ufw allow in on wg0 to any port 53. Ніколи не відкривайте порт 53 для загальнодоступного інтернету. Відкритий recursive resolver сканери знаходять протягом кількох днів і використовують для посилення атак типу denial of service. Ваш провайдер помітить цей трафік раніше за вас.

Повторно запустіть dig +short @10.8.0.1 example.com на клієнті. Адреса у виводі означає, що шлях до resolver працює, і тепер клієнту потрібно лише використовувати його. Додайте цей рядок до блоку [Interface] клієнта та перезапустіть інтерфейс за допомогою 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

Помилка 2: витоки DNS, оскільки split tunnel не маршрутизує запити до resolver

Ця проблема серйозніша, оскільки все, здається, працює. Імена розв’язуються, сторінки завантажуються, а запити передаються у відкритому вигляді через локальну мережу, якій ви не хотіли довіряти.

Її спричиняють дві конфігурації. Перша — клієнт із AllowedIPs = 0.0.0.0/0, ::/0 без рядка DNS =. wg-quick встановлює маршрут за замовчуванням у власній таблиці маршрутизації та додає правило з suppress_prefixlength 0. Це навмисно зберігає специфічніші локальні маршрути, щоб машина й надалі могла звертатися, наприклад, до принтера. Адреса resolver, яку клієнт отримав через DHCP, зазвичай адреса маршрутизатора 192.168.1.1, відповідає одному з таких локальних маршрутів. Ваш трафік проходить через тунель. Локальна мережа все одно отримує повний список імен, які ви запитуєте.

Друга конфігурація — split tunnel: AllowedIPs = 10.8.0.0/24 із DNS = 9.9.9.9. Оскільки 9.9.9.9 не входить до AllowedIPs, клієнт не має маршруту до цієї адреси через тунель. Тому запит виходить через локальне з’єднання, як і в першому випадку.

Перевірте, який resolver насправді відповідає. whoami.akamai.net — це публічне тестове ім’я, яке повертає IP-адресу recursive resolver, що надіслав запит. Так можна порівняти цю адресу з публічною адресою вашого сервера.

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

resolvectl status виводить окремий блок для кожного з’єднання. Якщо в блоці вашого ethernet- або wireless-з’єднання все ще вказано Current DNS Server: 192.168.1.1, а в блоці wg0 немає жодного запису, це і є витік. Якщо dig +short whoami.akamai.net повертає адресу вашого домашнього broadband-з’єднання, а не адресу сервера, це підтверджує витік із віддаленого боку. Рядок tcpdump остаточно підтверджує результат: за коректної роботи кожен пакет на порт 53, який залишає машину, проходить через wg0. У разі витоку такі пакети проходять через wlan0 або enp3s0. Пакети на lo або з нього до 127.0.0.53 — це обмін даними застосунків із локальним stub systemd-resolved, і вони не є витоком.

Виправлення має дві частини, і потрібні обидві. Встановіть DNS в адресу, яка знаходиться всередині тунелю, і переконайтеся, що ця адреса входить до 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 входить до 10.8.0.0/24, тому запит шифрується та надсилається на сервер. Якщо ви наполягаєте на використанні публічного resolver у split tunnel, додайте для нього маршрут до окремого хоста: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. Пакети проходитимуть через тунель, хоча локальна мережа все одно зможе побачити, якого провайдера ви обрали, за даними попередніх сеансів. Resolver, який ви запускаєте самостійно, усуває цю проблему.

Призначення resolver — одна з помітних відмінностей між WireGuard, налаштованим вручну, і скоординованою mesh-мережею. Це є частиною компромісів, описаних у порівнянні WireGuard і Tailscale. Запуск self-hosted control server Headscale дає таку координацію без передавання ключового матеріалу сторонній організації. Якщо саме остання фраза вас непокоїть, врахуйте: Tailscale ніколи не зберігає ключі, якими шифрується ваш трафік, а точніше питання полягає в тому, що скомпрометований coordination server або викрадений обліковий запис із правами ідентифікації може додати до вашої мережі.

Збій 3: resolvconf і systemd-resolved конфліктують на Linux-клієнтах

Клієнти macOS, Windows, iOS та Android застосовують DNS = через офіційний застосунок і зазвичай не створюють проблем. На Linux це налаштування застосовує shell script, який має визначити, який із кількох менеджерів resolver використовується.

Перший збій очевидний. sudo wg-quick up wg0 завершується з повідомленням:

resolvconf: command not found

wg-quick викликає resolvconf, але цей binary не встановлений. У Debian 12 і Ubuntu 24.04 або новіших версіях команда resolvconf, яка взаємодіє із systemd-resolved, постачається в пакеті systemd-resolved. Установіть цей пакет, а потім знову підніміть інтерфейс. В Ubuntu 22.04 окремого пакета для нього немає. Там створіть compatibility link вручну за допомогою sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf і пропустіть рядок встановлення.

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

Другий збій непомітний. Саме через нього можна втратити цілий вечір. Інтерфейс піднімається, resolvectl status wg0 правильно показує DNS Servers: 10.8.0.1, але запити все одно надходять до старого resolver. systemd-resolved не ігнорує wg0. Ім’я, яке не відповідає жодному routing domain, надсилається DNS-серверам кожного link, що вважається default route. Link із DNS-серверами та без route-only domain за замовчуванням також вважається default route. Тому запит одночасно надходить до 10.8.0.1 і до router у wireless link. Перша успішна відповідь перемагає. Router розташований на один hop ближче й зазвичай відповідає першим. У будь-якому разі запит уже вийшов у мережу у відкритому вигляді. Імена в межах власного search domain wireless link, наприклад lan, надсилаються лише до цього link, оскільки перемагає найспецифічніший routing domain.

Коли wg-quick застосовує DNS = через власну обгортку systemd resolvconf, він уже запитує catch-all routing domain. Тому цей збій здебільшого виникає, коли налаштування застосував інший варіант resolvconf або network manager. Явне встановлення параметра усуває невизначеність.

Встановіть resolver і одночасно призначте catch-all routing domain. %i розгортається в ім’я інтерфейсу, тому цей блок працюватиме без змін для будь-якого інтерфейсу.

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

Видаліть рядок DNS =, якщо в такому режимі використовуєте PostUp. Інакше два механізми записуватимуть стан resolver, а після завершення роботи очищення виконає лише один із них. Аргумент ~. є важливою частиною: він робить wg0 route-only domain для кожного імені. Завдяки цьому systemd-resolved припиняє надсилати до інших link імена, яким не відповідає специфічніший domain. Імена в межах власного domain іншого link, наприклад lan, і далі надсилатимуться до цього link. Для локальних host це зазвичай потрібна поведінка. Перевірте результат.

resolvectl status wg0

У коректному виводі є DNS Servers: 10.8.0.1 і DNS Domain: ~.. Якщо ~. відсутній, частина resolvectl domain не виконалася, і wg0 знову конкурує з іншими link. Не використовуйте прапорець default route для перевірки. Він показує yes (або +DefaultRoute у рядку Protocols, залежно від версії systemd) навіть без ~., оскільки link без route-only domain і так вважається default route.

Варто згадати ще один випадок. Якщо /etc/resolv.conf є звичайним файлом, а не symlink на /run/systemd/resolve/stub-resolv.conf, ним керує інший компонент, зазвичай NetworkManager або container runtime. Виконайте ls -l /etc/resolv.conf до початку іншого налагодження, оскільки інструмент, який переписує цей файл після кожної зміни мережі, скасує ваші зміни в найневдаліший момент.

Оновлення: власний filtering resolver через тунель

Коли запити стабільно проходять через тунель, resolver на віддаленому кінці стає точкою контролю. Запуск AdGuard Home там забезпечує кожному підключеному пристрою фільтрацію за blocklist і журнал DNS-запитів без клієнтського програмного забезпечення та окремого налаштування для кожного пристрою. Офіційний інсталяційний скрипт, перевірений у жовтні 2026 року, складається з одного рядка.

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

Під час першого запуску майстер налаштування слухає порт 3000. Відкрийте його через тунель за адресою http://10.8.0.1:3000, а не відкривайте цей порт у публічному доступі. У майстрі задайте для адреси прослуховування DNS і адреси прослуховування адміністративного інтерфейсу значення 10.8.0.1. Якщо unbound з першого збою досі використовує ту саму адресу, спочатку зупиніть його командою sudo systemctl disable --now unbound, оскільки обидва процеси конкуруватимуть за UDP-порт 53 на одній адресі, а AdGuard Home завершить роботу з помилкою listen udp 10.8.0.1:53: bind: address already in use.

Змінювати конфігурації клієнтів не потрібно, якщо в них уже вказано DNS = 10.8.0.1. Тепер у журналі DNS-запитів відображатиметься кожен запит, який надходить до нього від кожного peer, якщо ви не виключите клієнта з журналювання. Це реальне рішення щодо конфіденційності, а не безкоштовна перевага: ви переносите довіру від свого інтернет-провайдера до себе, а підтримувати цей сервер у пропатченому стані доведеться вам. Сервер, відкритий в інтернет, спочатку потрібно належно захистити, і перші десять хвилин на новому VPS описують ці кроки.

FAQ

Чому тунель WireGuard підключається, але імена не визначаються?

Тунель передає пакети й узагалі не обробляє імена. Тому справний тунель і несправне визначення імен означають, що вказаний resolver не відповідає. Перевірте це на клієнті за допомогою dig +short @10.8.0.1 example.com. Відповідь communication timed out означає, що на цій адресі тунелю не прослуховується жоден resolver. Часто причина в тому, що stub systemd-resolved прослуховує лише 127.0.0.53. Інша можлива причина — firewall сервера відкидає UDP-порт 53, що надходить через wg0. Спочатку виправте listener, а потім відкрийте порт лише для wg0.

Як перевірити, чи не витікають DNS-запити через WireGuard?

Запустіть sudo tcpdump -ni any -c 10 port 53 на клієнті й стежте за стовпцем інтерфейсу під час перегляду вебсторінок. Кожен пакет, що залишає машину, має проходити через wg0. Пакети на lo або з нього до 127.0.0.53 — це локальний stub systemd-resolved, і це нормально. Якщо вони з’являються на бездротовому або ethernet-інтерфейсі, запити надсилаються незашифрованими. dig +short whoami.akamai.net дає додаткову перевірку, оскільки відповідає публічною адресою recursive resolver, який виконав запит. Якщо адреса відрізняється від адреси вашого сервера, це підтверджує витік.

Чи потрібен рядок DNS =, якщо я використовую split tunnel?

Так. Адреса resolver також має входити до AllowedIPs, інакше клієнт не матиме маршруту до нього. З AllowedIPs = 10.8.0.0/24 resolver за адресою 10.8.0.1 буде охоплено, а запит передаватиметься в зашифрованому вигляді. Публічний resolver, наприклад 9.9.9.9, не охоплюється. Тому запит виходитиме через локальне з’єднання, навіть якщо рядок DNS виглядає правильно.

Чому resolvectl показує правильний сервер, але запити все одно надсилаються в інше місце?

systemd-resolved одночасно надсилає ім’я, яке не відповідає жодному routing domain, через усі посилання з default route, зокрема через wg0, і повертає першу успішну відповідь. Зазвичай її надсилає локальний маршрутизатор. Додайте PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. до блоку [Interface] клієнта й видаліть рядок DNS =. Після цього resolvectl status wg0 має повідомляти DNS Domain: ~., а кожне ім’я, для якого інший link не заявляє конкретніший домен, надсилатиметься лише до wg0.

Якого клієнта слід виправити першим, якщо несправні кілька?

Спочатку виправте один Linux-клієнт, оскільки лише ця платформа дає змогу побачити механізм роботи. resolvectl status і tcpdump показують, який resolver надав відповідь і через який інтерфейс пройшов пакет. Застосунки на телефонах і настільних системах застосовують ті самі значення DNS і AllowedIPs без доступної для перегляду внутрішньої конфігурації. Тому після виправлення Linux-клієнта ви копіюєте конфігурацію, яку вже перевірили.