Контейнеры Windows и VPN: почему ломается локальная сеть
Контейнер Windows использует таблицу маршрутизации хоста, поэтому VPN, блокирующий локальную подсеть, забирает с собой и префикс контейнера. Режим Linux идёт другим путём.
На одном ноутбуке, с одним подключённым клиентом VPN, контейнеры Linux продолжают общаться друг с другом, а контейнеры Windows останавливаются. Переменная не образ, а путь, по которому пакеты попадают в хост. Контейнеры Windows в сети по умолчанию заимствуют таблицу маршрутизации и трансляцию хоста, поэтому политика, меняющая маршрутизацию хоста, меняет и их. Подтвердите это поиском маршрута, прежде чем что-либо переустанавливать.
Где пакеты контейнера Windows попадают в хост
Контейнер в сети по умолчанию nat получает адрес из префикса службы Host Networking Service, обычно где-то внутри 172.16.0.0/12, и достигает хоста через виртуальный адаптер. Хост владеет маршрутом для этого префикса и транслирует трафик к тому адаптеру, который сейчас активен. В середине этого пути нет отдельной виртуальной машины.
Именно эта схема делает сбой механическим. Когда контейнер отправляет пакет, хост выполняет поиск маршрута для префикса контейнера. Какой маршрут победит, туда пакет и уйдёт, и контейнер тут ничего не решает.
Что клиент VPN меняет на этом пути
Корпоративный клиент часто устанавливает широкий набор маршрутов и фильтр, который запрещает доступ к локальной подсети, пока туннель активен. Намерение защитное: скомпрометированная локальная сеть не должна достигать защищённой машины. Побочный эффект в том, что маршрут хоста к префиксу контейнера больше не является локальной сетью NAT.
Вместо этого поиск совпадает с туннелем, и пакет передаётся шлюзу, который никогда не слышал об этом адресе контейнера. Ping не проходит, приложение упирается в тайм-аут, и контейнер выглядит сломанным, пока значок VPN светится. Посмотрите на таблицу хоста при подключении, и конкурирующий маршрут виден.
| Container mode | Outbound path | Typical result behind the VPN |
|---|---|---|
| Windows, default nat | Host routes and translates container traffic | Container prefix follows the tunnel and fails |
| Windows, transparent | Container sits on the physical link | Depends on the local subnet being allowed |
| Linux engine on Docker Desktop | VM egress proxied by a host process | Often keeps working |
Почему контейнеры Linux ведут себя иначе
Движок Linux запускает контейнеры внутри лёгкой виртуальной машины, а исходящие соединения из этой машины открывает прокси на стороне хоста от имени контейнера. Сокет инициирует хост, поэтому правило VPN о префиксе контейнера не применяется к нему тем же образом, и рабочая нагрузка продолжает достигать сети.
Это различие объясняет и то, почему рецепты для Linux не переносятся. Контейнеру Linux, которому нужен собственный туннель, дают устройство TUN плюс возможность NET_ADMIN. Контейнеры Windows не предоставляют интерфейс как устройство, поэтому у этого шаблона нет эквивалента на той стороне.
Докажите маршрут, затем выберите схему
Собирайте доказательства с хоста, а не из контейнера, потому что поиск выполняет хост:
docker network inspect nat --format "{{range .IPAM.Config}}{{.Subnet}}{{end}}"
route print -4
Get-NetRoute -AddressFamily IPv4 | Where-Object DestinationPrefix -like "172.16.*"
Запустите вывод маршрутов один раз до подключения VPN и один раз после. Если во втором выводе префикс контейнера указывает на адаптер туннеля, диагноз завершён, и перезапуск контейнеров не поможет.
Первый вариант — решение о политике: спросите владельца сети, можно ли разрешить локальный доступ к LAN для этого устройства. Несколько корпоративных клиентов предлагают это как настройку, но она ослабляет намеренную границу. Просите это как документированное исключение с владельцем и датой пересмотра. Переключатель для каждого устройства, к которому никто не возвращается, — это способ, которым защитная политика тихо исчезает.
Там, где политика должна оставаться, переносите рабочую нагрузку вместо борьбы с маршрутом. Прозрачная сеть с явной подсетью и шлюзом ставит контейнеры на локальное звено, рабочая нагрузка Linux работает за прокси виртуальной машины, и отдельная виртуальная машина со своим клиентом сохраняет собственную политику. Выберите одну осознанно и проверьте её изнутри контейнера, потому что именно там возникает запрос пользователя.