OpenVPN через SOCKS5: согласовать версию, транспорт и DNS
Цепочка SOCKS ломается, когда версия прокси, транспорт или путь DNS не совпадают. Согласуйте SOCKS5, переносите TCP и держите цепочку внутри одного пространства имён.
Один из маршрутов наружу с этой рабочей станции — прокси SOCKS, и OpenVPN за ним сообщает о тайм-ауте соединения без каких-либо полезных подробностей. Браузер на той же машине работает через этот прокси, учётные данные верны, и порт отвечает. Три вещи расходятся достаточно часто, чтобы проверить их первыми: версия SOCKS, на которой говорит прокси, транспорт, выбранный клиентом, и то место, где происходит разрешение имён. Подтвердите каждую, прежде чем винить межсетевой экран.
Согласуйте версию SOCKS до межсетевого экрана
Community OpenVPN говорит на SOCKS5 для socks-proxy. Направьте его на прокси, который предлагает только SOCKS4, и TCP-соединение откроется, а затем зависнет: клиент ждёт ответа на приветствие, которое другая сторона никогда не разбирает. Снаружи это зависание выглядит в точности как отфильтрованный порт, поэтому вопрос версии идёт раньше любого правила межсетевого экрана. Одна загрузка с поддержкой SOCKS решает это одной командой.
curl --socks5-hostname 127.0.0.1:9050 gw.lab.example
Завершённый запрос доказывает сразу две вещи: прокси принял запрос SOCKS и разрешил имя. Схема URL не нужна, потому что curl считает голое имя хоста HTTP, и для этого теста этого достаточно.
Эта загрузка исключает и сбой, который скрывает журнал клиента. Прокси может разрешить браузер и всё равно отказать в пункте назначения VPN, и тогда отказ происходит на CONNECT, до любой аутентификации VPN. OpenVPN сообщает только о тайм-ауте, поэтому собственный ответ прокси — это доказательство, которое вам нужно.
Транспорт должен быть TCP с обеих сторон
Здесь SOCKS5 несёт поток TCP, поэтому самому туннелю нужен proto tcp-client. Профиль, всё ещё настроенный на UDP, по этому пути не заработает, потому что клиент не использует режим UDP-ассоциации протокола. Переключение профиля — это настоящее изменение, а не формальность: дальняя сторона должна слушать TCP на том же порту. Затем полезная нагрузка становится TCP внутри TCP, где потерянный сегмент повторяется дважды и темп падает, что интерактивные сеансы замечают куда сильнее, чем большие передачи.
proto tcp-client
remote gw.lab.example 8443
socks-proxy 127.0.0.1 9050
Директива принимает хост прокси, необязательный порт и необязательный файл учётных данных. Поместите их в сам профиль, чтобы копия вела себя так же в другом месте.
Замкните всю цепочку в одном контейнере
Делать цепочку на хосте означает занять его маршрут по умолчанию и оставить распознаватель в состоянии, которое потом никто не объяснит. Пространство имён или контейнер ограничивают её вместо этого, так что хост сохраняет свой путь, а шаг SOCKS и туннель живут вместе. Внутри ничего не поднимется без устройства туннеля, прав на его настройку и доступного прокси. Сервис Compose получает это с cap_add: NET_ADMIN и примонтированным /dev/net/tun.
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
Этот набор уже́, чем запуск контейнера в привилегированном режиме, и это весь необходимый доступ к устройству. Клиент Tor или отдельный контейнер прокси может дать слушателя. В любом случае привяжите его к loopback и добирайтесь из пространства имён через хост, вместо того чтобы выставлять прокси в сеть.
Разрешение имён — там, где изоляция даёт сбой
Туннель, который несёт пакеты и при этом спрашивает распознаватель хоста, ничего не изолировал. Изоляцию делают настоящей две вещи: у пространства имён есть свой распознаватель, и его запросы идут через прокси или туннель, никогда через физический интерфейс. Для проверки возьмите два имени: одно, которое существует только за туннелем, и публичное, которое должно идти тем же путём.
docker compose exec vpn cat /etc/resolv.conf
docker compose exec vpn getent hosts internal.lab.example
Если дальнее имя разрешается, а публичное нет, цепочка избирательна, а не сломана, и разница говорит, какой путь прошёл каждый запрос. Если оба разрешаются с хоста, но ни одно внутри контейнера, контейнер по-прежнему одалживает распознаватель хоста. Запишите найденный файл распознавателя, потому что именно эту настройку нужно менять.
Докажите каждый шаг по порядку
Список короткий. Запрос SOCKS5 получает ответ, туннель доходит до своего слушателя через прокси, а пакеты, покидающие пространство имён, несут адрес, которого ждёт дальняя сторона. Подтверждайте по одному, вместо того чтобы наращивать конфигурацию. Когда клиент говорит «подключено», а ничего не движется, разрыв почти всегда во втором или третьем факте.
curl --socks5-hostname 127.0.0.1:9050 gw.lab.example
docker compose exec vpn ip route get 203.0.113.10
docker compose exec vpn getent hosts internal.lab.example
Запишите сочетание, которое сработало: версия SOCKS, транспорт, распознаватель и настройки пространства имён. Решать эту цепочку второй раз по памяти дороже заметки. Держите эти четыре факта в тикете, а не в переписке.
Цепочка, которая сообщает «подключено» и ничего не пропускает, обычно сломалась на том шаге, который вы ещё не доказали. Проверьте этот шаг, прежде чем добавлять ещё один слой.