ㅤ
Последнее время всё чаще начал сталкиваться с тем, что IP сервера висит на Dummy интерфейсе и вещаются в сторону сетевиков организации/хостера/провайдера по BGP.
🔤🔤🔥🔤🔤🔤🔤🔤🔤
Вроде бы всё выглядит и ничего, и всё работает, но:
1. У меня есть Docker на таких серверах
2. И он в упор отказывается 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...
Итоги
Мы имеем работающие Docker контейнеры, которые SNAT-ятся с нужного нам IP, кровь из глаз после вида исходников, и непреодолимое желание написать разработчикам, что бы поправили доку.
🛠 #балансбатл #networks
—
✅ @bashdays ✅ @linuxfactory ✅ @blog