Eu tenho um host Docker com dois endereços IP (chame-os de A
e B
). O Apache está vinculado a A
, que resolve para web.mydomain.com
, enquanto o Postfix está vinculado a B
on mail.mydomain.com
.
O aplicativo Docker foi projetado para enviar mensagens por SMTP-AUTH para mail.mydomain.com
. Ele também possui uma interface da web que escuta localhost:3000
(mostrada por docker ps
como 127.0.0.1:3000->3000/tcp
). Isso é enviado por proxy via Apache para o mundo externo no web.mydomain.com
.
O contêiner parece ser capaz de resolver as solicitações DNS corretas do host para A
(.59) e B
(.83):
nextjs@5b3fccbf323a:/app$ getent hosts 182.xx.xx.59
182.xx.xx.59 mail.mydomain.com
nextjs@5b3fccbf323a:/app$ getent hosts 182.xx.xx.83
182.xx.xx.83 www.mydomain.com
Mas quando tento me conectar à porta de correio (de dentro do contêiner), nada acontece até o tempo limite:
openssl s_client -connect mail.mydomain.com:587 -starttls smtp
Conectar-se a partir do host está bem. Outros contêineres Docker executando outros aplicativos podem enviar e-mails da mesma maneira, sem problemas.
Conheço muito pouco sobre redes Docker. A conexão na porta 587 do contêiner parece ir para o IP da web A
, mas não tenho certeza. A UI da web A
funciona normalmente. Como posso persuadir o contêiner a se conectar à interface A
da Web e à interface B
para envio SMTP?
tcpdump mostra pacotes na porta 587 vindos do contêiner, mas não voltando do servidor de e-mail:
14:30:53.168711 br-36bedc370406 In ifindex 102 02:42:ac:1f:00:02 ethertype IPv4 (0x0800), length 80: 172.31.0.2.55954 > 185.x.x.59.587: Flags [S], seq 1208255870, win 64240, options [mss 1460,sackOK,TS val 241634928 ecr 0,nop,wscale 7], length 0
iptables-save
(regras fail2ban omitidas):
# Generated by iptables-save v1.8.7 on Tue Jun 25 14:37:41 2024
*filter
:INPUT DROP [162608:8659146]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [31071143:38820437864]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
:LOGGING - [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i eth0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i docker0 -p tcp -j ACCEPT
-A INPUT -p tcp -m tcp --dport 21 -m conntrack --ctstate NEW,ESTABLISHED -m geoip --source-country GB,UG,KE,ZA -j ACCEPT
-A INPUT -p tcp -m tcp --dport 20 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --sport 1024:65535 --dport 1024:65535 -m conntrack --ctstate ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m multiport --dports 12000:13000 -j ACCEPT
-A INPUT -p tcp -m multiport --dports 12000:13000 -j ACCEPT
-A INPUT -i eth0 -p tcp -m multiport --dports 25,110,143,465,587,993,995,2525,2526 -j ACCEPT
-A INPUT -i eth0 -p tcp -m multiport --dports 80,443 -j ACCEPT
-A INPUT -i eth0 -p tcp -m multiport --dports 22,873 -m geoip --source-country GB -j ACCEPT
-A INPUT -i eth0 -p tcp -m multiport --dports 8080,3100 -m geoip --source-country GB -j ACCEPT
-A INPUT -i eth0 -p icmp -j ACCEPT
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A FORWARD -o br-36bedc370406 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o br-36bedc370406 -j DOCKER
-A FORWARD -i br-36bedc370406 ! -o br-36bedc370406 -j ACCEPT
-A FORWARD -i br-36bedc370406 -o br-36bedc370406 -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -o docker0 -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 443 -m owner ! --uid-owner 117 -j LOGGING
-A OUTPUT -p tcp -m tcp --dport 443 -m owner ! --uid-owner 117 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -p tcp -m tcp --dport 563 -m owner ! --uid-owner 117 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -p tcp -m tcp --dport 21 -m conntrack --ctstate NEW -j REJECT --reject-with icmp-port-unreachable
-A DOCKER -d 172.31.0.2/32 ! -i br-36bedc370406 -o br-36bedc370406 -p tcp -m tcp --dport 3000 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -i br-36bedc370406 ! -o br-36bedc370406 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-36bedc370406 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN
-A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: "
COMMIT
# Completed on Tue Jun 25 14:37:41 2024
# Generated by iptables-save v1.8.7 on Tue Jun 25 14:37:41 2024
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -p tcp -m tcp --dport 80 -m owner ! --uid-owner 117 -j REDIRECT --to-ports 8888
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.31.0.0/16 ! -o br-36bedc370406 -j MASQUERADE
-A POSTROUTING -s 172.31.0.2/32 -d 172.31.0.2/32 -p tcp -m tcp --dport 3000 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER -i br-36bedc370406 -j RETURN
-A DOCKER -d 127.0.0.1/32 ! -i br-36bedc370406 -p tcp -m tcp --dport 3000 -j DNAT --to-destination 172.31.0.2:3000
COMMIT
nft list ruleset
mostra:
table ip filter {
chain INPUT {
type filter hook input priority filter; policy drop;
meta l4proto tcp tcp dport { 80,443} counter packets 1642563 bytes 190179301 jump f2b-apache-fakegooglebot
meta l4proto tcp tcp dport { 80,443} counter packets 9116728 bytes 1069624630 jump f2b-apache-auth
meta l4proto tcp tcp dport 22 counter packets 413620 bytes 31257328 jump f2b-sshd
meta l4proto tcp tcp dport { 110,995,143,993,587,465,4190} counter packets 6304246 bytes 659133161 jump f2b-dovecot
meta l4proto tcp tcp dport { 25,465,587} counter packets 1358308 bytes 1357726201 jump f2b-postfix
meta l4proto tcp tcp dport { 80,443,25,587,110,995,143,993,4190} counter packets 15403400 bytes 2939709444 jump f2b-postfix
iifname "lo" counter packets 45927262 bytes 22275943601 accept
ct state related,established counter packets 23416790 bytes 5815553538 accept
iifname "eth0" meta l4proto tcp tcp dport 53 counter packets 5885 bytes 340579 accept
iifname "eth0" meta l4proto udp udp dport 53 counter packets 895484 bytes 69357764 accept
iifname "docker0" meta l4proto tcp counter packets 3001 bytes 180060 accept
meta l4proto tcp tcp dport 21 ct state new,established # -m geoip --source-country GB,UG,KE,ZA counter packets 49 bytes 2480 accept
meta l4proto tcp tcp dport 20 ct state related,established counter packets 0 bytes 0 accept
meta l4proto tcp tcp sport 1024-65535 tcp dport 1024-65535 ct state established counter packets 0 bytes 0 accept
meta l4proto tcp tcp dport 12000-13000 counter packets 2973 bytes 135532 accept
meta l4proto tcp tcp dport 12000-13000 counter packets 0 bytes 0 accept
iifname "eth0" meta l4proto tcp tcp dport { 25,110,143,465,587,993,995,2525,2526} counter packets 197174 bytes 11469412 accept
iifname "eth0" meta l4proto tcp tcp dport { 80,443} counter packets 787414 bytes 46406299 accept
iifname "eth0" meta l4proto tcp tcp dport { 22,873} # -m geoip --source-country GB counter packets 275 bytes 15232 accept
iifname "eth0" meta l4proto tcp tcp dport { 8080,3100} # -m geoip --source-country GB counter packets 143 bytes 6132 accept
iifname "eth0" meta l4proto icmp counter packets 29321 bytes 1823925 accept
counter packets 205049 bytes 10393170 jump SpamhausIN
}
chain LOGGING {
limit rate 2/minute counter packets 24372 bytes 1259527 log prefix "IPTables-Dropped: "
}
chain FORWARD {
type filter hook forward priority filter; policy drop;
counter packets 91 bytes 19822 jump DOCKER-USER
counter packets 91 bytes 19822 jump DOCKER-ISOLATION-STAGE-1
oifname "docker0" ct state related,established counter packets 6 bytes 636 accept
oifname "docker0" counter packets 0 bytes 0 jump DOCKER
iifname "docker0" oifname != "docker0" counter packets 6 bytes 390 accept
iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
oifname "br-36bedc370406" ct state related,established counter packets 217 bytes 55250 accept
oifname "br-36bedc370406" counter packets 6 bytes 360 jump DOCKER
iifname "br-36bedc370406" oifname != "br-36bedc370406" counter packets 0 bytes 0 accept
iifname "br-36bedc370406" oifname "br-36bedc370406" counter packets 6 bytes 360 accept
counter packets 0 bytes 0 jump SpamhausIN
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
oifname "lo" counter packets 45927236 bytes 22275941960 accept
oifname "docker0" counter packets 53070 bytes 13526184 accept
meta l4proto tcp tcp dport 443 skuid != 117 counter packets 137554 bytes 6981510 jump LOGGING
meta l4proto tcp tcp dport 443 skuid != 117 counter packets 137554 bytes 6981510 reject
meta l4proto tcp tcp dport 563 skuid != 117 counter packets 6 bytes 312 reject
meta l4proto tcp tcp dport 21 ct state new counter packets 0 bytes 0 reject
counter packets 32097922 bytes 38886685465 jump SpamhausOUT
}
chain f2b-dovecot {
counter packets 128626 bytes 13891673 return
}
chain DOCKER {
}
chain DOCKER-ISOLATION-STAGE-1 {
iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
iifname "br-36bedc370406" oifname != "br-36bedc370406" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
counter packets 0 bytes 0 return
}
chain DOCKER-ISOLATION-STAGE-2 {
oifname "docker0" counter packets 0 bytes 0 drop
oifname "br-36bedc370406" counter packets 0 bytes 0 drop
counter packets 0 bytes 0 return
}
chain DOCKER-USER {
counter packets 0 bytes 0 return
}
}
table ip nat {
chain OUTPUT {
type nat hook output priority -100; policy accept;
meta l4proto tcp tcp dport 80 skuid != 117 counter packets 2 bytes 120 redirect to :8888
ip daddr != 127.0.0.0/8 fib daddr type local counter packets 8 bytes 480 jump DOCKER
}
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 3 bytes 195 masquerade
oifname != "br-36bedc370406" ip saddr 172.31.0.0/16 counter packets 0 bytes 0 masquerade
meta l4proto tcp ip saddr 172.31.0.2 ip daddr 172.31.0.2 tcp dport 3000 counter packets 0 bytes 0 masquerade
}
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
fib daddr type local counter packets 2221 bytes 148479 jump DOCKER
}
chain DOCKER {
iifname "docker0" counter packets 3 bytes 180 return
iifname "br-36bedc370406" counter packets 7 bytes 420 return
iifname != "br-36bedc370406" meta l4proto tcp ip daddr 127.0.0.1 tcp dport 3000 counter packets 0 bytes 0 dnat to 172.31.0.2:3000
}
}
table ip6 nat {
chain OUTPUT {
type nat hook output priority -100; policy accept;
meta l4proto tcp tcp dport 80 skuid != 117 counter packets 35400 bytes 2832000 redirect to :8888
}
chain DOCKER {
}
}
Saída de host:~$ ip route
( ip
não está no contêiner):
default via 185.x.x.1 dev eth0 proto static onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.31.0.0/16 dev br-36bedc370406 proto kernel scope link src 172.31.0.1
185.x.x.0/22 dev eth0 proto kernel scope link src 185.x.x.83
~$ ip a
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 mq state UP group default qlen 1000
link/ether 02:00:00:00:40:17 brd ff:ff:ff:ff:ff:ff
inet 185.x.x.83/22 brd 185.x.x.255 scope global eth0
valid_lft forever preferred_lft forever
inet 185.x.x.59/22 brd 185.x.x.255 scope global secondary eth0
valid_lft forever preferred_lft forever
inet6 2001:xxx:x:xxx::3b/64 scope global
valid_lft forever preferred_lft forever
inet6 2001:xxx:x:xxx::53/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::ff:fe00:4017/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:91:dc:5d:95 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:91ff:fedc:5d95/64 scope link
valid_lft forever preferred_lft forever
10: vethf05d58a@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 6a:7e:2f:5d:df:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::687e:2fff:fe5d:dff3/64 scope link
valid_lft forever preferred_lft forever
102: br-36bedc370406: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:da:79:75:ae brd ff:ff:ff:ff:ff:ff
inet 172.31.0.1/16 brd 172.31.255.255 scope global br-36bedc370406
valid_lft forever preferred_lft forever
inet6 fe80::42:daff:fe79:75ae/64 scope link
valid_lft forever preferred_lft forever
104: vetheec212b@if103: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-36bedc370406 state UP group default
link/ether 1e:4a:34:89:98:cf brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::1c4a:34ff:fe89:98cf/64 scope link
valid_lft forever preferred_lft forever
110: veth216a525@if109: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-36bedc370406 state UP group default
link/ether 4e:15:61:92:30:a0 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::4c15:61ff:fe92:30a0/64 scope link
valid_lft forever preferred_lft forever
host:~$ docker network ls
mostra isso:
NETWORK ID NAME DRIVER SCOPE
1f2cb04b3b54 bridge bridge local
c9ea0692db1b host host local
a91ee892376d none null local
36bedc370406 rallly_default bridge local
A rede utilizada pelo container em questão é chamada de "rallly_default".
host:~$ docker network inspect rallly_default
{
"Name": "rallly_default",
"Id": "36bedc3704069d8d2ad6cc02031296a5fadb95ca988ee1a0e9d461b5e6ba083b",
"Created": "2024-06-24T20:59:40.356497842Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.31.0.0/16",
"Gateway": "172.31.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"4c41cdd942d82c1504804fbf94e3980ef96adaabbec43ae36ac8ec21c255d970": {
"Name": "rallly-rallly_db-1",
"EndpointID": "1f7352607a1062ddb6ae421b490bb12739a099476137396737a1a60b752e67d0",
"MacAddress": "02:42:ac:1f:00:02",
"IPv4Address": "172.31.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "rallly",
"com.docker.compose.version": "2.27.1"
}
}
]
Como você pode ver, sua
filter/INPUT
política éDROP
, e a porta tcp 587 só é aberta para tráfegos deeth0
(enquanto no caso de preocupação, a solicitação vem debr-36bedc370406
). Ao mesmo tempo, todos os tráfegos da ponte docker padrãodocker0
são aceitos (o que pode explicar o caso doother containers
, assumindo que eles não sejam iniciados com o docker compose).Aparentemente, você está mantendo um firewall
iptables
(ou algum frontend que usa oiptables
programa), embora seja uma variante que realmente traduz regras/comandos em regras nftables. Portanto, você pode continuar fazendo isso (e ignorar as saídas denft
/ apenas considere usar iptables).Não tenho certeza de qual é a melhor prática aqui. Você pode adicionar uma regra que abra a porta tcp 587 para todas as interfaces (ou seja, nenhum
-i
teste na regra) ou talvez ver se consegue encontrar uma maneira de especificar um nome de ponte fixo no arquivo de composição. (IIRC por padrão, o nome da ponte mudará se você executar um ciclo de composição down/up, mas eu realmente não sei/lembro. Da mesma forma, a sub-rede IP privada pode não ser necessariamente o mesmo bloco todas as vezes, mas é claro que isso é um pouco mais pseudo-determinístico.) Em última análise, depende de que tipo de filtragem/"proteção" você realmente precisa.