Использование переадресации iptables с сохранением исходного IP-адреса

Использование переадресации iptables с сохранением исходного IP-адреса

У меня есть сервер, на котором работает 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

Связанный контент