Учим Docker в SNAT
Последнее время всё чаще начал сталкиваться с тем, что IP сервера висит на Dummy интерфейсе и вещаются в сторону сетевиков организации/хостера/провайдера по BGP.
Вроде бы всё выглядит и ничего, и всё работает, но:
- У меня есть Docker на таких серверах
- И он в упор отказывается SNAT-ить контейнеры с IP Dummy-ка
Это надо как-то исправить.
Смотрим, то что мы имеем
Такс, у нас есть:
Железонька стандартная, с двумя сетевыми интерфейсами, которые воткнуты в сеть провайдера.
На интерфейсе
p1p0у нас адрес10.0.0.1/31(у провайдера соответственно10.0.0.0/31)На интерфейсе
p1p1у нас адрес10.0.0.3/31(у провайдера соответственно10.0.0.2/31)На интерфейсе
dummy-moewу нас висит адрес188.222.33.1/32На сервере стоит гордый птыц bird, который делает bgp с провайдером
Зачем 32е и 31е маски? Потому что могу, да и ну его, айпишники тратить на 30е маски…
Делаем докериную сетку (bridge) и запускаем контейнер (например alpine:3), проверяем сетку:
docker network create -d bridge br-dzhigurda
docker run -it --rm --network br-dzhigurda alpine:3
ping -c 1 8.8.8.8
Получаем:
PING 8.8.8.8 (8.8.8.8): 56 data bytes
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
Херня, товарищи! Идём смотреть правила в iptables:
-A POSTROUTING -s 198.18.66.0/24 ! -o br-dzhigurda -j MASQUERADE
Видим, что Docker делает MASQUERADE. Это обычный, всем привычный NAT, но! оно работает только с физическими сетевыми интерфейсами. И весь трафик от сервера выходит с IP именно физических интерфейсов. Нам такое не подходит.
Имеем, то что мы смотрим
Закапываемся в доку Docker и трём глаза в изумлении, ничего не нашли. Отлично, полезем в кишочки разбираться. Нам надо попробовать сделать вместо MASQUERADE - SNAT.
Идём в сырцы Docker (а точнее в moby/moby), грепаем, копаемся, и видим волшебные грибы которые потребляли разработчики строчки:
// Файл integration/network/network_linux_test.go
ipv4SNATAddr := "172.0.0.172"
// Create a bridge network with --opt com.docker.network.host_ipv4=172.0.0.172
bridgeName := "hostIPv4Bridge"
network.CreateNoError(ctx, t, c, bridgeName,
network.WithDriver("bridge"),
network.WithOption("com.docker.network.host_ipv4", ipv4SNATAddr),
network.WithOption("com.docker.network.bridge.name", bridgeName),
)
Ага. В доке этого не было. Поехали, попробуем!
docker network create -d bridge br-fuckingsnat --opt com.docker.network.bridge.name=br-fuckingsnat --opt com.docker.network.host_ipv4=188.222.33.1
docker run -it --rm --network br-fuckingsnat alpine:3
ping -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=108 time=16.5 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 16.497/16.497/16.497/0.000 ms
Отлично, связь есть. Смотрим в iptables:
-A POSTROUTING -s 198.18.66.0/24 ! -o br-fuckingsnat -j SNAT --to-source 188.222.33.1
Поразительно. Не, я точно слепой, такую же фичу не могли в документации пропустить… Или могли? Идём гуглим, и получаем всего один результат! И тот в Release Notes…
Итоги








Комментарии