Verwenden der iptables-Weiterleitung unter Beibehaltung der Quell-IP

Verwenden der iptables-Weiterleitung unter Beibehaltung der Quell-IP

Ich habe einen Server, auf dem Wireguard läuft (und benötige daher masquerade), und einen Container, der auf Port 2525 läuft.

Ich habe folgende iptablesRegeln:

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

Bei einer server:2525direkten Verbindung zu kann der Docker-Container meine echte IP-Adresse sehen ( 1.2.3.4). Bei einer Verbindung zu Port server:25sieht der Docker-Container die lokale IP, die bereitgestellt wird von 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

Wie stelle ich sicher, dass der Docker-Container die öffentliche IP-Adresse beim Verbinden mit Port 25 richtig sieht (und nicht nur beim Verbinden mit Port 2525)?

Danke

# 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

Antwort1

Überlassen Sie die Umleitung einfach Docker. Sie ist dynamisch und kann sich ändern, wenn Container hinzugefügt, entfernt oder neu gestartet werden. Aber sieheAKTUALISIERENunten.

Diese Umleitung sollte nicht auf 172.18.0.1 erfolgen, da es sich hier um den Host und nicht um den Container handelt. Wenn der Host eine solche Verbindung empfängt, wird sie vondocker-proxywelches es an den Container weiterleitet, wobei die Quell-IP-Adresse dabei verloren geht.

Docker leitet diesen Port bereits in der allerletzten Regel des Regelsatzes korrekt per DNAT + weiter (außer vom Host selbst, der docker-proxydiese Rolle spielt), zum laufenden Container mit der Adresse 172.18.0.2. Außer dass er so konfiguriert ist, dass Port 2525 statt Port 25 verwendet wird.

  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

Dies sollte mit den Docker-Einstellungen behoben werden, nicht manuelliptablesRegeln, die sich nicht ändern können, wenn sich das Containerlayout ändert. Da Port 25 privilegiert ist, sind zusätzliche Einstellungen erforderlich, wenn Docker rootless ausgeführt wird. Überprüfen Sie dieDokumentation zum Freigeben privilegierter Ports.


AKTUALISIEREN(unter Berücksichtigung der Kommentare des OP): Der OP kann derzeit nicht verwendet werden, -p 25:25da docker-proxyer mit dem SMTP-Server des lokalen Hosts kollidiert und um das Abhören auf Port 25 auf dem Host konkurriert. Das ist der Grund für die anfängliche (falsche)iptablesDie Umleitung wurde von OP vorgenommen.

Man kann entweder:

  • Deaktivieren Sie es global, docker-proxyindem Sie es dockerdmit der Eigenschaft userland-proxy„false“ ausführen.

    entweder alsParameter --userland-proxy=falseoder als Eigenschaft "userland-proxy": falsehinzugefügt zu/etc/docker/daemon.json.

    Dies ermöglicht dann die Verwendung docker run ... -p 25:25 ...(alsdokumentiert) ohne Konflikte: Der Host erreicht sich selbst, wenn er localhost oder $HOSTNAME erreicht, Remote-Systeme erreichen den Container und keine „Adresse wird bereits verwendet“ führt dazu, dass der SMTP-Daemon des Hosts oder der Container von Docker beim Start fehlschlägt.

  • oder fügen Sie eine manuelle Umleitung hinzu (mit der langwierigen Einrichtung weiter unten, um dies fast automatisch durchzuführen)

    Bei jedem Neustart des Containers besteht das Risiko, dass sich seine (interne) IP-Adresse ändert. Diese muss also berechnet werden. Bei einem Container, der mxüber ein Netzwerk benannt ist mxund nur eine IP-Adresse verwendet, kann dies wie unten erläutert erfolgen.

    Erstellen Sie eine separate Prerouting-Kette (damit sie geleert werden kann, ohne dass etwas anderes geleert werden muss) und rufen Sie sie zuerst auf:

    iptables -t nat -N mynat
    iptables -t nat -I PREROUTING -j mynat
    

    Die IP-Adresse des Containers kann programmgesteuert abgerufen werden (für den einfachen Fall eines Containers, der mxmit einer einzelnen Adresse benannt ist):

    containerip=$(docker container inspect --format '{{.NetworkSettings.IPAddress}}' mx)
    

    (oder man könntejq: containerip=$(docker container inspect mx | jq '.[].NetworkSettings.IPAddress')

    Den Namen der Bridge-Schnittstelle zu finden ist komplizierter, oder zumindest konnte ich keine Möglichkeit finden, dies nur mit zu tun docker ... inspect. Suchen Sie also die IP-Adresse auf dem Host und fragen Sie den Host mit ab, ip addressum nur die Bridge-Schnittstelle zu finden, auf der diese spezielle IP-Adresse festgelegt ist (erfordert den jqBefehl).

     bridgeip=$(docker network inspect --format '{{(index .IPAM.Config 0).Gateway}}' mx)
     bridgeinterface=$(ip -json address show to "$bridgeip"/32 | jq -r '.[].ifname')
    

    mynatBei jedem (Neu-)Start des Containers leeren und neu auffüllen :

    iptables -t nat -F mynat
    iptables -t nat -A mynat ! -i "$bridgeinterface" -p tcp --dport 25 -j DNAT --to-destination "$containerip":25
    

    was im vorliegenden Fall lauten würde:

    iptables -t nat -I mynat ! -i br-b147ffdbc9f3 -p tcp --dport 25 -j DNAT --to-destination 172.18.0.2:25

    Und um sicherzugehen, dass Dockers eigene Firewall-Regeln diesen Datenverkehr nicht blockieren, führen Sie etwas Ähnliches aus, beginnend in filter/ FORWARDimDOCKER-USERKette.

    Zunächst (wenn vom Boot aus, müssen Sie möglicherweise auch zuerstDOCKER-USER):

    iptables -N myforward
    iptables -I DOCKER-USER 1 -j myforward
    

    Dann später bei jedem (Neu-)Start des Containers:

    iptables -F myforward
    iptables -A myforward -p tcp ! -i "$bridgeinterface" -d "$containerip" -p tcp --dport 25 -j ACCEPT
    

    was im vorliegenden Fall lauten würde:

    iptables -A myforward -p tcp ! -i br-b147ffdbc9f3 -d 172.18.0.2 -p tcp --dport 25 -j ACCEPT
    

Anmerkungen:

  • Um die oben genannten Regeln zu vereinfachen und einige der Berechnungen zu vermeiden, können der Container und sein Bridge-Netzwerk mit festen IP-Adressen gestartet werden. Siehe beispielsweise diese SO Q/A:Weisen Sie dem Docker-Container eine statische IP zu.

  • Hier ist auch ein UL SE Q/A mit einer Antwort von mir über Probleme bei der Interaktion mit Docker (es richtet sich anNftablesaber einige Teile über die DOCKER-USERKetten- oder br_netfilterBrückeninteraktionen sind immer noch von Interesse):Nftables-Whitelist-Docker

verwandte Informationen