Usando o encaminhamento de iptables, mantendo adequadamente o IP de origem

Usando o encaminhamento de iptables, mantendo adequadamente o IP de origem

Eu tenho um servidor rodando o Wireguard (precisando assim masquerade) e um contêiner rodando na porta 2525.

Eu tenho as seguintes iptablesregras:

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

Ao conectar-se server:2525diretamente, o contêiner Docker é capaz de ver meu endereço IP real ( 1.2.3.4). Ao se conectar ao port server:25, o contêiner do Docker vê o IP local fornecido 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

Como posso ter certeza de que o contêiner do Docker está vendo corretamente o endereço IP público ao conectar-se à porta 25 (e não apenas ao conectar-se à porta 2525).

Obrigado

# 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

Responder1

Deixe o Docker cuidar do redirecionamento, que é dinâmico e pode mudar quando os contêineres são adicionados, removidos ou reiniciados. Mas vejaATUALIZARabaixo.

Esse redirecionamento não deve ser para 172.18.0.1, que é o host e não o contêiner. Quando o host recebe tal conexão, ela é tratada pordocker-proxyque faz proxy para o contêiner, perdendo o endereço IP de origem no processo.

O Docker já DNAT+ roteia essa porta corretamente (exceto do próprio host, onde docker-proxydesempenha essa função) na última regra do conjunto de regras, para o contêiner em execução com o endereço 172.18.0.2. Exceto que está configurado para usar a porta 2525 em vez da porta 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

Isso deve ser corrigido com as configurações do Docker, não manualmentetabelas de ipregras que não podem ser alteradas sempre que o layout do contêiner for alterado. Como a porta 25 é privilegiada, se o Docker estiver rodando sem root, serão necessárias configurações adicionais, verifique odocumentação sobre a exposição de portas privilegiadas.


ATUALIZAR(considerando os comentários do OP): O OP não pode ser usado no momento -p 25:25porque docker-proxyentra em conflito com o servidor SMTP do host local e compete pela escuta na porta 25 do host. Essa é a razão pela qual a inicial (errada)tabelas de ipo redirecionamento foi feito pelo OP.

Pode-se:

  • desabilitar globalmente docker-proxyexecutando dockerdcom a propriedade userland-proxydefinida como false

    seja como umparâmetro --userland-proxy=falseou como uma propriedade "userland-proxy": falseadicionada a/etc/docker/daemon.json.

    Isso permitirá então usar docker run ... -p 25:25 ...(comodocumentado) sem conflito: o host alcançará a si mesmo ao alcançar localhost ou $ HOSTNAME, os sistemas remotos alcançarão o contêiner e nenhum "Endereço já em uso" fará com que o daemon SMTP do host ou o contêiner do Docker falhem na inicialização.

  • ou então adicione um redirecionamento manual (com configuração demorada abaixo para fazer isso quase automaticamente)

    Sempre que o contêiner é reiniciado, existe o risco de seu endereço IP (interno) ser alterado. Então isso deve ser computado. Portanto, com um contêiner nomeado mxusando uma rede nomeada mxe um único endereço IP envolvido, isso pode ser feito conforme explicado abaixo.

    Crie uma cadeia de pré-roteamento separada (para que possa ser liberada sem a necessidade de liberar mais nada) e chame-a primeiro:

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

    O endereço IP do contêiner pode ser recuperado programaticamente (para o caso simples de um contêiner nomeado mxcom um único endereço):

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

    (ou alguém poderia usarjq:)containerip=$(docker container inspect mx | jq '.[].NetworkSettings.IPAddress'

    Encontrar o nome da interface da ponte é mais complicado, ou pelo menos não consegui encontrar uma maneira de fazer isso usando apenas docker ... inspect. Portanto, encontre seu endereço IP no host e consulte o host usando ip addresspara encontrar apenas a interface da ponte onde esse endereço IP específico está definido (requer o jqcomando).

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

    Limpe e preencha novamente mynatcada vez que o contêiner for (re)iniciado:

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

    o que seria para o caso atual:

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

    E para ter certeza de que as próprias regras de firewall do Docker não bloqueiam esse tráfego, faça algo semelhante começando em filter/ FORWARDdoDOCKER-USERcorrente.

    Inicialmente (se for da inicialização, talvez você precise criar primeiroDOCKER-USER):

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

    Mais tarde, cada vez que o contêiner for (re)iniciado:

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

    o que seria para o caso atual:

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

Notas:

  • Para simplificar as regras acima e evitar alguns cálculos, o contêiner e sua rede bridge podem ser iniciados com endereços IP fixos. Veja, por exemplo, este SO Q/A:Atribuir IP estático ao contêiner Docker.

  • Aqui está também uma pergunta/resposta da UL SE com uma resposta minha sobre problemas ao interagir com o Docker (ele é voltado paranftáveismas algumas partes sobre as interações DOCKER-USERem cadeia ou br_netfilterponte ainda são de interesse):janela de encaixe da lista de permissões do nftables

informação relacionada