Iptables — не могу заставить работать мои собственные правила SNAT
Проще говоря, у меня есть две сети: одна для докера, другая для Libvirt. Мне нужно разрешить одному контейнеру из сети докеров обращаться ко всем виртуальным машинам в сети Libvirt. Итак, я добавил правило SNAT, чтобы соответствовать любому пакету, полученному из «172.17.0.4» (IP-адрес контейнера) и предназначенному для «192.168.122.0/24» (сеть libvirt), и нацелил эти пакеты на SNAT следующим образом.
sudo iptables -t NAT -I POSTROUTING 1 -s 172.17.0.4 -d 192.168.122.0/24 -j SNAT --to 192.168.122.1
Но, к сожалению, когда я попытался отправить несколько эхо-сигналов, ни один из пакетов не соответствовал правилу, и я получаю ошибку «Порт назначения недоступен» на уровне докера, и пакеты никогда не достигают ни одной из виртуальных машин.
Изучив это, я нашел приведенную ниже цепочку в таблице фильтров, созданной libvirt:
Chain LIBVIRT_FWO (1 references)
target prot opt source destination
ACCEPT all -- 192.168.121.0/24 anywhere
REJECT all -- anywhere anywhere reject-with icmp-port-
unreachable
ACCEPT all -- 192.168.122.0/24 anywhere
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
Вот вывод команды ip Route из контейнера:
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.4
Любые идеи?
2 ответа
Вам не нужны какие-либо специальные правила NAT.
Трафик, выходящий из контейнеров, будет соответствовать правилу, которое Docker устанавливает для сети (например,-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
), поэтому ваши виртуальные машины будут видеть трафик, исходящий от моста libvirt (192.168.122.1
).
Конфигурация
У меня есть контейнер Docker с именемexample-container
прикреплен к:
$ docker ps
[...]
0ddd3554466b alpine "/bin/sh" 5 seconds ago Up 4 seconds example-container
Этот контейнер имеет следующую конфигурацию сети:
/ # ip addr show eth0
2642: eth0@if2643: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fdc9:87ad:f15:5bea:0:242:ac11:2/64 scope global flags 02
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:2/64 scope link
valid_lft forever preferred_lft forever
У меня есть виртуальная машина с именемexample-vm
прикреплен к libvirtdefault
сеть:
$ virsh list
Id Name State
----------------------------
2 example-vm running
Виртуальная машина имеет следующую конфигурацию сети:
[root@localhost ~]# ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:30:7b:a6 brd ff:ff:ff:ff:ff:ff
altname enp1s0
inet 192.168.122.70/24 brd 192.168.122.255 scope global dynamic noprefixroute eth0
valid_lft 3548sec preferred_lft 3548sec
inet6 fe80::2ccd:72ed:1fea:ccda/64 scope link noprefixroute
valid_lft forever preferred_lft forever
Из контейнера в виртуальную машину
Изнутри контейнера я могу на виртуальной машине:
/ # ping -c2 192.168.122.70
PING 192.168.122.70 (192.168.122.70): 56 data bytes
64 bytes from 192.168.122.70: seq=0 ttl=63 time=0.262 ms
64 bytes from 192.168.122.70: seq=1 ttl=63 time=0.286 ms
--- 192.168.122.70 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.262/0.274/0.286 ms
Если я при этом запущу виртуальную машину, я увижу:
[root@localhost ~]# tcpdump -i eth0 -n icmp
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
07:23:38.661246 IP 192.168.122.1 > 192.168.122.70: ICMP echo request, id 9, seq 0, length 64
07:23:38.661277 IP 192.168.122.70 > 192.168.122.1: ICMP echo reply, id 9, seq 0, length 64
07:23:39.661247 IP 192.168.122.1 > 192.168.122.70: ICMP echo request, id 9, seq 1, length 64
07:23:39.661261 IP 192.168.122.70 > 192.168.122.1: ICMP echo reply, id 9, seq 1, length 64
Из виртуальной машины в контейнер
Аналогично я могу пропинговать контейнер с виртуальной машины:
[root@localhost ~]# ping -c2 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=63 time=0.149 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=63 time=0.127 ms
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1035ms
rtt min/avg/max/mdev = 0.127/0.138/0.149/0.011 ms
И в контейнере показывает:
/ # tcpdump -i eth0 -n
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:24:48.690361 IP 172.17.0.1 > 172.17.0.2: ICMP echo request, id 2, seq 1, length 64
12:24:48.690373 IP 172.17.0.2 > 172.17.0.1: ICMP echo reply, id 2, seq 1, length 64
12:24:49.723189 IP 172.17.0.1 > 172.17.0.2: ICMP echo request, id 2, seq 2, length 64
12:24:49.723204 IP 172.17.0.2 > 172.17.0.1: ICMP echo reply, id 2, seq 2, length 64
Правила сетевого фильтра
Это работает, потому что Docker устанавливает следующее правило:
host# iptables -t nat -S POSTROUTING
-P POSTROUTING ACCEPT
[...]
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
[...]
Когда трафик выходит из контейнера, он попадает вMASQUERADE
правило. Поскольку libvirtvirbr0
интерфейс является выходным интерфейсом для этого пакета, он перезаписывается на адрес моста, поэтому вtcpdump
вывод в виртуальной машине мы видимping
запросы, исходящие от 192.168.122.1.
Сходным образом,libvirt
устанавливает следующие правила:
host# iptables -t nat -S LIBVIRT_PRT
[...]
-A LIBVIRT_PRT -s 192.168.122.0/24 -d 224.0.0.0/24 -j RETURN
-A LIBVIRT_PRT -s 192.168.122.0/24 -d 255.255.255.255/32 -j RETURN
-A LIBVIRT_PRT -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE
[...]
Они достигают того же самого; запросы от виртуальной машины к контейнеру перезаписываются на адресdocker0
мост.
По неизвестной причине я нашел следующую запись в таблице фильтров iptables.
-A LIBVIRT_FWI -o virbr1 -j REJECT --reject-with icmp-port-unreachable
После его удаления я могу без проблем получить доступ к сети libvirt из контейнера докеров.
Спасибо Теро и Ларксу за вашу помощь.