Полная настройка WG-Easy → AdGuard Home → Unbound на Ubuntu 26.04 без IPv6

Пошаговая настройка WG-Easy, AdGuard Home и Unbound на Ubuntu 26.04 VPS: WireGuard VPN, DNS через AdGuard, рекурсивный Unbound, принудительный DNS, закрытые Web UI и отключение IPv6.

Вступление

В этом мануале собирается связка:

WG-Easy → AdGuard Home → Unbound

Итоговая схема будет такой:

VPN-клиент
WG-Easy / WireGuard
AdGuard Home: 10.42.42.43:53
Unbound: 10.42.42.44:5335
Root DNS servers

Что получится:

WG-Easy — создаёт WireGuard-клиентов, QR-коды и .conf
AdGuard Home — принимает DNS-запросы клиентов и фильтрует их
Unbound — работает как рекурсивный кэширующий DNS-резолвер

Unbound — это validating, recursive, caching DNS resolver, то есть он может сам рекурсивно резолвить домены и кэшировать ответы, а не просто пересылать всё в Cloudflare или Google.

Как будет работать

Пользователь подключается к WireGuard-клиенту, получает VPN-адрес, например:

10.8.0.2

В его WireGuard-конфиге будет:

DNS = 10.42.42.43
AllowedIPs = 0.0.0.0/0

Это значит:

весь IPv4-трафик идёт через VPN
DNS идёт в AdGuard Home
AdGuard Home отправляет upstream в Unbound
Unbound сам резолвит домены

Даже если пользователь вручную поменяет DNS на 1.1.1.1 или 8.8.8.8, обычный DNS на 53/tcp и 53/udp будет принудительно перенаправляться в AdGuard Home.

Итоговые адреса и порты

Docker network:
10.42.42.0/24
WG-Easy container:
10.42.42.42
AdGuard Home:
10.42.42.43
Unbound:
10.42.42.44
WireGuard VPN:
51820/udp наружу
WG-Easy Web UI:
127.0.0.1:51821
AdGuard Web UI:
127.0.0.1:3000
AdGuard DNS:
10.42.42.43:53 внутри Docker-сети
Unbound DNS:
10.42.42.44:5335 внутри Docker-сети

Наружу открывается только:

22/tcp
51820/udp

Наружу не открываем:

51821/tcp
3000/tcp
53/tcp
53/udp
5335/tcp
5335/udp

Важно

В этой версии мануала IPv6 не используется.

WG-Easy: DISABLE_IPV6=true
Docker network: только IPv4
WireGuard client AllowedIPs: только 0.0.0.0/0
WireGuard client config: без ::/0

В WG-Easy v15 есть переменная DISABLE_IPV6=true. Она отключает IPv6-поддержку у WG-Easy, но IPv6 CIDR всё равно может отображаться в Web UI как поле формы.

Поэтому IPv6 CIDR в интерфейсе не нужно удалять — оставь валидное значение вроде fd42:42:42::/64.

1. Установка Docker на Ubuntu 26.04

В этом мануале Docker устанавливается через официальный APT-репозиторий Docker, а не через быстрый скрипт:

curl -fsSL https://get.docker.com | sudo sh

Для чистой установки Ubuntu 26.04 лучше использовать официальный репозиторий Docker. Так Docker Engine и Docker Compose Plugin будут обновляться штатно через apt.

Шаг 1. Удалить конфликтующие старые пакеты

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
    sudo apt remove -y "$pkg" 2>/dev/null || true
done

Эта команда удаляет старые или конфликтующие пакеты Docker, если они были установлены из стандартного репозитория Ubuntu.

Шаг 2. Установить зависимости

sudo apt update
sudo apt install ca-certificates curl -y

Что делает команда:

ca-certificates
— нужны для проверки HTTPS-сертификатов
curl
— нужен для скачивания GPG-ключа Docker

Шаг 3. Добавить официальный GPG-ключ Docker

sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

Проверить:

ls -l /etc/apt/keyrings/docker.asc

Ожидаемо должен быть файл:

/etc/apt/keyrings/docker.asc

Шаг 4. Проверить codename Ubuntu

. /etc/os-release && echo "$VERSION_CODENAME"

Для Ubuntu 26.04 должно быть:

resolute

Проверить архитектуру:

dpkg --print-architecture

Обычно на VPS будет:

amd64

Шаг 5. Создать Docker APT source

Открыть файл:

sudo nano /etc/apt/sources.list.d/docker.sources

Вставить:

Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: resolute
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc

Если VPS использует не amd64, замени строку:

Architectures: amd64

на свою архитектуру из команды:

dpkg --print-architecture

Шаг 6. Установить Docker Engine и Docker Compose Plugin

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

Что проверить после шага

docker --version
docker compose version
sudo systemctl status docker --no-pager
sudo docker run hello-world

Ожидаемо:

Docker установлен
Docker Compose Plugin работает
служба docker active/running
контейнер hello-world запускается

Если hello-world запустился без ошибок, Docker готов к установке WG-Easy, AdGuard Home и Unbound.

2. Создать папки проекта

sudo mkdir -p /opt/wg-easy
sudo mkdir -p /opt/wg-easy/adguard/work
sudo mkdir -p /opt/wg-easy/adguard/conf
sudo mkdir -p /opt/wg-easy/unbound/data
cd /opt/wg-easy

Права для AdGuard:

sudo chmod -R 700 /opt/wg-easy/adguard/work
sudo chmod -R 700 /opt/wg-easy/adguard/conf

AdGuard Home Docker-образ использует две постоянные директории: одну для рабочих данных и одну для конфигурации.

3. Создать Dockerfile для Unbound

Откройте файл:

sudo nano /opt/wg-easy/unbound/Dockerfile

Вставьте:

FROM alpine:3.21

RUN apk add --no-cache unbound ca-certificates wget bind-tools

COPY unbound.conf /etc/unbound/unbound.conf
COPY entrypoint.sh /entrypoint.sh

RUN chmod +x /entrypoint.sh

EXPOSE 5335/tcp 5335/udp

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD dig @127.0.0.1 -p 5335 example.com A +short >/dev/null || exit 1

ENTRYPOINT ["/entrypoint.sh"]

4. Создать entrypoint для Unbound

Откройте файл:

sudo nano /opt/wg-easy/unbound/entrypoint.sh

Вставьте:

#!/bin/sh
set -e

mkdir -p /var/lib/unbound

if [ ! -f /var/lib/unbound/root.hints ]; then
  wget -q -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
fi

unbound-anchor -a /var/lib/unbound/root.key || true

chown -R unbound:unbound /var/lib/unbound

exec unbound -d -c /etc/unbound/unbound.conf

5. Создать конфиг Unbound

Откройте файл:

sudo nano /opt/wg-easy/unbound/unbound.conf

Вставьте:

server:
    verbosity: 1

    interface: 0.0.0.0
    port: 5335

    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes

    access-control: 127.0.0.0/8 allow
    access-control: 10.42.42.0/24 allow

    root-hints: "/var/lib/unbound/root.hints"
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    username: "unbound"
    directory: "/var/lib/unbound"

    hide-identity: yes
    hide-version: yes

    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-referral-path: yes
    harden-algo-downgrade: yes

    use-caps-for-id: no
    qname-minimisation: yes
    minimal-responses: yes

    edns-buffer-size: 1232

    prefetch: yes
    prefetch-key: yes

    msg-cache-size: 64m
    rrset-cache-size: 128m

    cache-min-ttl: 0
    cache-max-ttl: 86400

    val-clean-additional: yes

Что делает этот конфиг

do-ip6: no
— IPv6 в Unbound отключён
port: 5335
— Unbound слушает не 53, а 5335
access-control 10.42.42.0/24
— разрешены только контейнеры из Docker-сети
root-hints
— Unbound знает root DNS servers
auto-trust-anchor-file
— DNSSEC trust anchor
prefetch
— кэш прогревается заранее

6. Создать финальный docker-compose.yml

Откройте файл:

sudo nano /opt/wg-easy/docker-compose.yml

Вставьте:

📦 Скачать архив

WG-Easy официально использует Docker Compose как основной пример запуска, а для v15 рекомендуется использовать тег ghcr.io/wg-easy/wg-easy:15, а не полагаться на старый latest.

Важно

В compose-файле специально нет:

- "53:53/tcp"
- "53:53/udp"

Это правильно.

AdGuard Home слушает 53 только внутри Docker-сети:

10.42.42.43:53

На сам VPS порт 53 не пробрасывается. Поэтому если на VPS порт 53 занят systemd-resolved, это не мешает.

7. Запуск стека

Перед первым запуском обязательно проверим docker-compose.yml на ошибки.

Открыть папку проекта:

cd /opt/wg-easy

Проверить итоговую конфигурацию Docker Compose:

sudo docker compose config

Если файл написан правильно, команда выведет итоговую объединённую конфигурацию без ошибок.

Если в docker-compose.ym есть ошибка в отступах, двоеточиях или структуре YAML, Docker Compose покажет ошибку ещё до запуска контейнеров.

Типичные ошибки YAML:

mapping values are not allowed here
services must be a mapping
networks must be a mapping
yaml: line X: did not find expected key

Если ошибок нет, запускаем стек:

sudo docker compose up -d --build

Проверить контейнеры:

sudo docker compose ps

Ожидаемо:

wg-easy
Up / healthy
adguard-vpn-dns
Up
unbound-vpn-dns
Up / healthy

Посмотреть логи:

sudo docker logs wg-easy --tail=100
sudo docker logs adguard-vpn-dns --tail=100
sudo docker logs unbound-vpn-dns --tail=100

Что проверить после шага

Проверить, какие порты слушаются на VPS:

sudo ss -luntp | grep -E ':51820|:51821|:3000|:53'

Нормально:

0.0.0.0:51820/udp
WireGuard наружу
127.0.0.1:51821/tcp
WG-Easy UI только локально
127.0.0.1:3000/tcp
AdGuard UI только локально
127.0.0.53:53
systemd-resolved, это нормально

Проверить Docker-пробросы:

sudo docker port adguard-vpn-dns
sudo docker port wg-easy

Для AdGuard Home ожидаемо:

3000/tcp -> 127.0.0.1:3000

У AdGuard не должно быть:

53/tcp -> ...
53/udp -> ...

Если порт 53 проброшен наружу, это ошибка. В этой схеме AdGuard DNS должен быть доступен только внутри Docker-сети как:

10.42.42.43:53

8. Открыть firewall

Если используете UFW:

sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw status

Не открывайте:

51821/tcp
3000/tcp
53/tcp
53/udp
5335/tcp
5335/udp

9. Открыть WG-Easy через SSH-туннель

На Windows PowerShell:

ssh -N -L 51821:127.0.0.1:51821 root@IP_ВАШЕГО_VPS

Окно PowerShell не закрывайте.

В браузере:

http://127.0.0.1:51821

10. Первичная настройка WG-Easy

В WG-Easy укажите:

Host: IP_ВАШЕГО_VPS или vpn.example.com
Port: 51820
DNS: 10.42.42.43
IPv4 CIDR: 10.8.0.0/24
IPv6 CIDR: fd42:42:42::/64

Замените vpn.example.com на свой домен или поддомен. Например: secure.jeyber.com.

Да, IPv6 CIDR оставляем заполненным, но IPv6 фактически отключаем через:

DISABLE_IPV6=true

Ошибка новичка

Не удаляйте IPv6 CIDR в WG-Easy UI.

Неправильно:

IPv6 CIDR: пусто

Правильно:

IPv6 CIDR: fd42:42:42::/64
DISABLE_IPV6=true

Если удалить IPv6 CIDR, WG-Easy может ругаться на невалидное значение. При DISABLE_IPV6=true этот CIDR не должен попадать в клиентские конфиги.

11. Открыть AdGuard Home через SSH-туннель

AdGuard Home Web UI в этой схеме не открыт наружу. Он доступен только локально на VPS:

127.0.0.1:3000

С Windows PowerShell открыть SSH-туннель:

ssh -N -L 3000:127.0.0.1:3000 root@IP_ВАШЕГО_VPS

Окно PowerShell не закрывать. Оно держит туннель.

После этого открыть в браузере на Windows:

http://127.0.0.1:3000

Если порт 3000 уже занят на Windows, можно использовать локальный порт 3001:

ssh -N -L 3001:127.0.0.1:3000 root@IP_ВАШЕГО_VPS

Тогда в браузере открыть:

http://127.0.0.1:3001

Если AdGuard Home не открывается

Сначала проверить на самом VPS:

curl -I http://127.0.0.1:3000

Нормально, если ответ будет похож на один из этих вариантов:

HTTP/1.1 200 OK

или:

HTTP/1.1 302 Found

или:

HTTP/1.1 307 Temporary Redirect

Проверить, слушается ли порт 3000 на VPS:

sudo ss -luntp | grep ':3000'

Правильный результат выглядит примерно так:

tcp LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:* users:(("docker-proxy",pid=...,fd=...))

Это значит, что AdGuard Home доступен только локально на VPS. Это правильно.

Проверить Docker-проброс:

sudo docker port adguard-vpn-dns

Ожидаемо:

3000/tcp -> 127.0.0.1:3000

Если curl на VPS отвечает, а в браузере на Windows http://127.0.0.1:3000 не открывается, проблема почти всегда в SSH-туннеле.

Правильная команда для Windows PowerShell:

ssh -N -L 3000:127.0.0.1:3000 root@IP_ВАШЕГО_VPS

Если AdGuard был случайно настроен на порт 80, а не 3000, проверить конфиг:

sudo nano /opt/wg-easy/adguard/conf/AdGuardHome.yaml

Найти блок:

http:
  address: 0.0.0.0:80

Заменить на:

http:
  address: 0.0.0.0:3000

Перезапустить AdGuard:

cd /opt/wg-easy
sudo docker compose restart adguard

Проверить снова:

curl -I http://127.0.0.1:3000

12. Первичная настройка AdGuard Home

При первом открытии AdGuard Home появится мастер настройки.

В мастере указать:

Admin Web Interface:
Listen interface: All interfaces
Port: 3000
DNS Server:
Listen interface: All interfaces
Port: 53

Это безопасно, потому что в docker-compose.yml порт 53 не проброшен наружу на VPS. AdGuard будет слушать 53 только внутри Docker-сети.

После установки открыть:

Settings → DNS settings → Upstream DNS servers

Указать upstream DNS:

10.42.42.44:5335

Это значит:

AdGuard Home → Unbound

В Bootstrap DNS servers можно оставить:

9.9.9.9
1.1.1.1

Для upstream 10.42.42.44:5335 bootstrap почти не нужен, потому что upstream задан IP-адресом.

Что проверить после шага

Проверить Unbound напрямую:

sudo docker run --rm --network wg alpine:3.21 sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @10.42.42.44 -p 5335 example.com A +short'

Ожидаемо должны вернуться IP-адреса.

Проверить AdGuard → Unbound:

sudo docker run --rm --network wg alpine:3.21 sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @10.42.42.43 example.com A +short'

Если есть ответ, цепочка работает:

AdGuard Home 10.42.42.43:53 → Unbound 10.42.42.44:5335

Важно про DoH

Принудительный DNS через AdGuard перехватывает обычный DNS на:

53/tcp
53/udp

Также в hooks можно заблокировать:

853/tcp
DNS-over-TLS
853/udp
DNS-over-QUIC
784/udp
альтернативный DoQ
8853/udp
альтернативный DoQ

Но DNS-over-HTTPS полностью не отличить от обычного HTTPS только iptables-правилами, потому что он идёт через:

443/tcp

Для своих устройств лучше отключить Secure DNS в браузере.

Chrome / Edge:

Settings → Privacy and security → Security → Use secure DNS → Off

Firefox:

Settings → Privacy & Security → DNS over HTTPS → Off

Опционально: блокировка QUIC / HTTP3 через UDP/443

Некоторые браузеры и приложения используют QUIC/HTTP3 через:

443/udp

Обычные сайты при этом используют HTTPS через:

443/tcp

Если заблокировать 443/udp, большинство сайтов продолжит открываться через обычный HTTPS 443/tcp, но QUIC/HTTP3 будет отключён.

Это может быть полезно, если нужно сделать более строгий режим и уменьшить количество обходных вариантов.

Добавить блокировку UDP/443

В WG-Easy открыть:

Admin Panel → WireGuard → Hooks

В PostUp можно добавить к существующим правилам:

iptables -A FORWARD -i wg0 -p udp --dport 443 -j REJECT;

В PostDown тогда обязательно добавить:

iptables -D FORWARD -i wg0 -p udp --dport 443 -j REJECT || true;

После сохранения перезапустить WG-Easy:

sudo docker restart wg-easy

Проверить, что правило появилось:

sudo docker exec -it wg-easy sh -lc 'iptables -S FORWARD | grep "dport 443"'

Ожидаемо:

-A FORWARD -i wg0 -p udp -m udp --dport 443 -j REJECT

Важно

Сначала лучше проверить базовую схему без блокировки 443/udp.

Сначала настраиваем:

WG-Easy → AdGuard Home → Unbound

Потом проверяем:

DNS работает
AdGuard Query Log видит запросы
Unbound отвечает
VPN-клиент выходит в интернет
IPv6 не утекает

И только после этого, при необходимости, добавляем блокировку 443/udp.

Блокировка 443/udp может влиять на приложения, которые активно используют QUIC/HTTP3. Обычно они переключаются на 443/tcp, но проверять это нужно отдельно.

13. Проверить Unbound

На VPS:

sudo docker run --rm --network wg alpine:3.21 sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @10.42.42.44 -p 5335 example.com A +short'

Нормально, если вернутся IP-адреса:

172.66.147.243
104.20.23.154

IP могут отличаться. Главное — чтобы был ответ.

14. Проверить AdGuard → Unbound

sudo docker run --rm --network wg alpine:3.21 sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @10.42.42.43 example.com A +short'

Если есть ответ, цепочка работает:

AdGuard Home 10.42.42.43:53 → Unbound 10.42.42.44:5335

15. Настроить принудительный DNS в WG-Easy Hooks

Это нужно, чтобы пользователь не мог просто поменять DNS на 1.1.1.1, 8.8.8.8 или другой обычный DNS-сервер.

В WG-Easy откройте:

Admin Panel → WireGuard → Hooks

WG-Easy поддерживает server-level hooks, которые выполняются при старте и остановке WireGuard-интерфейса.

PostUp

Вставьте в PostUp:

iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43:53; iptables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43:53; iptables -A FORWARD -i wg0 -p tcp --dport 853 -j REJECT; iptables -A FORWARD -i wg0 -p udp --dport 853 -j REJECT; iptables -A FORWARD -i wg0 -p udp --dport 784 -j REJECT; iptables -A FORWARD -i wg0 -p udp --dport 8853 -j REJECT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE;

PostDown

Вставьте в PostDown:

iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; iptables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43:53 || true; iptables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43:53 || true; iptables -D FORWARD -i wg0 -p tcp --dport 853 -j REJECT || true; iptables -D FORWARD -i wg0 -p udp --dport 853 -j REJECT || true; iptables -D FORWARD -i wg0 -p udp --dport 784 -j REJECT || true; iptables -D FORWARD -i wg0 -p udp --dport 8853 -j REJECT || true; iptables -D FORWARD -i wg0 -j ACCEPT || true; iptables -D FORWARD -o wg0 -j ACCEPT || true; iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE || true;

Официальный пример WG-Easy с AdGuard Home тоже использует hooks и DNAT DNS-запросов на AdGuard Home; здесь оставлена только IPv4-логика и добавлена блокировка DoT/DoQ-портов.

После сохранения:

sudo docker restart wg-easy

Что делают эти hooks

Обычный DNS перехватывается:

любой DNS-запрос клиента на 53/tcp или 53/udp
принудительно отправляется в 10.42.42.43:53

Если пользователь вручную поставит DNS:

1.1.1.1
8.8.8.8
9.9.9.9
77.88.8.8

обычный DNS всё равно попадёт в AdGuard Home.

Дополнительно блокируются:

853/tcp
DNS-over-TLS
853/udp
DNS-over-QUIC
784/udp
альтернативный DoQ
8853/udp
альтернативный DoQ

Важно про DoH

DNS-over-HTTPS полностью не отличить от обычного HTTPS только iptables-правилами, потому что он идёт через:

443/tcp

Для своих устройств отключайте Secure DNS в браузерах:

Chrome / Edge:
Settings → Privacy and security → Security → Use secure DNS → Off
Firefox:
Settings → Privacy & Security → DNS over HTTPS → Off

Опционально можно заблокировать 443/udp, чтобы отключить QUIC/HTTP3:

16. Проверить, что hooks применились

После перезапуска WG-Easy:

sudo docker exec -it wg-easy sh -lc 'iptables -t nat -S | grep 10.42.42.43'

Должно быть что-то вроде:

-A PREROUTING -i wg0 -p udp -m udp --dport 53 -j DNAT --to-destination 10.42.42.43:53
-A PREROUTING -i wg0 -p tcp -m tcp --dport 53 -j DNAT --to-destination 10.42.42.43:53

Проверить FORWARD:

sudo docker exec -it wg-easy sh -lc 'iptables -S FORWARD'

Проверить NAT:

sudo docker exec -it wg-easy sh -lc 'iptables -t nat -S POSTROUTING'

17. Создать нового клиента в WG-Easy

В WG-Easy:

Clients → New Client

Например:

windows-pc

Скачайте .conf.

Если старый ключ когда-либо публиковался или отправлялся в чат, лучше удалить старого клиента и создать нового.

18. Каким должен быть клиентский WireGuard-конфиг

Правильный вид:

[Interface]
PrivateKey = NEW_CLIENT_PRIVATE_KEY
Address = 10.8.0.2/32
MTU = 1420
DNS = 10.42.42.43
[Peer]
PublicKey = SERVER_PUBLIC_KEY
PresharedKey = NEW_PRESHARED_KEY
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Endpoint = IP_ТВОЕГО_VPS:51820

Если WG-Easy создаёт:

PersistentKeepalive = 0

можно оставить, но для клиентов за NAT, роутером или мобильной сетью лучше:

PersistentKeepalive = 25

Без IPv6 в клиентском конфиге должно быть так

Должно быть:

AllowedIPs = 0.0.0.0/0

Не должно быть:

AllowedIPs = 0.0.0.0/0, ::/0

Должно быть:

Address = 10.8.0.2/32

Не должно быть:

Address = 10.8.0.2/32, fd42:42:42::...

19. Kill switch на Windows и проверка IPv6

После импорта конфига в WireGuard for Windows:

WireGuard → Import tunnel from file
WireGuard → Edit
Включить: Block untunneled traffic (kill-switch)
Save
Activate

Так как в этом мануале используется IPv4-only схема, kill switch важен. Он помогает избежать ситуации, когда часть трафика или IPv6 уходит мимо VPN.

Проверка IPv4 после подключения

После подключения WireGuard открыть Windows PowerShell и проверить внешний IPv4:

curl.exe -4 https://ifconfig.me

Должен отобразиться IPv4-адрес VPS.

Например:

62.60.236.217

Если отображается домашний IP провайдера, значит трафик не пошёл через VPN.

Проверка IPv6 после подключения

Так как в этом мануале используется IPv4-only схема, IPv6 через VPN не выдаётся.

Проверить IPv6:

curl.exe -6 https://ifconfig.me

Для этой схемы нормально, если команда не вернёт IPv6-адрес или завершится ошибкой подключения.

Если команда показывает IPv6 домашнего провайдера, значит IPv6 уходит мимо VPN.

В этом случае обязательно:

1. Включить Block untunneled traffic в WireGuard.
2. Переподключить туннель.
3. Повторить проверку curl.exe -6 https://ifconfig.me.

Если IPv6 всё равно виден, можно временно отключить IPv6 на сетевом адаптере Windows.

Проверка через сайт

Открыть в браузере:

https://test-ipv6.com

Для этой схемы ожидаемо:

IPv4 работает через VPS
IPv6 либо недоступен, либо не должен показывать IPv6 домашнего провайдера

Если сайт показывает IPv6 домашнего провайдера, это утечка IPv6.

Проверка DNS

Проверить обычный DNS:

nslookup example.com

DNS-сервер должен быть:

10.42.42.43

Это AdGuard Home.

Проверить принудительный DNS:

nslookup example.com 1.1.1.1

И:

nslookup example.com 8.8.8.8

После этих команд открыть:

AdGuard Home → Query Log

Если правила DNAT работают, запросы должны появиться в журнале AdGuard Home, даже если в команде указан 1.1.1.1 или 8.8.8.8.

Что должно быть в клиентском конфиге без IPv6

В клиентском .conf должно быть:

AllowedIPs = 0.0.0.0/0

Не должно быть:

AllowedIPs = 0.0.0.0/0, ::/0

Должно быть:

Address = 10.8.0.2/32

Не должно быть:

Address = 10.8.0.2/32, fd42:42:42::...

DNS должен быть:

DNS = 10.42.42.43

Итог проверки

Рабочее состояние выглядит так:

curl.exe -4 https://ifconfig.me
→ показывает IP VPS
curl.exe -6 https://ifconfig.me
→ не показывает IPv6 домашнего провайдера
nslookup example.com
→ DNS = 10.42.42.43
nslookup example.com 1.1.1.1
→ запрос всё равно виден в AdGuard Query Log
WireGuard
→ Block untunneled traffic включён

20. Финальная проверка на VPS

Проверить контейнеры:

cd /opt/wg-easy
sudo docker compose ps

Ожидаемо:

wg-easy
healthy
unbound-vpn-dns
healthy
adguard-vpn-dns
up

Проверить Unbound:

sudo docker run --rm --network wg alpine:3.21 sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @10.42.42.44 -p 5335 example.com A +short'

Проверить AdGuard:

sudo docker run --rm --network wg alpine:3.21 sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @10.42.42.43 example.com A +short'

Проверить IPv6 в WG-Easy:

sudo docker inspect wg-easy --format '{{range .Config.Env}}{{println .}}{{end}}' | grep DISABLE_IPV6

Должно быть:

DISABLE_IPV6=true

Проверить wg0 внутри контейнера:

sudo docker exec -it wg-easy sh -lc 'ip addr show wg0'

Не должно быть адреса:

inet6 fd...

21. Финальная проверка на Windows

После подключения WireGuard:

curl.exe https://ifconfig.me

Должен быть IP твоего VPS.

DNS:

nslookup example.com

Проверка принудительного DNS:

nslookup example.com 1.1.1.1
nslookup example.com 8.8.8.8

После этих команд открой:

AdGuard Home → Query Log

Запросы должны появиться в AdGuard. Это значит, что даже при попытке использовать 1.1.1.1 или 8.8.8.8 обычный DNS перенаправляется в AdGuard.

Проверка IPv6:

https://test-ipv6.com

В этой схеме IPv6 через VPN не используется. Если сайт показывает внешний IPv6 провайдера клиента, значит на клиенте нужно включить kill switch или отключить IPv6 на сетевом адаптере.

22. Как будет в итоге

Итоговая работа:

1. Пользователь подключается к WireGuard через WG-Easy.
2. Клиент получает IPv4-адрес из 10.8.0.0/24.
3. Весь IPv4-трафик идёт через VPS.
4. DNS в клиентском конфиге: 10.42.42.43.
5. 10.42.42.43 — это AdGuard Home.
6. AdGuard отправляет upstream в Unbound 10.42.42.44:5335.
7. Unbound сам рекурсивно резолвит домены.
8. Обычный DNS на 53/tcp и 53/udp принудительно уходит в AdGuard.
9. DoT/DoQ на 853/784/8853 блокируется.
10. IPv6 клиентам не выдаётся.
11. Web UI WG-Easy и AdGuard не открыты наружу.

Частые ошибки

Ошибка 1. Открыть AdGuard DNS наружу

Не надо:

- "53:53/tcp"
- "53:53/udp"

Правильно: 53 только внутри Docker-сети.

Ошибка 2. Указать Unbound в WG-Easy DNS

Неправильно:

WG-Easy DNS = 10.42.42.44

Правильно:

WG-Easy DNS = 10.42.42.43

Unbound указывается только в AdGuard:

AdGuard upstream = 10.42.42.44:5335

Ошибка 3. Удалить IPv6 CIDR

Неправильно:

IPv6 CIDR пустой

Правильно:

IPv6 CIDR = fd42:42:42::/64
DISABLE_IPV6=true

Ошибка 4. Открывать WG-Easy и AdGuard напрямую

Неправильно:

http://IP_ВАШЕГО_VPS:51821
http://IP_ВАШЕГО_VPS:3000

Правильно:

ssh -N -L 51821:127.0.0.1:51821 root@IP_ВАШЕГО_VPS
ssh -N -L 3000:127.0.0.1:3000 root@IP_ВАШЕГО_VPS

И открывать:

http://127.0.0.1:51821
http://127.0.0.1:3000

Ошибка 5. Не пересоздать клиента после изменения DNS

Если клиент был создан до настройки DNS, в его .conf может быть старый DNS.

Правильно:

Delete client → New client → Download new config

В новом конфиге должно быть:

DNS = 10.42.42.43

Финальный чек-лист

Docker:
[ ] docker --version работает
[ ] docker compose version работает
Контейнеры:
[ ] wg-easy healthy
[ ] unbound-vpn-dns healthy
[ ] adguard-vpn-dns up
Порты:
[ ] 51820/udp открыт наружу
[ ] 51821/tcp только 127.0.0.1
[ ] 3000/tcp только 127.0.0.1
[ ] 53 не проброшен наружу
WG-Easy:
[ ] DNS = 10.42.42.43
[ ] IPv4 CIDR = 10.8.0.0/24
[ ] IPv6 CIDR = fd42:42:42::/64
[ ] DISABLE_IPV6=true
[ ] Hooks добавлены
AdGuard:
[ ] DNS server = 0.0.0.0:53 внутри контейнера
[ ] Web UI = 0.0.0.0:3000 внутри контейнера
[ ] Upstream DNS = 10.42.42.44:5335
Unbound:
[ ] слушает 10.42.42.44:5335
[ ] отвечает на dig
[ ] do-ip6: no
Клиент:
[ ] DNS = 10.42.42.43
[ ] AllowedIPs = 0.0.0.0/0
[ ] нет ::/0
[ ] нет IPv6 Address
[ ] включён Block untunneled traffic