====== Конфликт сети 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