Usar el reenvío de iptables, mientras se mantiene correctamente la IP de origen

Usar el reenvío de iptables, mientras se mantiene correctamente la IP de origen

Tengo un servidor que ejecuta Wireguard (por lo que necesito masquerade) y un contenedor que se ejecuta en el puerto 2525.

Tengo las siguientes iptablesreglas:

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

Cuando me conecto server:2525directamente, el contenedor Docker puede ver mi dirección IP real ( 1.2.3.4). Al conectarse al puerto server:25, el contenedor Docker ve la IP local proporcionada por 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

¿Cómo me aseguro de que el contenedor Docker vea correctamente la dirección IP pública mientras me conecto al puerto 25 (y no solo cuando me conecto al puerto 2525)?

Gracias

# 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

Respuesta1

Simplemente deje que Docker se encargue de la redirección, que es dinámica y podría cambiar cuando se agregan, eliminan o reinician contenedores. Pero miraACTUALIZARabajo.

Esta redirección no debe ser a 172.18.0.1, que es el host y no el contenedor. Cuando el host recibe dicha conexión, es manejada pordocker-proxyque lo envía al contenedor, perdiendo la dirección IP de origen en el proceso.

Docker ya DNAT+ enruta este puerto correctamente (excepto desde el propio host, donde docker-proxydesempeña esta función) en la última regla del conjunto de reglas, al contenedor en ejecución con la dirección 172.18.0.2. Excepto que está configurado para usar el puerto 2525 en lugar del puerto 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

Esto debería solucionarse con la configuración de Docker, no manualmenteiptablesreglas que no pueden cambiar cada vez que cambia el diseño del contenedor. Como el puerto 25 tiene privilegios, si Docker se ejecuta sin raíz, se necesitan configuraciones adicionales, verifique ladocumentación sobre la exposición de puertos privilegiados.


ACTUALIZAR(teniendo en cuenta los comentarios de OP): OP no se puede usar actualmente -p 25:25porque docker-proxychoca con el servidor SMTP del host local y compite por escuchar en el puerto 25 del host. Esa es la razón por la que la inicial (incorrecta)iptablesLa redirección fue realizada por OP.

Uno puede:

  • deshabilitar globalmente docker-proxyejecutando dockerdcon la propiedad userland-proxyestablecida en falso

    ya sea como unparámetro --userland-proxy=falseo como una propiedad "userland-proxy": falseañadida a/etc/docker/daemon.json.

    Esto permitirá entonces utilizar docker run ... -p 25:25 ...(comodocumentado) sin conflictos: el host se alcanzará a sí mismo cuando llegue a localhost o $HOSTNAME, los sistemas remotos llegarán al contenedor y ninguna "Dirección ya en uso" hará que el demonio SMTP del host o el contenedor de Docker fallen al inicio.

  • o agregue una redirección manual (con una configuración extensa a continuación para hacerlo casi automáticamente)

    Cada vez que se reinicia el contenedor, existe el riesgo de que su dirección IP (interna) cambie. Entonces esto debe calcularse. Entonces, con un contenedor nombrado mxusando una red nombrada mxy una única dirección IP involucrada, esto se puede hacer como se explica a continuación.

    Cree una cadena de enrutamiento previo separada (para que se pueda vaciar sin tener que vaciar nada más) y llámela primero:

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

    La dirección IP del contenedor se puede recuperar mediante programación (para el caso simple de un contenedor nombrado mxcon una única dirección):

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

    (o uno podría usarjq: containerip=$(docker container inspect mx | jq '.[].NetworkSettings.IPAddress')

    Encontrar el nombre de la interfaz del puente es más complicado, o al menos no pude encontrar una manera de hacerlo usando solo docker ... inspect. Así que busque su dirección IP en el host y consulte el host para ip addressencontrar solo la interfaz del puente donde está configurada esta dirección IP específica (requiere el jqcomando).

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

    Enjuague y vuelva a llenar mynatcada vez que se (re)inicie el contenedor:

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

    cual sería para el caso actual:

    iptables -t nat -¡Yo mynat! -i br-b147ffdbc9f3 -p tcp --dport 25 -j DNAT --to-destino 172.18.0.2:25

    Y para asegurarse de que las propias reglas de firewall de Docker no bloqueen dicho tráfico, haga algo similar comenzando en filter/ FORWARDdesde elDOCKER-USERcadena.

    Inicialmente (si es desde el arranque, es posible que también tengas que crear primeroDOCKER-USER):

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

    Luego, cada vez que se (re)inicia el contenedor:

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

    cual sería para el caso actual:

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

Notas:

  • Para simplificar las reglas anteriores y evitar algunos de los cálculos, el contenedor y su red puente se pueden iniciar con direcciones IP fijas. Vea, por ejemplo, esta SO Q/A:Asignar IP estática al contenedor Docker.

  • Aquí también hay una pregunta/respuesta de UL SE con una respuesta mía sobre problemas al interactuar con Docker (está diseñado paranftablespero algunas partes sobre las interacciones DOCKER-USERen cadena o br_netfilterpuente siguen siendo de interés):Ventana acoplable de lista blanca de nftables

información relacionada