Содержание

Конфликт сети Docker с корпоративной / VPN-сетью

Страница: dev:docker_network_conflict Когда смотреть: внутренний сервис не открывается, DNS «в порядке», VPN подключён, недавно поднимали Docker.

Симптомы

Суть проблемы

Docker создаёт bridge-интерфейсы (br-xxxxxxxxxxxx) с собственными подсетями:

Если подсеть 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 hosts172.22.224.153 change-pass.iep.loc DNS/hosts работает, IP «правильный» Да, имя → IP определено верно. Это не проблема resolv.conf
ip route getdev 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:
  1. Корпоративная сеть IEP — реальный сервер change-pass.iep.loc (172.22.224.153), доступ через VPN (tun0)
  2. 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:

Скрипт проверки

Сохраните как check-docker-network-conflict.sh:

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 в систему

Профилактика

  1. Не использовать 172.16.0.0/12 для Docker, если работаете с IEP/RTLabs VPN
  2. Перед docker compose up нового проекта: docker network inspect … | grep Subnet
  3. При смене машины / VPN — один раз: ip route get <корп-хост>
  4. 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

История кейса