![Usar el reenvío de iptables, mientras se mantiene correctamente la IP de origen](https://rvso.com/image/776289/Usar%20el%20reenv%C3%ADo%20de%20iptables%2C%20mientras%20se%20mantiene%20correctamente%20la%20IP%20de%20origen.png)
Tengo un servidor que ejecuta Wireguard (por lo que necesito masquerade
) y un contenedor que se ejecuta en el puerto 2525.
Tengo las siguientes iptables
reglas:
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:2525
directamente, 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-proxy
que 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-proxy
desempeñ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:25
porque docker-proxy
choca 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-proxy
ejecutandodockerd
con la propiedaduserland-proxy
establecida en falsoya sea como unparámetro
--userland-proxy=false
o como una propiedad"userland-proxy": false
añ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
mx
usando una red nombradamx
y 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
mx
con una única dirección):containerip=$(docker container inspect --format '{{.NetworkSettings.IPAddress}}' mx)
(o uno podría usar
jq
: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 paraip address
encontrar solo la interfaz del puente donde está configurada esta dirección IP específica (requiere eljq
comando).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
mynat
cada 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/
FORWARD
desde elDOCKER-USER
cadena.Inicialmente (si es desde el arranque, es posible que también tengas que crear primero
DOCKER-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-USER
en cadena obr_netfilter
puente siguen siendo de interés):Ventana acoplable de lista blanca de nftables