====== Конфликт сети Docker с корпоративной / VPN-сетью ====== **Страница:** ''dev:docker_network_conflict'' **Когда смотреть:** внутренний сервис не открывается, DNS «в порядке», VPN подключён, недавно поднимали Docker. ===== Симптомы ===== * Не открывается внутренний сервис (Jira, LDAP, ''change-pass.iep.loc'', git и т.п.) — ни в браузере, ни в ''curl'' * Ошибка: ''Connection refused'' или ''Failed to connect'' на портах 80/443 * ''getent hosts hostname'' возвращает **ожидаемый** IP * Проблема часто появляется после ''docker compose up'' или когда на машине много compose-проектов * VPN подключён (''tun0'' / ''tun1'' есть), но сервис недоступен ===== Суть проблемы ===== Docker создаёт bridge-интерфейсы (''br-xxxxxxxxxxxx'') с собственными подсетями: * ''172.17.0.0/16'' — default bridge * ''172.22.0.0/16'' — сеть проекта ''rpcserver_default'' * ''172.18.0.0/16'' — ''status_app-network'' Если подсеть Docker **пересекается** с корпоративной (часто диапазон ''172.16.0.0/12'', в IEP — ''172.22.0.0/16''), Linux направляет пакеты **в Docker-bridge**, а не в VPN. Это **не** проблема DNS и **не** ''resolv.conf''. Имя резолвится верно; ломается **маршрутизация** на уровне ядра Linux. ===== Один IP — два разных случая ===== Типичная ловушка: **DNS и маршрут говорят разное** про один и тот же хост. Поэтому кажется, что «имя резолвится — значит всё ок», а ''curl''/браузер всё равно не открывают URL. ==== Пример с change-pass.iep.loc ==== Выполняем подряд: grep -n change-pass /etc/hosts /etc/resolv.conf 2>/dev/null getent hosts change-pass.iep.loc ip route get 172.22.224.153 Вывод **при конфликте с Docker**: 172.22.224.153 change-pass.iep.loc 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 uid 1891243316 cache Разбор по строкам: ^ Команда / строка ^ Что кажется ^ Что на самом деле ^ | ''getent hosts'' → ''172.22.224.153 change-pass.iep.loc'' | DNS/hosts работает, IP «правильный» | Да, имя → IP определено верно. Это **не** проблема ''resolv.conf'' | | ''ip route get'' → ''dev br-355c08cd95dd src 172.22.0.1'' | — | Пакеты к ''172.22.224.153'' уходят в **Docker-bridge**, а не в VPN/IEP | | ''grep … /etc/hosts'' — пусто | Запись только в DNS | Имя может резолвиться и без ''/etc/hosts'' | **Два случая использования одной подсети ''172.22.0.0/16'':** - **Корпоративная сеть IEP** — реальный сервер ''change-pass.iep.loc'' (''172.22.224.153''), доступ через VPN (''tun0'') - **Docker-сеть ''rpcserver_default''** — локальный bridge ''br-355c08cd95dd'' с адресом ''172.22.0.1/16'' Ядро Linux не различает «корпоративный'' и «docker'' ''172.22.x.x'' — срабатывает маршрут ''172.22.0.0/16 dev br-...''. Трафик **не доходит** до сервера в IEP → ''Connection refused''. ==== Как отличить: DNS vs маршрут ==== ^ Проверка ^ Норма (конфликта нет) ^ Конфликт с Docker ^ | ''getent hosts HOST'' | IP из корпсети (напр. ''172.22.224.153'') | То же — IP верный | | ''ip route get IP'' | ''via 10.x.x.x dev tun0'' (VPN) | ''dev br-xxxxxxxxxxxx src 172.22.0.1'' | | ''curl https://HOST/'' | HTTP 200 / редирект | ''Connection refused'' | Если ''getent'' и ''curl'' расходятся по смыслу — **сначала смотрите ''ip route get''**, не ''resolv.conf''. ==== После устранения конфликта ==== docker compose down # в проекте с сетью 172.22.0.0/16 ip route get 172.22.224.153 Ожидаемый маршрут: 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211 cache Тот же IP ''172.22.224.153'', но теперь трафик идёт **через VPN**, а не в Docker-bridge. ===== Быстрая диагностика ===== ==== 1. DNS ==== HOST=change-pass.iep.loc getent hosts "$HOST" Если IP получен — резолв в порядке (DNS или ''/etc/hosts''). ==== 2. Маршрут (главная проверка) ==== ip route get $(getent hosts "$HOST" | awk '{print $1; exit}') **Плохо** — трафик в Docker: 172.22.224.153 dev br-355c08cd95dd src 172.22.0.1 **Хорошо** — трафик через VPN: 172.22.224.153 via 10.5.192.1 dev tun0 src 10.5.193.211 ==== 3. Порты и HTTP ==== IP=$(getent hosts "$HOST" | awk '{print $1; exit}') nc -vz "$IP" 80 nc -vz "$IP" 443 curl -vk --connect-timeout 5 "https://$HOST/" При конфликте с Docker — ''Connection refused'' **до** VPN, даже если ''tun0'' поднят. ==== 4. Найти конфликтующую Docker-сеть ==== ip route | grep 172.22 docker network ls docker network inspect $(docker network ls -q) | grep -E '"Name"|Subnet|Gateway' Ищите сеть, у которой ''Subnet'' совпадает с диапазоном корпоративных адресов. **Кейс 2026-08-13:** * Сеть: ''rpcserver_default'' * Subnet: ''172.22.0.0/16'' * Bridge: ''br-355c08cd95dd'' * Конфликт с: ''change-pass.iep.loc'' (''172.22.224.153'') ===== Скрипт проверки ===== Сохраните как ''check-docker-network-conflict.sh'': #!/usr/bin/env bash set -u HOST="${1:-change-pass.iep.loc}" echo "=== DNS ===" getent hosts "$HOST" || { echo "DNS fail"; exit 1; } IP=$(getent hosts "$HOST" | awk '{print $1; exit}') echo "IP=$IP" echo echo "=== Route ===" ip route get "$IP" echo echo "=== TCP 80/443 ===" nc -vz "$IP" 80 2>&1 || true nc -vz "$IP" 443 2>&1 || true echo echo "=== Docker routes (172.16/12) ===" ip route | grep -E '172\.(1[6-9]|2[0-9]|3[0-1])\.' || echo "(none)" echo echo "=== Docker networks ===" docker network inspect $(docker network ls -q) 2>/dev/null \ | grep -E '"Name"|Subnet|Gateway' || echo "docker not available" Запуск: chmod +x check-docker-network-conflict.sh ./check-docker-network-conflict.sh change-pass.iep.loc ===== Решение ===== ==== Сразу восстановить доступ ==== cd ~/projects/rpc.server docker compose down Проверка: ip route get 172.22.224.153 curl -vk https://change-pass.iep.loc/ Маршрут должен идти через ''tun0'', не через ''br-...''. ''curl'' — HTTP 200. ==== Навсегда: другой subnet в compose ==== В ''docker-compose.yml'' проекта задайте подсеть **вне** корпоративной: networks: default: ipam: config: - subnet: 192.168.220.0/24 Выбирайте диапазоны вне ''172.16.0.0/12'' (172.16–172.31), если VPN/IEP использует ''172.22.x'', ''172.18.x'' и т.п. Удобные варианты: ''192.168.200.0/24'', ''192.168.220.0/24''. ==== Глобально: пулы для новых Docker-сетей ==== ''/etc/docker/daemon.json'': { "default-address-pools": [ { "base": "192.168.200.0/16", "size": 24 } ] } Применить: sudo systemctl restart docker После смены пулов **пересоздайте** существующие compose-проекты (''docker compose down && docker compose up -d''). Старые сети с ''172.22.0.0/16'' останутся, пока их не удалите. ===== Таблица: что проверять ===== ^ Симптом ^ Вероятная причина ^ Что смотреть ^ | DNS не резолвит | ''resolv.conf'', VPN DNS, ''/etc/hosts'' | ''getent hosts'' | | DNS OK, route через ''br-...'' | Конфликт subnet Docker | ''ip route get'', ''docker network inspect'' | | Route через ''tun0'', но timeout | VPN без маршрута в сеть | ''ip route'', админы VPN | | Route OK, Connection refused | Сервис не слушает порт | ''nc -vz IP 443'' на сервере | | TLS verify error | Корпоративный CA | ''curl -k'' / установка CA в систему | ===== Профилактика ===== - Не использовать ''172.16.0.0/12'' для Docker, если работаете с IEP/RTLabs VPN - Перед ''docker compose up'' нового проекта: ''docker network inspect … | grep Subnet'' - При смене машины / VPN — один раз: ''ip route get <корп-хост>'' - Subnet каждого локального compose — в README проекта ===== Полезные команды ===== # все маршруты Docker-bridge в 172.16/12 ip route | grep '^172\.' # какой bridge за какой сетью docker network ls docker network inspect rpcserver_default # удалить «висячую» сеть (если compose уже down) docker network rm rpcserver_default ===== История кейса ===== * **Дата:** 2026-08-13 * **Хост:** ''change-pass.iep.loc'' → ''172.22.224.153'' * **Причина:** Docker-сеть ''rpcserver_default'' (''172.22.0.0/16'') перехватывала маршрут * **Решение:** ''docker compose down'' в ''rpc.server'' + subnet ''192.168.220.0/24'' в compose * **Результат:** ''ip route get'' → ''via 10.5.192.1 dev tun0'', ''curl'' → HTTP 200