Eu tenho um convidado CentOS 8 rodando em um host Fedora 31. O convidado está conectado a uma rede de ponte virbr0
, e tem endereço 192.168.122.217
. Eu posso fazer login no convidado via ssh nesse endereço.
Se eu iniciar um serviço no convidado escutando na porta 80, todas as conexões do host para o convidado falham assim:
$ curl 192.168.122.217
curl: (7) Failed to connect to 192.168.122.217 port 80: No route to host
O serviço está vinculado a 0.0.0.0
:
guest# ss -tln
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 5 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
Usando tcpdump
(no virbr0
host ou no eth0
convidado), vejo que o convidado parece estar respondendo com uma mensagem ICMP "administrador proibido".
19:09:25.698175 IP 192.168.122.1.33472 > 192.168.122.217.http: Flags [S], seq 959177236, win 64240, options [mss 1460,sackOK,TS val 3103862500 ecr 0,nop,wscale 7], length 0
19:09:25.698586 IP 192.168.122.217 > 192.168.122.1: ICMP host 192.168.122.217 unreachable - admin prohibited filter, length 68
Não há regras de firewall na INPUT
cadeia no convidado:
guest# iptables -S INPUT
-P INPUT ACCEPT
A tabela de roteamento no convidado parece perfeitamente normal:
guest# ip route
default via 192.168.122.1 dev eth0 proto dhcp metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.217 metric 100
O SELinux está no modo permissivo:
guest# getenforce
Permissive
Se eu parar sshd
e iniciar meu serviço na porta 22, tudo funcionará conforme o esperado.
O que está causando a falha dessas conexões?
Caso alguém solicite, a saÃda completa iptables-save
do convidado é:
*filter
:INPUT ACCEPT [327:69520]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [285:37235]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
-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 DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -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 -j RETURN
-A DOCKER-USER -j RETURN
COMMIT
*security
:INPUT ACCEPT [280:55468]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [285:37235]
COMMIT
*raw
:PREROUTING ACCEPT [348:73125]
:OUTPUT ACCEPT [285:37235]
COMMIT
*mangle
:PREROUTING ACCEPT [348:73125]
:INPUT ACCEPT [327:69520]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [285:37235]
:POSTROUTING ACCEPT [285:37235]
COMMIT
*nat
:PREROUTING ACCEPT [78:18257]
:INPUT ACCEPT [10:600]
:POSTROUTING ACCEPT [111:8182]
:OUTPUT ACCEPT [111:8182]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i docker0 -j RETURN
COMMIT
Bem, eu percebi. E é um doozy.
O CentOS 8 usa nftables , o que por si só não é surpreendente. Ele vem com a
nft
versão dosiptables
comandos, o que significa que quando você usa oiptables
comando ele mantém um conjunto de tabelas de compatibilidade em nftables.No entanto...
O Firewalld -- que é instalado por padrão -- tem suporte nativo para nftables, então não faz uso da camada de compatibilidade do iptables.
Então, enquanto
iptables -S INPUT
mostra a você:O que você realmente tem é:
A solução aqui (e honestamente provavelmente um bom conselho em geral) é:
Com o firewalld fora do caminho, as regras do iptables visÃveis com
iptables -S
se comportarão conforme o esperado.Caso você não pretenda desabilitar o firewalld, a solução é adicionar a regra nftables mencionada abaixo no host do hipervisor.
Primeiro adicione docker0 à zona interna se você não tiver usado outra especÃfica:
Execute nftables para aplicar a regra para aceitar o tráfego tcp na porta 80 para a interface virbr0 com destino 192.168.122.217:
/sbin/nft adicionar regra inet firewalld filter_FWDI_internal_allow oifname "virbr0" ip daddr 192.168.122.217 tcp dport 80 counter aceitar comentário "comentar algo"
Também é necessário adicionar a regra iptables (ou convertê-la em nftables para consistência, veja abaixo):
Essas duas regras você pode agrupar com script de shell e adicionar a unidade de serviço systemd com um atraso definido para 5 segundos. Para que ele seja executado automaticamente durante a inicialização.
Também é possÃvel converter a regra iptables para nftables e salvar ambas no conjunto de regras nftables. Mas uma vez que você executar a regra convertida do iptables, o iptables será bloqueado e quaisquer novas alterações no iptables serão possÃveis apenas com firewalld e nftables.
Além disso, precisa ter um kernel customizado de tuntables que faça com que os pacotes que atravessam a ponte não passem pelo iptables. Net.bridge.bridge-nf-call e sysctl.conf
Observe que, se você adicioná-lo ao sysctl.conf, ele pode não ser aplicado automaticamente durante a reinicialização, pois é um bug conhecido (dependendo da sua distribuição Linux).