DNS через WireGuard: 3 причини збою та виправлення
Тунель WireGuard працює, але DNS не відповідає або запити витікають до локального маршрутизатора. Визначте одну з 3 причин і виправте її.
Чому DNS перестає працювати одразу після встановлення тунелю WireGuard
DNS через WireGuard не працює належним чином у трьох випадках. Для кожного випадку потрібне окреме виправлення. Іноді не визначається жодне ім’я. Іноді імена визначаються, але запити виходять із вашого комп’ютера поза тунелем. Іноді власний менеджер резолвера клієнта перезаписує налаштування за кілька секунд після запуску інтерфейсу. Тунель майже ніколи не є причиною проблеми. Причиною є один рядок, який повідомляє клієнту, до якого резолвера звертатися, а також маршрутизація, що визначає шлях пакетів до цього резолвера.
WireGuard передає IP-пакети й нічого не знає про DNS (систему доменних імен, службу, яка перетворює такі імена, як example.com, на IP-адреси). Рядок DNS = у блоці клієнта [Interface] не є налаштуванням WireGuard. Його читає wg-quick — оболонка-обгортка, яка запускає інтерфейс, а 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.1wg show має показувати вузол із нещодавнім latest handshake, а обидві перевірки ping мають завершуватися відповіддю. Якщо ping 1.1.1.1 завершується тайм-аутом, проблема пов’язана з пересиланням пакетів або NAT (трансляцією мережевих адрес), а не з DNS. Жодні налаштування резолвера не допоможуть. У всіх прикладах на цій сторінці використовується 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Помилка два: витоки DNS, оскільки split tunnel не маршрутизує резолвер
Це гірше, оскільки все начебто працює. Імена розпізнаються, сторінки завантажуються, а запити передаються у відкритому вигляді через локальну мережу, якій ви не хотіли довіряти.
Причиною є дві конфігурації. Перша — клієнт із AllowedIPs = 0.0.0.0/0, ::/0 без рядка DNS =. wg-quick встановлює маршрут за замовчуванням у власній таблиці маршрутизації та додає правило з suppress_prefixlength 0. Це навмисно зберігає локальні маршрути з конкретнішими префіксами, щоб комп’ютер і надалі міг підключатися до свого принтера. Резолвер, адресу якого клієнт отримав через DHCP, зазвичай маршрутизатор за адресою 192.168.1.1, відповідає одному з таких локальних маршрутів. Ваш трафік проходить через тунель. Локальна мережа все одно отримує повний список імен, які ви шукаєте.
Друга конфігурація — split tunnel: AllowedIPs = 10.8.0.0/24 із DNS = 9.9.9.9. Оскільки 9.9.9.9 не входить до AllowedIPs, клієнт не має маршруту до нього через тунель, тому запит виходить через локальне з’єднання так само, як у першому випадку.
Перевірте, який резолвер насправді відповідає. whoami.akamai.net — це загальнодоступне тестове ім’я, яке повертає IP-адресу рекурсивного резолвера, що надіслав запит. Так можна порівняти цю адресу із загальнодоступною адресою вашого сервера.
resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53resolvectl status виводить окремий блок для кожного з’єднання. Якщо в блоці вашого ethernet- або wireless-з’єднання все ще вказано Current DNS Server: 192.168.1.1, а в блоці wg0 нічого не вказано, це і є витік. Якщо dig +short whoami.akamai.net повертає адресу вашого домашнього broadband-з’єднання, а не адресу сервера, це підтверджує витік на віддаленому кінці. Рядок tcpdump є остаточним доказом: за коректної роботи всі пакети до порту 53 проходять через wg0, а під час витоку — через wlan0 або enp3s0.
Виправлення складається з двох частин, і потрібні обидві. Установіть 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/1610.8.0.1 входить до 10.8.0.0/24, тому запит шифрується та надсилається на сервер. Якщо ви наполягаєте на використанні загальнодоступного резолвера через split tunnel, додайте для нього маршрут до вузла: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. Пакети проходитимуть через тунель, хоча локальна мережа все одно зможе визначити, якого постачальника ви обрали, з попередніх сеансів. Резолвер, який ви запускаєте самостійно, усуває цю проблему.
Призначення резолвера — одна з помітних відмінностей між WireGuard, налаштованим вручну, та координованою mesh-мережею. Це є частиною компромісів, описаних у порівнянні WireGuard і Tailscale. Запуск власного керувального сервера Headscale забезпечує таку координацію без передавання матеріалу ключів третій стороні.
Помилка третя: resolvconf і systemd-resolved конфліктують на клієнтах Linux
Клієнти macOS, Windows, iOS та Android застосовують DNS = через офіційну програму й майже не створюють проблем. У Linux це налаштування застосовує shell-скрипт, якому доводиться визначати, який із кількох менеджерів резолвінгу використовується.
Перша помилка одразу помітна. sudo wg-quick up wg0 завершується з таким повідомленням:
resolvconf: command not foundwg-quick викликає resolvconf, але цей двійковий файл не встановлено. Встановіть реалізацію, яка взаємодіє із systemd-resolved, а потім знову активуйте інтерфейс.
sudo apt install -y openresolv
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, але запити все одно надсилаються до старого резолвера. systemd-resolved зберігає окремий список резолверів для кожного з’єднання та вибирає з’єднання для кожного запиту. Якщо жодне з’єднання не позначене як маршрут за замовчуванням для імен, він продовжує використовувати резолвер бездротового з’єднання, оскільки це з’єднання має домен пошуку, а ваше — ні.
Задайте резолвер і в тому самому кроці призначте маршрут за замовчуванням. %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 у такий спосіб, оскільки інакше два механізми записуватимуть стан резолвера, а очищення після завершення виконуватиме лише один із них. Аргумент ~. є важливою частиною: він позначає wg0 як домен маршрутизації для кожного імені, тому systemd-resolved надсилає всі запити туди, а не вибирає з’єднання для кожного запиту. Перевірте результат.
resolvectl status wg0Коректний результат містить DNS Servers: 10.8.0.1 і Default Route: yes. Якщо Default Route показує no, частина resolvectl domain не виконалася, і ви знову повернулися до вибору з’єднання.
Варто згадати ще один випадок. Якщо /etc/resolv.conf є звичайним файлом, а не символічним посиланням на /run/systemd/resolve/stub-resolv.conf, ним керує інший компонент, зазвичай NetworkManager або середовище виконання контейнерів. Виконайте ls -l /etc/resolv.conf до початку будь-якого іншого налагодження, оскільки інструмент, який перезаписує цей файл після кожної зміни мережі, скасує вашу роботу в найбільш невідповідний момент.
Оновлення: власний фільтрувальний резолвер через тунель
Коли запити стабільно проходять через тунель, резолвер на віддаленому кінці стає точкою керування. Якщо запустити там AdGuard Home, усі підключені пристрої отримають фільтрацію за списками блокування та журнал запитів без клієнтського програмного забезпечення й окремого налаштування кожного пристрою. Офіційний скрипт встановлення, перевірений у July 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 до однієї адреси, і другий процес завершується з помилкою listen udp 10.8.0.1:53: bind: address already in use.
Конфігурації клієнтів не потребують змін, якщо в них уже вказано DNS = 10.8.0.1. Тепер журнал запитів показуватиме кожен запит від кожного вузла. Це реальне рішення щодо конфіденційності, а не безкоштовна перевага: ви переносите довіру від свого інтернет-провайдера на себе й самі повинні своєчасно встановлювати оновлення на цьому сервері. Сервер, доступний з інтернету, спочатку потребує базового захисту, а перші десять хвилин на новому VPS описують ці кроки.
FAQ
Чому мій тунель WireGuard підключається, але імена не розв’язуються?
Тунель передає пакети й узагалі не обробляє імена. Тому робочий тунель і несправні запити означають, що вказаний вами резолвер не відповідає. Перевірте це на клієнті за допомогою dig +short @10.8.0.1 example.com. Відповідь communication timed out означає, що на цій адресі тунелю ніхто не прослуховує запити, часто тому, що заглушка systemd-resolved прослуховує лише 127.0.0.53, або що брандмауер сервера відкидає UDP-порт 53, який надходить через wg0. Спочатку виправте прослуховування, а потім відкрийте порт лише для wg0.
Як перевірити, чи не витікають мої DNS-запити через WireGuard?
Запустіть sudo tcpdump -ni any -c 10 port 53 на клієнті та стежте за стовпцем інтерфейсу під час перегляду вебсторінок. Кожен пакет має проходити через wg0. Якщо вони з’являються на бездротовому або ethernet-інтерфейсі, запити виходять у незашифрованому вигляді. dig +short whoami.akamai.net надає додаткову перевірку, оскільки відповідає загальнодоступною адресою рекурсивного резолвера, який виконав запит. Тому адреса, що не збігається з адресою вашого сервера, підтверджує витік.
Чи потрібен мені рядок DNS =, якщо я використовую тунель із роздільною маршрутизацією?
Так. Адреса резолвера також має входити до AllowedIPs, інакше клієнт не матиме маршруту до неї. За допомогою AllowedIPs = 10.8.0.0/24 резолвер за адресою 10.8.0.1 буде охоплено, а запит шифруватиметься. Загальнодоступний резолвер, наприклад 9.9.9.9, не охоплюється, тому запит виходить через локальне з’єднання, навіть якщо рядок DNS виглядає правильним.
Чому resolvectl показує правильний сервер, але запити все одно надсилаються в інше місце?
systemd-resolved підтримує окремий список резолверів для кожного з’єднання та вибирає з’єднання для кожного запиту. Тому правильний запис у wg0 ігнорується, якщо інше з’єднання містить маршрут за замовчуванням для імен. Додайте PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. до блоку [Interface] клієнта та видаліть рядок DNS =. Після цього resolvectl status wg0 має показати Default Route: yes.
Якого клієнта виправляти першим, якщо кілька клієнтів не працюють?
Спочатку виправте один клієнт Linux, оскільки лише ця платформа показує механізм роботи. resolvectl status і tcpdump показують, який резолвер відповів і через який інтерфейс пройшов пакет. Програми на телефонах і настільних комп’ютерах застосовують ті самі значення DNS і AllowedIPs без видимих внутрішніх налаштувань. Тому після виправлення клієнта Linux ви копіюєте конфігурацію, яку вже перевірили.