К контенту
habrforever
СКРЫТА #dpi ≈2019 · ~6 мин чтения

Чиним резолвинг адресов в VPN-локалке (openconnect) для docker и systemd-resolved — актуализация 2026

автор оригинала: Ренат Ескенин (3draven)

оригинал на Хабре habr.com/…/547830
снимок Wayback web.archive.org
статус на Хабре (ноябрь 2026) скрыта (403/451)
права © автор, переиздание в архивных целях с указанием источника

Чиним резолвинг адресов в VPN-локалке (openconnect) для docker и systemd-resolved

Архивное переиздание с актуализацией. Оригинал опубликован на Habr (≈2019), автор — Ренат Ескенин (3draven). оригинал скрыт на Habr (HTTP 403/451), доступен только через Wayback Machine Снимок: Wayback Machine. Ниже — редакция ноября 2026: что работает сейчас, что изменилось и как правильно повторить рецепт сегодня. Полный текст оригинала — в конце, без купюр.

TL;DR — версия 2026

В 2026 ByeDPI-семейство (GoodbyeDPI/ByeDPI/Zapret/SpoofDPI) — способ номер один для «открыть сайт» и «вернуть скорость YouTube»: правка пакетов вместо VPN, ноль серверов, ноль подписок.

С момента выхода оригинала DPI-обход из нишевого трюка стал народным массовым решением: замедление YouTube и волны блокировок 2024–2026 привели к тому, что ByeDPI ставят даже те, кто никогда не слышал слово «VPN». ТСПУ (DPI-боксы) стоят у всех крупных российских провайдеров и умеют резать VPN-хендшейки WireGuard, OpenVPN и «голый» Shadowsocks. Изменились и сами приёмы: появились фрагментация TLS-ClientHello в обе стороны, fake-Seq, десинхронизация и обфускация Host-заголовка.

Статус оригинала

Семейство инструментов 2026

  • GoodbyeDPI (Windows, ValdikSS) — классика, режимы 1–9 + стратегии под конкретных провайдеров;
  • ByeDPI (кросс-платформа, форки) — lighter, есть Android-сборки, работающие как локальный VPN-сервис без root;
  • Zapret (Linux) — богатый набор DPI-трюков (nfqws, tpws), конфиги под ТСПУ конкретных операторов;
  • SpoofDPI (macOS/Linux) — «поставил и забыл»;
  • Android: ByeDPI-Mod и вариации, плюс системный «приватный DNS» DoH не забыть;
  • iOS: остаётся personal-VPN-профиль с SOCKS/DNS-трюками — слабее, чем на других платформах.

Как выбрать режим (методика из оригинала жива)

Алгоритм не изменился с оригинала: tracert до сайта → посмотреть, где теряется (пер-хоп) → определить тип блокировки (RST-инъекция, дроп ClientHello, дроп по SNI, IP-блок) → подобрать режим фрагментации. Только теперь всё это умеет автоматизировать blockcheck (скрипт ValdikSS): он сам прогоняет комбинации и печатает таблицу «что сработало».

Что изменилось с ≈2019 года

  • у ТСПУ появились «мягкие» и «жёсткие» профили по провайдерам — настройки индивидуальны
  • появились Android-сборки ByeDPI, работающие без root
  • обфускация Host и фрагментация в обе стороны вместо старого one-sided
  • GitHub-репозитории периодически «прибирают» — держите локальные копии бинарников

Чек-лист 2026

  • сначала blockcheck — потом настройка
  • DoH/DoT отдельно от DPI-обхода
  • проверить и TCP, и QUIC (UDP/443) пути
  • локальная копия инструмента + конфига

Вердикт

Оригинальный лаунчер-гайд был верен: «окошко с галочками» — то, что нужно людям. В 2026 окно то же, галочек больше, а под капотом — та же идея из 2017-го: подсунуть ТСПУ пакет, который она не умеет классифицировать.


ОРИГИНАЛ СТАТЬИ (без изменений)

Для подключения к корпоративной сети у нас используется CiscoAnyConnect, работает хорошо, но не с докером. Как только докер пытается приподнять свою сеть, утилитка тут же отрубает VPN и переподключает, не знаю зачем. От этого докер себя плохо чувствует. Поэтому я решил использовать обычный линуксовый openconnect соместно с NetworkManager. И начались хождения по граблям. Каких только я рецептов ни насмотрелся как сделать так что бы имена хостов разрешались в IP адреса с помощью DNS серверов в VPN сети (резолвились, коротко говоря :)). Многие так и не разобравшись просто удаляли systemd-resolved. Но в моем случае, после чтения документации ларчик открылся просто, а дело было в неверных настройках VPN сервера к которому я подключался, но это поправимо на стороне клиента.

[]

systemd-resolved

Поставил пакеты network-manager-openconnect network-manager-openconnect-gnome

Настроил соединение и оно даже подключилось. Проблема первая, каждый раз когда я подключался снова, он забывал имя пользователя, что раздражает. Я нашел решение и создал

баг. Решение простое с консоли задаем свое имя в особом виде nmcli con mod prgcvp vpn.secrets ‘form:main:username=yourName’,‘save_passwords=yes’

После чего оно будет запомнено. Да, галочку «запомнить пароль» я ставил и пароль он даже запомнил, но вот в тексте галочки про имя пользователя ничего не сказано, так что он честно его забыл :) Напомню, что настройки менеджера лежат в /etc/NetworkManager/system-connections/

И если параметры знаешь, то можно и руками отредактировать нужное соединение.

Подключаться стало удобно и возникла вторая проблема, имена ресурсов в VPN сети не разрешаются в адреса DNS сервером. Сервер есть, все настройки на месте, но nslookup someserver.local выдает ошибку, а nslookup someserver.local somednsIP выдет правильный ответ. Странно, подумал я, как так, сервер есть, отвечает, а если его конкретно не указать, ошибка? Ответ оказался прост (хоть я и потратил изрядно времени пока разбирался, потому и пишу эту заметку что бы через год не забыть). Когда systemd-resolved пытается найти адрес сервера по имени, он выступает фасадом для других DNS серверов. Делается это так, ссылка /etc/resolv.conf может указывать на несколько мест:

  • /run/systemd/resolve/stub-resolv.conf это опция по умолчанию и этот файл будет содержать примерно такое

nameserver 127.0.0.53

options edns0 trust-ad

search somedomain.local

  • /run/systemd/resolve/resolv.conf это можно использовать если функционал systemd-resolved чем то не устроил когда он прикидывается DNS сервером 127.0.0.53. В итоге это не пригодилось, так для информации пишу.

Подробности тут

Саму ссылку /etc/resolv.conf вы можете сами настроить что бы смотрела в любое из мест.

Так вот, дело в том, что openconnect при подключении к VPN получает таблицу route, DNS сервера, а так же search domain и этот домен от VPN сервера приходил неверный (так неправильно настроен у нас). От сервера приходило somedomain.local, а надо было просто local что бы somesrver.local был распознан.

Когда systemd-resolved прикинулся локальным DNS сервером и через /etc/resolv.conf всех отправил к себе за разрешением имен, логика его работы такова. Для каждого коннекта, которые можно посмотреть командой nmcli connection show (это те коннекты, которые знает NetworkManager) systemd-resolved помнит DNS сервера, которые получил по DHCP. Это можно посмотреть командой:

`resolvectl dns

Global:

Link 6 (docker0):

Link 5 (vpn0): 999.999.999.999 999.999.999.999

Link 3 (wlp4s0): 192.168.3.8

Link 2 (enp5s0f2):

`

Когда в 127.0.0.53 приходит запрос на разрешение имени, systemd-resolved смотрит search domain у каждого из коннектов (эти домены он при подключении от DHCP получил тоже). Домены можно посмотреть командой:

`resolvectl domain

Global:

Link 6 (docker0):

Link 5 (vpn0): somedomain.local

Link 3 (wlp4s0): ~.

Link 2 (enp5s0f2):

`

Далее имя хоста проверяется на частичное совпадение с теми доменами, которые прикреплены к коннектам и самое длинное совпадение и определяет какой конкретно DNS сервер вызвать. Либо все идет в DNS сервер где search domain ”~.”

От сервера компании мне приходил неверный search domain (somedomain.local) для VPN коннекта и потому когда я пытался разрешить адрес someserver.local, systemd-resolved их не мог найти, так как предполагал, что DNS сервера, полученные из этого соединения нужны что бы распознавать имена someserver.somedomain.local. Поправил я это подменив search domain в NetworkManager командой

nmcli connection modify yourConnectionName ipv4.dns-search «local»

Доменов может быть несколько через пробел. В итоге все заработало.

Помимо этого я удалил пакет avahi-daemon, так как службы bonjur, которые обслуживает этот демон по умолчанию тоже резолвятся на домене local, а в нашей сетке именно это имя используется для локальной сети и будут конфликты.

docker

Теперь в хост системе работает резолвинг адресов, но при запуске докера резолвинг локальных адресов в VPN может не работать по прежнему. И тут есть несколько вариантов.

Контейнер запущен с network_mode: host в таком случае будет использоваться для резольвинга то, что лежит в /run/systemd/resolve/resolv.conf и там у меня первый же днс сервер выбирается для резольва local и он для этого не подходит. Итог, не работает. Зато все сервисы хост машины видно из контейнера, что еще и неправильно.

Контейнер запущен с network_mode: bridge с созданием отдельной сети. В таком случае сервисы хоста будет не видно, помимо этого будет использован все тот же не работающий у меня /run/systemd/resolve/resolv.conf

Контейнер запущен без настроек сети и использует дефолтный bridge, созданный докером при инсталляции (docker0). В этом случае используется для резольва имен внутренний докеровский DNS, который судя по всему нормально взаимодействует с systemd-resolved и все резолвит как надо. В /etc/resolv.conf будет такое:

`bash-5.0# cat /etc/resolv.conf

search local

nameserver 127.0.0.11

options ndots:0

`

Если надо показать сервисы хост машины для доступа из контейнера, то просто запускаем сервисы слушать на docker0 IP и получаем доступ.

`ifconfig|grep -n1 docker0

27-

28:docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500

29- inet 192.168.32.1 netmask 255.255.240.0 broadcast 192.168.47.255

`

Не забываем открыть порты, например у меня ufw зарезал 8080 и пришлось открывать.

Было бы отлично если бы docker просто использовал systemd-resolved dns stub как в последнем описанном варианте всегда. Но к сожалению это не так. В версии systemd-resolved 248, которая только вышла и в дистрах ее нет в документации есть параметр DNSStubListenerExtra, который может задать адрес где слушать для stub. Подробности тут

www.freedesktop.org/software/systemd/man/resolved.conf.html#DNSStubListenerExtra=

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

Есть другое решение, когда контейнер пойдет на порт 53 мы будем перебрасывать его запросы в стаб systemd с помощью чего то вроде

socat UDP-LISTEN:53,fork,reuseaddr,bind=yourInterfaceIP UDP:127.0.0.53:53

Соответственно, этот DNS можно указать докеру при старте и весь функционал systemd будет работать. Но не стал это использовать.

В итоге мы получаем работающий VPN на хосте, который резолвит имена в локалной сети, работающий докер, который может резолвить эти адреса как родные.

по теме #dpi