Por que o firewalld permite tráfego público para minhas portas não públicas, vinculado a contêineres Docker?

Por que o firewalld permite tráfego público para minhas portas não públicas, vinculado a contêineres Docker?

Estou tentando implementar um firewall bem simples no Fedora, onde a internet pública possa acessar SSH, HTTP, HTTPS e Cockpit, mas nada mais. Enquanto isso, os servidores executam microsserviços via Docker que podem se comunicar entre si nas portas 8000-8999.

diagrama de rede

Eu configurei isso em uma nova instalação do Fedora Server com os seguintes comandos:

firewall-cmd --zone=public --add-service=cockpit
firewall-cmd --zone=public --add-service=http
firewall-cmd --zone=public --add-service=https

firewall-cmd --zone=internal --add-source=192.168.1.65
firewall-cmd --zone=internal --add-source=192.168.1.66

firewall-cmd --zone=internal --add-port=8000-8999/tcp

firewall-cmd --runtime-to-permanent

Quando verifico minha configuração com --list-all, tudo parece correto:

> firewall-cmd --list-all --zone=internal
internal (active)
  target: default
  icmp-block-inversion: no
  interfaces:
  sources: 192.168.1.65 192.168.1.66
  services: dhcpv6-client ssh
  ports: 8000-8999/tcp
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

> firewall-cmd --list-all --zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp2s0
  sources:
  services: cockpit dhcpv6-client http https ssh
  ports:
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

No entanto, quando testo isso, consigo acertarhttp://192.168.1.65:8080. Posso acessá-lo de uma máquina na mesma rede interna (192.168.128.128), bem como de uma solicitação pública de uma máquina externa. Como nenhum internaldos dois estava listado em sources, presumi que o firewalld não permitiria a passagem das solicitações.

Tenho uma zona autoconfigurada dockercom a docker0interface, mas removê-la não parece alterar minha capacidade de acessar a porta interna.

Por que consigo solicitar :8080 com êxito de fontes que não estão listadas em internal?

Responder1

Bem, acontece que o problema decorre do fato de estar usando o Docker para minhas portas internas. Para simplificar o processo de fazer com que os contêineres se comuniquem com o mundo e entre si, o Docker optou por assumir um grande controle sobre seus firewalls/rede. Isso significa que se vocênãodeseja que seus contêineres sejam acessíveis publicamenteEvocê precisa controlar o acesso público através do firewall na mesma máquina que seu daemon Docker, você precisa configurar seus firewalls de maneira um pouco diferente.Docker tem alguma documentação oficial sobre como fazer isso. Basicamente, você tem as seguintes opções:

  • Configure uma máquina separada apenas para o seu firewall. Provavelmente seria o mais fácil, já que o Docker e seu firewall não precisariam compartilhar recursos.
  • Adicione suas iptablesregras à DOCKER-USERcadeia (isso é mais uma resposta para iptablesos usuários; não tenho certeza de como replicar firewalldessa abordagem)
  • Desative tudo configurando iptables=falsea configuração do serviço Docker. (esta postagem do blogdiscute esta opção)

eu tambémencontrei uma postagemque achei uma boa variação da DOCKER-USERopção de corrente. Basicamente, você cria um iptables.confarquivo que pode ser carregado sem liberação usando iptables-restore -n. Infelizmente, é uma iptablessolução, não uma firewalldsolução.

Uma das partes difíceis de diagnosticar esse problema foi executar um script para modificar o firewall para corresponder ao que eu queria, e isso funcionaria até que eu reiniciasse a máquina ou iniciasse o daemon do Docker. O Docker substitui a iptablesconfiguração quando é inicializado.

Responder2

Consegui resolver esse problema recriando a DOCKER-USERcadeia e gostaria de compartilhá-la caso alguém ache útil.

Isso é baseado em uma solução que encontrei no Github (https://github.com/firewalld/firewalld/issues/869) e no blog de algum gênio (https://roosbertl.blogspot.com/2019/06/securing-docker-ports-with-firewalld.html) mas tive que adaptar/refinar um pouco.

# 1. Stop Docker
systemctl stop docker.socket
systemctl stop docker.service

# 2. Recreate DOCKER-USER iptables chain with firewalld. Ignore warnings, do not ignore errors
firewall-cmd --permanent --direct --remove-chain ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER

# 3. Add iptables rules to DOCKER-USER chain - unrestricted outbound, restricted inbound to private IPs
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'Allow containers to connect to the outside world'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 127.0.0.0/8 -m comment --comment 'allow internal docker communication, loopback addresses'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 172.16.0.0/12 -m comment --comment 'allow internal docker communication, private range'

# 3.1 optional: for wider internal networks
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 10.0.0.0/8 -m comment --comment 'allow internal docker communication, private range'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 192.168.0.0/16 -m comment --comment 'allow internal docker communication, private range'

# 4. Block all other IPs. This rule has lowest precedence, so you can add rules before this one later.
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 10 -j REJECT -m comment --comment 'reject all other traffic to DOCKER-USER'

# 5. Activate rules
firewall-cmd --reload

# 6. Start Docker
systemctl start docker.socket
systemctl start docker.service

informação relacionada