Настройка доступа к локальной сети через WireGuard
Рукопожатие WireGuard проходит успешно, но пакеты не доходят до устройств в локальной сети. Разберитесь в четырех критических настройках маршрутизации для корректной передачи трафика.
Почему ваша домашняя локальная сеть не отвечает через WireGuard
Чтобы маршрутизация в домашнюю локальную сеть через WireGuard работала, должны быть согласованы четыре отдельных параметра. Если настроены три из четырех, туннель выглядит полностью работоспособным, что и делает эту проблему такой запутанной. wg show сообщает о недавнем рукопожатии, ping 10.8.0.1 отвечает за несколько миллисекунд, а ping 192.168.20.10 не возвращает ничего.
Ниже приведен полный список в порядке прохождения пакета. LAN означает локальную сеть (local area network) — частную сеть за вашим домашним маршрутизатором.
AllowedIPsна клиенте должна охватывать удаленную подсеть, иначе пакет никогда не попадет в туннель.AllowedIPsна сервере должна охватывать адрес туннеля клиента, иначе пакет будет отброшен сразу после расшифровки.net.ipv4.ip_forwardдолжна быть1на сервере, так как Linux отбрасывает любой пакет, который не адресован самому узлу.- Локальная сеть должна знать маршрут обратно к
10.8.0.0/24через правило маскарадинга (masquerade) на сервере или статический маршрут на домашнем маршрутизаторе.
Каждый из этих пунктов может привести к сбою без сообщения об ошибке. Ничего не записывается в логи, система не выдает предупреждений, а рукопожатие продолжает работать всё время. Проверяйте их по порядку, и неисправный элемент обнаружится примерно за минуту.
Используемая в руководстве сеть
Все приведенные ниже адреса являются примерами. Замените их на свои собственные и соблюдайте единообразие при замене, так как частично обновленная конфигурация — вторая по частоте причина возникновения проблем.
- Домашняя локальная сеть —
192.168.20.0/24. Домашний маршрутизатор —192.168.20.1. - Сервер WireGuard — это Linux-машина в данной локальной сети. Её интерфейс локальной сети
enp1s0имеет адрес192.168.20.5, а туннельный интерфейсwg0— адрес10.8.0.1. - Хост, к которому требуется получить доступ, — это NAS (сетевое хранилище) с адресом
192.168.20.10. - Клиент — это ноутбук, находящийся в другом месте, с адресом
10.8.0.2внутри туннеля.
Сервер является машиной в локальной сети, а не самим маршрутизатором. Это стандартный случай: Raspberry Pi или старый мини-ПК. Это важно для правила 4: ваш маршрутизатор не знает о существовании туннеля, пока вы его об этом не уведомите, а каждый хост в локальной сети отправляет трафик за пределы подсети на этот маршрутизатор.
Если у вашего домашнего подключения нет публичного IP-адреса, этот метод не будет работать самостоятельно, так как никто из интернета не сможет инициировать рукопожатие с вашим домом. Раздел об использовании VPS в качестве промежуточного узла описывает этот случай. Там действуют те же четыре правила, с одним дополнительным узлом, который нужно учитывать.
AllowedIPs выполняет две разные функции
Один параметр отвечает за две задачи, и попытка интерпретировать его одинаково с обеих сторон — главная причина большинства подобных обращений в поддержку. WireGuard называет этот механизм криптографической маршрутизацией (cryptokey routing), подробнее об этом написано в том, как WireGuard привязывает публичные ключи к диапазонам IP.
При исходящем трафике AllowedIPs — это таблица маршрутизации. wg-quick превращает каждую запись в маршрут, указывающий на wg0. Пакет для 192.168.20.10 шифруется и отправляется пиру только в том случае, если какой-либо пир заявил диапазон, содержащий этот адрес. Укажите только 10.8.0.0/24, и ваш ноутбук отправит трафик локальной сети через обычный Wi-Fi, где он либо будет отброшен, либо попадет в совершенно другой 192.168.20.10.
При входящем трафике AllowedIPs — это список контроля доступа. После того как WireGuard расшифровывает пакет от пира, он сверяет внутренний адрес источника с AllowedIPs этого пира и отбрасывает пакет, если они не совпадают. В логах нет записи об этом, и счетчик для такого сброса отсутствует. Пакет просто исчезает.
Таким образом, два конфигурационных файла никогда не являются зеркальными отражениями друг друга. Клиент указывает, к чему он хочет получить доступ через сервер. Сервер указывает, какие адреса источника разрешено использовать данному клиенту.
Соответствующая пара конфигурационных файлов
Клиент, /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
[Peer]
PublicKey = <server public key>
Endpoint = home.example.com:51820
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24
PersistentKeepalive = 25Сервер, /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <server private key>
Address = 10.8.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32На домашнем роутере также необходимо настроить проброс портов: UDP 51820 на 192.168.20.5, иначе рукопожатие (handshake) не начнётся, а в логах клиента появится Handshake for peer 1 did not complete after 5 seconds, retrying. Данное руководство предполагает, что этот этап вы уже прошли.
Четыре строки отличаются от стандартной настройки полного туннеля (full-tunnel), и каждое изменение внесено намеренно.
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24на клиенте вместо0.0.0.0/0, ::/0. Это раздельное туннелирование (split tunnel): подсеть туннеля и домашняя локальная сеть проходят черезwg0, а весь остальной трафик использует локальный маршрут. Ваш веб-трафик не пойдёт через домашнее соединение, что обычно и требуется, если вам нужен только доступ к NAS.AllowedIPs = 10.8.0.2/32на сервере: указан один адрес вместо диапазона. Если написать там10.8.0.0/24, этот единственный клиент сможет присвоить себе любой адрес внутри туннеля. Если позже добавить второго пира с пересекающимся диапазоном, трафик будет перенаправляться на того пира, который был настроен последним, при этом никаких ошибок в логах не появится.PersistentKeepalive = 25только на клиенте. Клиент находится за NAT (network address translation), и его роутер забывает UDP-маппинг после минуты или двух бездействия, из-за чего сервер теряет возможность связаться с ним. У сервера есть публичный адрес, поэтому keepalive ему не требуется.- Пока отсутствует строка
DNS =. Её добавление меняет разрешение имён для всей клиентской машины. В разделе DNS ниже объясняется, что именно она делает, прежде чем вы её активируете.
Полный туннель также обеспечивает доступ к локальной сети, так как 0.0.0.0/0 соответствует любому адресу. Однако это расходует весь ваш трафик и приводит к конфликту подсетей, который невозможно исправить со стороны клиента.
Почему использование одной и той же подсети с обеих сторон приводит к сбою
Выберите для домашней сети подсеть, которую почти никто не использует, например 192.168.20.0/24 или 10.44.7.0/24. Адреса 192.168.1.0/24 и 192.168.0.0/24 являются заводскими настройками большинства потребительских маршрутизаторов, поэтому рано или поздно ваш ноутбук окажется в сети кафе или отеля с точно таким же диапазоном адресов.
Конфликт адресов является критической ошибкой, и в двух случаях он проявляется по-разному. При использовании split tunnel wg-quick пытается добавить маршрут для префикса, который уже существует в интерфейсе Wi-Fi, ip route add отклоняет запрос, и интерфейс не поднимается:
RTNETLINK answers: File existsПри использовании full tunnel wg-quick устанавливает правила policy routing, которые намеренно удерживают более специфичные маршруты вне основной таблицы. Локальный маршрут 192.168.1.0/24 оказывается приоритетнее туннеля, поэтому каждый пакет для удаленной локальной сети уходит через локальное соединение. Туннель поднят, рукопожатие проходит успешно, но NAS остается недоступным. Единственным надежным решением является изменение адресации домашней локальной сети.
Превращение сервера в маршрутизатор
ip route show default
printf 'net.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forwardПервая команда позволяет узнать реальное имя LAN-интерфейса. В современных образах используются имена вида enp1s0 или ens3, реже eth0; правило маскарадинга, указывающее на неверный интерфейс, не будет работать. Последняя команда должна вывести net.ipv4.ip_forward = 1. Простая команда sudo sysctl -w устанавливает то же значение, но оно сбрасывается после перезагрузки, что является классической причиной жалоб в духе «всё работало до вторника».
Пересылка пакетов (forwarding) также должна быть разрешена в межсетевом экране. В Ubuntu при активном ufw пересылаемые пакеты отбрасываются, если в /etc/default/ufw не задано значение DEFAULT_FORWARD_POLICY="ACCEPT". Docker самостоятельно устанавливает такую же политику, поэтому если sudo iptables -S FORWARD | head -1 выводит -P FORWARD DROP на сервере, где вы не настраивали firewall вручную, значит, это сделал Docker, и для вашего туннельного трафика потребуется явное правило разрешения (accept).
Почему ответы не возвращаются
Если правила с 1 по 3 настроены верно, ping действительно доходит до NAS. Однако вы всё равно ничего не видите, так как у ответа нет обратного пути. NAS отвечает на 10.8.0.2 — адрес вне своей подсети, поэтому он передает пакет своему шлюзу по умолчанию, домашнему маршрутизатору 192.168.20.1. Этот маршрутизатор ничего не знает о 10.8.0.0/24, поэтому пересылает ответ на свой собственный шлюз по умолчанию (ваше интернет-соединение), где пакет отбрасывается. Запрос доходит, а ответ теряется.
Вариант A: маскарадинг (masquerade) на WireGuard сервере. Сервер перезаписывает адрес источника каждого пересылаемого пакета на 192.168.20.5, свой собственный адрес в локальной сети. Теперь NAS видит запрос от соседа по своей подсети, отвечает напрямую серверу, а сервер отменяет подмену и отправляет ответ обратно в туннель. В остальной локальной сети ничего менять не нужно.
Добавьте это в блок [Interface] на сервере, чтобы правило появлялось и исчезало вместе с интерфейсом:
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADEЕсли система уже управляется через nftables, запишите правило в /etc/nftables.conf:
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" counter masquerade
}
}Обязательно оставьте ключевое слово counter. Без него sudo nft list ruleset выведет правило без счетчика пакетов, а именно этот счетчик показывает, используется ли правило в данный момент.
Маскарадинг дает второе преимущество, которое легко упустить. Многие хосты используют брандмауэр, принимающий соединения только из своей подсети. Общий доступ к файлам в Windows работает именно так, как и многие панели управления NAS. Пакет от 10.8.0.2 будет отброшен целевым хостом, даже если маршрутизация настроена идеально. После маскарадинга источником становится адрес локальной сети, поэтому такие правила пропускают трафик. Минус в том, что в логах всех хостов локальной сети каждый клиент туннеля будет выглядеть как 192.168.20.5. Вы не сможете различать клиентов, а правила для конкретных IP на устройствах в локальной сети работать не будут.
Вариант B: статическая маршрутизация на домашнем маршрутизаторе. Сообщите маршрутизатору, что сеть 10.8.0.0/24 находится за 192.168.20.5. На Linux-маршрутизаторе это делается одной командой:
sudo ip route add 10.8.0.0/24 via 192.168.20.5В бытовых маршрутизаторах есть страница «Статические маршруты» (Static Routes) или «Маршрутизация» (Routing) в расширенных настройках: назначение 10.8.0.0, маска 255.255.255.0, шлюз 192.168.20.5. Сохраните настройки в конфигурации маршрутизатора, так как команда ip route add, введенная в консоли Linux, исчезнет после перезагрузки.
Этот способ сохраняет реальный адрес клиента, поэтому логи и правила для конкретных клиентов в локальной сети остаются информативными. Для этого требуется маршрутизатор с поддержкой статических маршрутов, и это помогает только тем хостам, которые используют этот маршрутизатор как шлюз по умолчанию. Любой хост с локальным брандмауэром, ограниченным подсетью, все равно потребует собственного правила для 10.8.0.0/24. Начните с маскарадинга, так как он не требует изменений вне сервера, который вы уже контролируете, а переходите к статическим маршрутам, когда вам потребуются реальные адреса клиентов.
Как определить, какой из четырех компонентов работает неверно
Двигайтесь от клиента к серверу. Каждый шаг показывает, доходит ли пакет до конкретной точки.
Попадает ли пакет в туннель? На клиенте выполните:
ip route get 192.168.20.10В ответе должно быть указано dev wg0. Если там указан ваш Wi-Fi интерфейс, значит, правило 1 нарушено и AllowedIPs клиента не охватывает подсеть локальной сети. Аналогичную информацию покажет ping: connect: Network is unreachable.
Доходят ли пакеты до сервера? Запустите эту команду на сервере, а затем пропингуйте NAS с клиента:
sudo tcpdump -ni wg0 icmpРабочий путь отобразит IP 10.8.0.2 > 192.168.20.10: ICMP echo request, где ICMP — это протокол межсетевых управляющих сообщений, который использует ping. Если здесь пусто, но «рукопожатие» (handshake) проходит успешно, значит, нарушено правило 2: AllowedIPs сервера для этого узла не включает 10.8.0.2, поэтому пакет был отброшен при расшифровке еще до того, как попал в wg0.
Уходят ли пакеты в локальную сеть? На сервере наблюдайте за интерфейсом локальной сети:
sudo tcpdump -ni enp1s0 icmpЕсли запросы видны на wg0, но отсутствуют здесь, значит, нарушено правило 3: пересылка (forwarding) отключена или пакет был отброшен правилом FORWARD. Если запросы видны здесь с исходным адресом 10.8.0.2, но ответов нет, значит, нарушено правило 4: у ответа нет обратного маршрута. Если запросы видны здесь с исходным адресом 192.168.20.5, но ответов нет, значит, правило маскарадинга работает, а целевой хост отклоняет запрос сам — проверьте настройки брандмауэра на NAS. Этот же алгоритм применим к любому другому сервису, если заменить icmp на port 445 или другой нужный вам порт.
Я могу подключиться по IP, но не по имени
ssh 192.168.20.10 работает, а ssh nas.home.arpa выдает ошибку:
ssh: Could not resolve hostname nas.home.arpa: Name or service not knownС туннелем все в порядке. Разрешение имен — это отдельный процесс, и ваш ноутбук по-прежнему отправляет запросы тому DNS-резолверу, который он получил от локальной сети Wi-Fi. Этот резолвер ничего не знает об именах в вашей домашней сети.
Для работы домашних имен должны выполняться два условия. Адрес резолвера должен находиться внутри AllowedIPs на клиенте, иначе DNS-запрос (domain name system) никогда не попадет в туннель. Кроме того, резолвер должен быть готов отвечать на запросы, источник которых имеет адрес 10.8.0.2. Многие домашние резолверы по умолчанию отказывают в этом: dnsmasq, запущенный с параметром local-service, отвечает только на запросы из напрямую подключенной подсети, а Pi-hole поставляется с режимом прослушивания, разрешающим только локальные запросы. Правило masquerade маскирует эту проблему, так как после перезаписи адрес источника запроса меняется на 192.168.20.5.
На стороне клиента достаточно одной строки:
DNS = 192.168.20.1На клиенте под управлением Linux требуется openresolv или аналогичный инструмент, иначе wg-quick завершится с ошибкой resolvconf: command not found. Прежде чем применять настройки на машине с systemd-resolved, убедитесь, что вы понимаете последствия: wg-quick регистрирует эти серверы как единственные, поэтому, пока туннель активен, все запросы с ноутбука будут уходить на ваш домашний резолвер, а не только запросы домашних имен. Проверьте результат с помощью resolvectl status wg0. Если вы хотите, чтобы домашние имена разрешались дома, а все остальное — локально, это называется split DNS, и в настройке DNS через туннель WireGuard этот вопрос разобран полностью.
Нет публичного IP дома? Используйте VPS в качестве посредника
Если на странице состояния вашего роутера отображается WAN-адрес из диапазона 100.64.0.0/10 или частный адрес 192.168.x.x, значит, вы находитесь за CGNAT (carrier grade network address translation), и входящие соединения из интернета не могут достичь вашего дома. Исходящие соединения работают в обычном режиме, поэтому решение заключается в использовании третьего узла с публичным адресом. На небольшом VPS запускается хаб, к которому подключается домашний сервер.
Четыре правила не меняются. Теперь они применяются к двум переходам, поэтому объем настроек удваивается.
- На VPS в записи пира для домашнего сервера указывается
AllowedIPs = 10.8.0.3/32, 192.168.20.0/24: его собственный адрес в туннеле плюс подсеть, для которой ему разрешено передавать трафик. - На VPS запись пира для ноутбука остается прежней —
AllowedIPs = 10.8.0.2/32. - На ноутбуке в качестве пира для VPS указывается
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24, так как теперь весь трафик направляется через хаб. - На домашнем сервере в качестве пира для VPS указывается
AllowedIPs = 10.8.0.0/24, а сам домашний сервер используетPersistentKeepalive = 25, так как теперь он находится за NAT. - VPS также требует
net.ipv4.ip_forward = 1, а в его цепочке forward необходимо разрешить передачу трафика сwg0наwg0, поскольку трафик ноутбука приходит и уходит через один и тот же интерфейс. Межсетевой экран, настроенный для обычного VPS с полным туннелированием, блокирует именно такие действия.
Если вы еще не настроили сторону VPS, в руководстве настройка WireGuard на VPS описаны генерация ключей, межсетевой экран и юнит systemd. Более общий подход к доступу к машине, которая не может принимать входящие соединения, описан в статье открытие обратного туннеля из-за CGNAT. Когда ручное управление адресами пиров станет утомительным, запуск Tailscale subnet router позволит автоматизировать маршрутизацию и учет адресов.
Обеспечение автозапуска после перезагрузки
sudo systemctl enable --now wg-quick@wg0
sudo systemctl enable --now nftables
sudo wg showenable --now — это этап, который многие пропускают. Запущенный вручную wg-quick up wg0 исчезнет после следующего обновления ядра и перезагрузки. wg show должен отображать пир с недавней строкой latest handshake и ненулевыми счетчиками передачи данных в обоих направлениях.
Добавление второго клиента позже не требует перезапуска, который привел бы к разрыву соединений у всех текущих пользователей:
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip выводит конфигурацию без ключей, которые понимает только wg-quick, а syncconf применяет изменения, пока активные сессии продолжают работать. Эта команда обновляет только пиры. Изменение Address или добавление новой строки PostUp по-прежнему требует полного выполнения команд down и up.
FAQ
Почему я могу пропинговать сервер WireGuard, но не другие устройства в домашней локальной сети?
Пинг 10.8.0.1 лишь подтверждает, что туннель активен. Доступ к остальной части локальной сети — это вопрос маршрутизации. Параметр AllowedIPs на клиенте должен включать 192.168.20.0/24, иначе пакеты не попадут в туннель. На сервере необходимо установить net.ipv4.ip_forward в значение 1, иначе он будет отбрасывать любые пакеты, адресованные не ему. Кроме того, в локальной сети должен быть настроен обратный маршрут к 10.8.0.0/24. Запустите sudo tcpdump -ni enp1s0 icmp на сервере во время пинга: если запросы уходят с исходным адресом 10.8.0.2, но ответов нет, значит, отсутствует обратный путь.
Нужен ли мне статический маршрут на домашнем роутере?
Только если вы не используете правило маскарадинга (masquerade). Правило маскарадинга на сервере WireGuard подменяет исходный адрес трафика туннеля на собственный адрес сервера в локальной сети. В результате хосты в локальной сети отвечают соседу, путь к которому им уже известен, и роутер не участвует в процессе. Альтернатива — статический маршрут для 10.8.0.0/24 через адрес сервера в локальной сети. Это стоит делать, если вам нужны реальные адреса клиентов в логах локальных хостов или если вы планируете настраивать правила межсетевого экрана для конкретных клиентов.
Почему совпадение подсетей на обоих концах нарушает работу туннеля?
Ваш ноутбук не может содержать два маршрута для одного префикса. Если локальная сеть выдает адрес 192.168.1.0/24, а ваша домашняя сеть — это 192.168.1.0/24, то при использовании split-tunnel wg-quick up возникнет сбой, так как ip route add выдаст ошибку RTNETLINK answers: File exists. Вместо этого поднимется full tunnel, но wg-quick добавит правила политики, которые вытеснят более специфичные маршруты из основной таблицы, поэтому локальная сеть перехватит трафик, а удаленная сеть останется недоступной. Перенастройте домашнюю сеть на редкий диапазон, например 192.168.20.0/24. Исправить это на стороне клиента невозможно.
Я могу получить доступ к NAS по IP, но не по имени. В чем причина?
Разрешение имен не работает через туннель автоматически. Добавьте DNS = 192.168.20.1 (ваш домашний резолвер) в блок [Interface] на клиенте и убедитесь, что этот адрес попадает в AllowedIPs пира, иначе запрос не уйдет в туннель. Затем проверьте, отвечает ли резолвер на запросы извне своей подсети, так как dnsmasq с local-service, а также режим работы Pi-hole «только локальные запросы» (local-only) отклоняют их. Правило маскарадинга на сервере WireGuard позволяет обойти это ограничение, подменяя исходный адрес запроса.
У моего домашнего провайдера нет публичного IP. Могу ли я получить доступ к своей сети?
Да, с помощью третьего узла. При использовании CGNAT WAN-адрес вашего роутера является частным, поэтому другие узлы в интернете не могут инициировать рукопожатие (handshake) с ним, хотя исходящее соединение работает нормально. Запустите WireGuard на VPS с публичным адресом, настройте домашний узел на исходящее подключение к нему через PersistentKeepalive = 25, а в настройках пира на VPS укажите AllowedIPs, содержащий адрес туннеля и 192.168.20.0/24. На VPS необходимо включить пересылку пакетов (forwarding) и добавить правило, разрешающее трафик из wg0 в wg0.