Знакомая картина: вы развернули современный клиент на базе ядра Xray или Sing-box (Happ, Nekoray, CLI-демон), включили заветный режим TUN, проверили браузер — всё летает, геолокация сменилась, заблокированные технические доки и репозитории открываются. Вы садитесь за привычные рабочие задачи, открываете терминал, и тут начинается инфраструктурный кошмар:
- Локальные базы данных и микросервисы в
docker-composeвнезапно перестают отвечать на запросы из хостовой системы. - Команда
docker pullзависает на первом же слое, упираясь в таймауты TLS-рукопожатий. - SSH-сессии к внутреннему серверу в подсети офиса или умного дома (
192.168.10.x) бесследно дропаются. - Пакетные менеджеры, сборщики образов и консольный
gitведут себя совершенно непредсказуемо.
Почему так происходит? Для операционной системы включение TUN — это не просто «поднятие прокси для браузера». Это фундаментальное перестроение системной таблицы маршрутизации ядра на сетевом уровне (L3 OSI).
Разберем природу этих коллизий в Linux и пошагово настроим рабочую среду так, чтобы и контейнеры жили, и прокси работал стабильно.
1. Что на самом деле происходит в ядре Linux при включении TUN
Чтобы понять причину багов, взглянем на вывод утилиты ip route до и после включения TUN-режима. В штатном состоянии таблица маршрутов Linux предельно проста:
Bash
$ ip route show
default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
Когда клиент (например, Xray TUN или встроенный драйвер в Sing-box) активирует режим туннелирования, в системе создается виртуальный интерфейс (tun0 или кастомный вроде happ-xray). Чтобы заставить абсолютно весь трафик идти через него, клиент выполняет один из двух маневров:
- Создает маршрут по умолчанию с наивысшим приоритетом:
default dev happ-xray metric 1. - Либо применяет классический трюк OpenVPN: разбивает маршрут
0.0.0.0/0на две половинки:0.0.0.0/1и128.0.0.0/1. По правилу Longest Prefix Match ядро всегда отдает приоритет более специфичной маске перед общим/0.
В этот момент сетевой стек переключается: любой сгенерированный локальным процессом пакет ядро пытается протолкнуть в сокет туннеля.
Корень проблемы: конфликт серых диапазонов
Клиенты вроде Xray и Sing-box работают в пользовательском пространстве (userspace). Они вычитывают raw-пакеты из виртуального интерфейса, разбирают заголовки TCP/UDP и по своим внутренним правилам (routing.rules) решают: выпустить пакет локально или завернуть в исходящий шифрованный туннель до внешнего VPS.
Если во внутренних правилах клиента нет строгих исключений для приватных сетей, пакет к 172.18.0.2 (ваш локальный контейнер с PostgreSQL или Redis) упаковывается в VLESS/Trojan и улетает на удаленный VPS в Нидерландах или Германии. Разумеется, удаленный сервер ничего не знает о вашей внутренней топологии и молча сбрасывает соединение.
2. Лечим изоляцию Docker-контейнеров
Демон Docker при создании сетей откусывает подсети из пространства 172.17.0.0/16 (дефолтный мост docker0) и динамически генерирует новые пулы 172.18.0.0/16, 172.19.0.0/16 под каждый запущенный docker-compose.yml.
Здесь кроется двойная опасность: если TUN-клиент случайно сам выбрал IP из диапазона 172.19.0.0/24, наступает жесткий конфликт на уровне ядра хоста — два интерфейса претендуют на один и тот же маршрут.
Шаг 1. Жесткая фиксация подсетей Docker
Первым делом заставим Docker брать адреса строго из заранее выделенного диапазона. Создаем или дополняем конфигурационный файл /etc/docker/daemon.json:
sudo nano /etc/docker/daemon.json
Вставляем блок конфигурации:
JSON
{
"bip": "172.28.0.1/16",
"default-address-pools": [
{
"base": "172.29.0.0/16",
"size": 24
}
],
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
Что здесь произошло: мы закрепили стандартный мост docker0 на адресе 172.28.0.1, а все сети из Compose-файлов загнали в диапазон 172.29.0.0/16 с 24-й маской (по 254 адреса на проект). Это исключает пересечение с адресами виртуальных туннелей (которые чаще всего занимают 172.19.x.x или 10.x.x.x).
Перезапускаем службу:
Bash
sudo systemctl restart docker
Шаг 2. Настройка Direct-роутинга для приватных IP в клиенте
Теперь убедимся, что ядро прокси никогда не шифрует пакеты, адресованные локальным серверам, роутерам и контейнерам. В конфигурацию ядра (блок routing в Xray или route в Sing-box) добавляем правило прямого выхода (direct):
JSON
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"outboundTag": "direct",
"ip": [
"geoip:private",
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16"
]
}
]
}
}
Важно: По стандарту RFC 1918 приватный класс B охватывает адреса от
172.16.0.0до172.31.255.255. Указание маски172.16.0.0/12одной строкой защищает любые bridge-сети Docker от попадания в прокси.
3. Проксирование самого Docker Daemon: решаем проблему docker pull
Частый сценарий: в браузере всё открывается, но попытка собрать проект или вытянуть базовый образ падает по таймауту:
$ docker pull golang:1.24-alpine
Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded)
Причина: демон dockerd запускается системным менеджером systemd как изолированный процесс в собственном пространстве имен еще до старта пользовательских графических сессий. Если в системе активен TUN, резолв адресов реестров через локальные DNS-заглушки (например, systemd-resolved) внутри демона сбоит.
Самое надежное решение — явно указать демону локальный HTTP-прокси, который параллельно поднимает любой Xray-клиент (по умолчанию порт 10809).
-
Создаем каталог переопределения юнита:Bash
sudo mkdir -p /etc/systemd/system/docker.service.d sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf -
Прописываем переменные окружения:Ini, TOML
[Service] Environment="HTTP_PROXY=http://127.0.0.1:10809" Environment="HTTPS_PROXY=http://127.0.0.1:10809" Environment="NO_PROXY=localhost,127.0.0.1,172.16.0.0/12,192.168.0.0/16,.corp.local" -
Перезагружаем конфигурацию systemd и перезапускаем Docker:Bash
sudo systemctl daemon-reload sudo systemctl restart docker -
Проверяем, подхватились ли параметры:Bash
sudo systemctl show --property=Environment docker
Теперь скачивание манифестов и слоев через docker pull пойдет на полной скорости канала без подвисаний.
4. Git, SSH и корпоративные репозитории
Разработчики регулярно клонируют код с GitHub, GitLab или корпоративных серверов. При этом смешивать внешние и внутренние ресурсы в одну кучу нельзя.
Тонкая настройка Git по HTTPS
Не нужно прописывать глобальный http.proxy на всю систему — это сломает доступ к внутренним корпоративным серверам Git в локальной сети. Настройте проксирование точечно для нужных публичных хостингов:
# Заворачиваем только GitHub через локальный SOCKS5 (порт 10808)
git config --global http.https://github.com.proxy socks5h://127.0.0.1:10808
# Заворачиваем GitLab.com при необходимости
git config --global http.https://gitlab.com.proxy socks5h://127.0.0.1:10808
Обратите внимание на букву «h»: схема
socks5h://принципиально отличается отsocks5://. Буква «h» заставляет клиент передавать доменное имя узла для резолва на стороне удаленного прокси-сервера. Если указать простоsocks5://, резолв домена попытается выполниться локально, что может привести к ошибкам DNS.
Проброс Git по SSH через ProxyCommand
Если репозитории клонируются по SSH (git@github.com:...), настройки http.proxy игнорируются, так как трафик идет по 22-му порту.
Открываем клиентский файл конфигурации SSH:
nano ~/.ssh/config
И настраиваем автоматический проброс соединения через openbsd-netcat (пакет netcat-openbsd должен быть установлен в системе):
Фрагмент кода
# Подключение к GitHub через локальный SOCKS5
Host github.com
User git
Port 22
ProxyCommand nc -X 5 -x 127.0.0.1:10808 %h %p
ServerAliveInterval 30
ServerAliveCountMax 5
# Корпоративный сервер пускаем напрямую
Host gitlab.mycompany.internal
User git
Port 22
ProxyCommand none
Проверяем соединение:
Bash
$ ssh -T git@github.com
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
5. Удобный switch-скрипт для терминала (Bash/Zsh)
Что делать, если тяжелый системный TUN прямо сейчас не нужен, но требуется запустить curl, обновить пакеты через apt, скачать зависимости через pip, npm или composer?
Добавьте в конец файла ~/.bashrc или ~/.zshrc удобную обвязку из функций:
# --- Proxy Switcher Helpers ---
proxy_on() {
local PROXY_HOST="127.0.0.1"
local SOCKS_PORT="10808"
local HTTP_PORT="10809"
export http_proxy="http://${PROXY_HOST}:${HTTP_PORT}"
export https_proxy="http://${PROXY_HOST}:${HTTP_PORT}"
export ftp_proxy="http://${PROXY_HOST}:${HTTP_PORT}"
export all_proxy="socks5h://${PROXY_HOST}:${SOCKS_PORT}"
export no_proxy="localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,*.local"
echo "[-] Terminal Proxy Activated"
echo "[-] Checking outbound IP..."
curl -s --connect-timeout 5 https://ipinfo.io/json | grep -E '"ip"|"country"|"org"'
}
proxy_off() {
unset http_proxy https_proxy ftp_proxy all_proxy no_proxy
echo "[x] Terminal Proxy Deactivated"
}
Применяем изменения в текущей сессии:
source ~/.bashrc # или source ~/.zshrc
Теперь достаточно набрать в терминале proxy_on — консоль подхватит переменные и выведет внешний IP прокси. Команда proxy_off возвращает прямое соединение.
Требования к серверу: стабильность под нагрузкой
Рабочая станция разработчика генерирует принципиально иной паттерн трафика по сравнению со смартфоном: тысячи одновременных короткоживущих TCP-соединений при параллельной установке зависимостей, непрерывные потоки сборщиков артефактов и тяжелый обмен данными с удаленными стендами.
Если бэкенд развернут на перегруженном хостинге с шумными соседями, вы будете регулярно сталкиваться с дропами пакетов на этапе TLS-handshake.
Где хостить рабочий узел:
Для задач разработки критичны не столько терабайты диска, сколько низкий джиттер (стабильность пинга), честный гигабитный порт и отсутствие скрытого троттлинга по CPU. Отличную сетевую связность и гарантированные ресурсы предоставляет скоростной VPS от UFO Hosting — быстрое развертывание чистых ОС, поддержка актуальных ядер Linux и широкие каналы без ограничений отлично подходят для личного или командного шлюза разработчика.
Итоговый чеклист проверки рабочей станции
- Диапазоны сетей Docker зафиксированы в
/etc/docker/daemon.jsonи не пересекаются с адресом TUN-адаптера. - В клиенте (Xray / Sing-box) маска
172.16.0.0/12добавлена в список правил со статусомdirect. - Для службы Docker настроен drop-in файл с параметрами локального HTTP-прокси.
- В файле
~/.ssh/configпрописанProxyCommandдля хостаgithub.comс использованиемsocks5h://. - Проверена доступность локальных баз данных и localhost при активном туннеле.