Хостът разглежда през тунела, а контейнерът до него не достига нищо. Публикуването на порт от контейнер със собствен VPN клиент е по-лошо: заявката пристига, а отговорът никога не излиза. И двете са решения за маршрутизация на хоста, а Docker е само пратеникът.

Три въпроса решават кой от двата случая виждате и отнемат около минута.

Две повреди с еднакъв вид

Първата е проблем с обратния път. Публикуваният порт се преобразува към контейнера, заявката пристига, а отговорът се маршрутизира по маршрута по подразбиране на хоста, който клиентът OpenVPN е насочил към tun0. Отговорът излиза от адрес, който отдалеченият партньор не очаква, и бива изхвърлен. Втората е сблъсък на префикси: мостът по подразбиране на Docker притежава 172.17.0.0/16, а корпоративен тунел изтласква същия диапазон.

Едната се поправя с таблица за маршрутизация, другата — с преномериране на мост. Редактирането, преди да знаете кое е кое, е начинът да счупите работещ контейнер.

Решете с три въпроса

Въпрос първи: изчезва ли проблемът, когато спрете VPN? Ако всичко работи при изключен тунел, подозирайте обратния път. Въпрос втори: дали ip route show table all изброява същия префикс два пъти — веднъж на мост и веднъж на tun0? Това е сблъсъкът. Въпрос трети: засегнатият адрес контейнер ли е, NAT гост или гост, свързан чрез мост?

Третият въпрос е важен, защото всяко има различно правилно решение, а прилагането на решението за контейнер към гост libvirt губи една вечер.

ip route
ip route show table all | grep -E '172\.1[7-9]|10\.8'
docker network inspect bridge --format '{{.IPAM.Config}}'

Обратен път: таблица за един адрес на източник

Когато трафикът на контейнера трябва да излиза през физическия интерфейс, докато самият хост използва тунела, маршрутизация по източник е най-малката промяна. Добавете правило, което съвпада с адреса на контейнера, и го насочете към отделна таблица. Нищо друго на хоста не се мести.

ip rule add from 172.22.0.3 lookup 10
ip route add default via 172.22.0.1 table 10

Всеки пакет, чийто източник е този адрес, първо проверява таблица 10 и таблица 10 го изпраща към шлюза на моста, а не към тунела.

Потвърдете с ip route get 1.1.1.1 from 172.22.0.3. Отговорът трябва да посочва интерфейса на моста, а не tun0. Правилата, добавени по този начин, изчезват при рестарт, затова ги сложете в unit или мрежова кука, която притежава контейнера.

Същият модел работи и за контейнер WireGuard, където обратният път също изисква явно правило.

Сблъсък на префикси: преномерирайте моста

Таблицата за маршрутизация изпраща един префикс на едно място. Ако VPN изтласка 172.17.0.0/16, докато мостът на Docker вече е там, печели по-ниската метрика: Docker инсталира свързания си маршрут с метрика 0, а изтласканият маршрут пристига с 50. Тогава тунелът изглежда счупен, докато Docker продължава да работи, или обратното, в зависимост от реда на инсталиране. Преномерирайте моста, вместо да се борите с метриката.

{
  "bip": "10.200.0.1/24",
  "default-address-pools": [{"base": "10.201.0.0/16", "size": 24}]
}

Рестартирайте Docker след промяната и пресъздайте мрежите, изградени със стария пул. Смяната на метриката вместо това само премества конфликта към следващото зареждане.

Виртуалните машини отново искат друг отговор

Гост libvirt в мрежата NAT по подразбиране наследява маршрутизацията на хоста и спира да достига до външния свят, щом тунелът на хоста завладее маршрута по подразбиране. Превключването на госта към мостово или macvtap устройство му дава собствен път във физическата мрежа. В virt-manager добавете хардуер, изберете Network, задайте източника като macvtap хост устройство, запазете virtio като модел и след това деактивирайте стария NAT интерфейс.

Гост macvtap обикновено не може да достигне собствения си хост. Това е очаквано поведение, а не неизправност, и мястото му е в бележката, която оставяте.

За гост, който се нуждае само от LAN на хоста, клиентът на доставчика може да има липсващия превключвател: с Mullvad mullvad lan set allow възстановява локалната достъпност, без да пипа libvirt. А когато контейнер трябва да е достъпен само през тунела, вържете публикувания порт към адреса на тунела, -p 10.8.0.5:8080:8080, вместо да го публикувате на всеки адрес.

Подобни статии