![Использование переадресации iptables с сохранением исходного IP-адреса](https://rvso.com/image/776289/%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D0%BF%D0%B5%D1%80%D0%B5%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0%D1%86%D0%B8%D0%B8%20iptables%20%D1%81%20%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5%D0%BC%20%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE%20IP-%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0.png)
У меня есть сервер, на котором работает Wireguard (поэтому ему нужен masquerade
), и контейнер, работающий на порту 2525.
У меня есть следующие iptables
правила:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 25 -j DNAT --to-destination 172.18.0.1:2525
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
При server:2525
прямом подключении к контейнер Docker видит мой реальный IP-адрес ( 1.2.3.4
). При подключении к порту server:25
контейнер Docker видит локальный IP-адрес, предоставленный docker network
:
Apr 07 12:45:46 mx postfix/smtpd[87]: lost connection after CONNECT from unknown[172.18.0.1]
Apr 07 12:45:46 mx postfix/smtpd[87]: disconnect from unknown[172.18.0.1] commands=0/0
Как убедиться, что контейнер Docker правильно видит публичный IP-адрес при подключении к порту 25 (а не только при подключении к порту 2525)?
Спасибо
# iptables -L -n -v -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
52300 3131K DNAT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:25 to:172.18.0.1:2525
150K 8524K DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 120 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
3385 256K MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
1733K 104M MASQUERADE all -- * !br-b147ffdbc9f3 172.18.0.0/16 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:53
0 0 MASQUERADE udp -- * * 172.17.0.2 172.17.0.2 udp dpt:53
0 0 MASQUERADE tcp -- * * 172.18.0.2 172.18.0.2 tcp dpt:25
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
12 1419 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- br-b147ffdbc9f3 * 0.0.0.0/0 0.0.0.0/0
56 3192 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5354 to:172.17.0.2:53
0 0 DNAT udp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5354 to:172.17.0.2:53
107 6020 DNAT tcp -- !br-b147ffdbc9f3 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:2525 to:172.18.0.2:25
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 32:d0:56:15:0a:64 brd ff:ff:ff:ff:ff:ff
altname enp0s3
altname ens3
inet 159.223.80.86/20 brd 159.223.95.255 scope global eth0
valid_lft forever preferred_lft forever
inet 10.15.0.19/16 brd 10.15.255.255 scope global eth0:1
valid_lft forever preferred_lft forever
inet6 2400:6180:0:d0::f57:6001/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::30d0:56ff:fe15:a64/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 32:dc:4a:e4:27:be brd ff:ff:ff:ff:ff:ff
altname enp0s4
altname ens4
inet 10.130.244.15/16 brd 10.130.255.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::30dc:4aff:fee4:27be/64 scope link
valid_lft forever preferred_lft forever
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.200.200.52/24 scope global wg0
valid_lft forever preferred_lft forever
5: wg1: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.222.111.1/24 scope global wg1
valid_lft forever preferred_lft forever
6: br-b147ffdbc9f3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:46:21:70:c0 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-b147ffdbc9f3
valid_lft forever preferred_lft forever
inet6 fe80::42:46ff:fe21:70c0/64 scope link
valid_lft forever preferred_lft forever
7: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:66:22:41:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:66ff:fe22:4191/64 scope link
valid_lft forever preferred_lft forever
9: veth31eff9d@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether e6:fb:80:5d:c7:a3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::e4fb:80ff:fe5d:c7a3/64 scope link
valid_lft forever preferred_lft forever
19: veth01269f5@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-b147ffdbc9f3 state UP group default
link/ether 36:f4:e7:43:5f:da brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::34f4:e7ff:fe43:5fda/64 scope link
valid_lft forever preferred_lft forever
решение1
Просто позвольте Docker заняться перенаправлением, которое является динамическим и может меняться при добавлении, удалении или перезапуске контейнеров. Но см.ОБНОВЛЯТЬниже.
Это перенаправление не должно быть на 172.18.0.1, который является хостом, а не контейнером. Когда хост получает такое соединение, оно обрабатываетсяdocker-proxy
который проксирует его в контейнер, теряя при этом исходный IP-адрес.
Docker уже DNAT + правильно маршрутизирует этот порт (кроме самого хоста, где docker-proxy
играет эту роль) в самом последнем правиле набора правил, на работающий контейнер с адресом 172.18.0.2. За исключением того, что он настроен на использование порта 2525 вместо порта 25.
107 6020 DNAT tcp -- !br-b147ffdbc9f3 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:2525 to:172.18.0.2:25
Это должно быть исправлено с помощью настроек Docker, а не вручную.iptablesправила, которые не могут меняться при изменении макета контейнера. Поскольку порт 25 является привилегированным, если Docker работает без прав root, необходимы дополнительные настройки, проверьтедокументация о раскрытии привилегированных портов.
ОБНОВЛЯТЬ(учитывая комментарии OP): OP в настоящее время не может использовать, -p 25:25
поскольку docker-proxy
конфликтует с SMTP-сервером локального хоста и конкурирует за прослушивание порта 25 на хосте. Вот почему изначально (неправильно)iptablesперенаправление было сделано OP.
Можно либо:
отключить глобально,
docker-proxy
запустивdockerd
со свойством,userland-proxy
установленным на falseлибо какпараметр
--userland-proxy=false
или как свойство,"userland-proxy": false
добавленное к/etc/docker/daemon.json
.Это затем позволит использовать
docker run ... -p 25:25 ...
(какзадокументировано) без конфликтов: хост обратится к самому себе при достижении localhost или $HOSTNAME, удаленные системы подключатся к контейнеру, и отсутствие «Адрес уже используется» приведет к сбою SMTP-демона хоста или контейнера Docker при запуске.или же добавьте ручное перенаправление (с длительной настройкой ниже, чтобы сделать это почти автоматически)
Всякий раз, когда контейнер перезапускается, существует риск, что его (внутренний) IP-адрес изменится. Поэтому это должно быть вычислено. Таким образом, с контейнером, названным
mx
с использованием имени сетиmx
и одного задействованного IP-адреса, это можно сделать, как описано ниже.Создайте отдельную цепочку предварительной маршрутизации (чтобы ее можно было очистить, не очищая ничего другого) и вызовите ее первой:
iptables -t nat -N mynat iptables -t nat -I PREROUTING -j mynat
IP-адрес контейнера можно получить программно (для простого случая, когда контейнер назван
mx
одним адресом):containerip=$(docker container inspect --format '{{.NetworkSettings.IPAddress}}' mx)
(или можно использовать
jq
:containerip=$(docker container inspect mx | jq '.[].NetworkSettings.IPAddress'
)Поиск имени интерфейса моста более запутан, или, по крайней мере, я не смог найти способ сделать это, используя только
docker ... inspect
. Поэтому найдите его IP-адрес на хосте и запросите хост, используя ,ip address
чтобы найти только интерфейс моста, где установлен этот конкретный IP-адрес (требуется командаjq
.)bridgeip=$(docker network inspect --format '{{(index .IPAM.Config 0).Gateway}}' mx) bridgeinterface=$(ip -json address show to "$bridgeip"/32 | jq -r '.[].ifname')
Очищать и заново заполнять
mynat
каждый раз при (пере)запуске контейнера:iptables -t nat -F mynat iptables -t nat -A mynat ! -i "$bridgeinterface" -p tcp --dport 25 -j DNAT --to-destination "$containerip":25
что будет для текущего случая:
iptables -t nat -I mynat ! -i br-b147ffdbc9f3 -p tcp --dport 25 -j DNAT --to-destination 172.18.0.2:25
И чтобы быть уверенным, что правила брандмауэра Docker не блокируют такой трафик, сделайте что-то подобное, начиная с filter/
FORWARD
изDOCKER-USER
цепь.Первоначально (если из загрузки, вам, возможно, придется также создать сначала
DOCKER-USER
):iptables -N myforward iptables -I DOCKER-USER 1 -j myforward
Затем каждый раз при (пере)запуске контейнера:
iptables -F myforward iptables -A myforward -p tcp ! -i "$bridgeinterface" -d "$containerip" -p tcp --dport 25 -j ACCEPT
что будет для текущего случая:
iptables -A myforward -p tcp ! -i br-b147ffdbc9f3 -d 172.18.0.2 -p tcp --dport 25 -j ACCEPT
Примечания:
Чтобы упростить правила выше и избежать некоторых вычислений, контейнер и его мостовая сеть могут быть запущены с фиксированными IP-адресами. См., например, этот SO Q/A:Назначить статический IP-адрес контейнеру Docker.
Вот также UL SE Q/A с моим ответом о проблемах при взаимодействии с Docker (он предназначен дляnftablesно некоторые части взаимодействия
DOCKER-USER
цепей илиbr_netfilter
мостиков все еще представляют интерес):nftables белый список docker