¿Por qué firewalld permite el tráfico público a mis puertos no públicos, vinculados a contenedores Docker?

¿Por qué firewalld permite el tráfico público a mis puertos no públicos, vinculados a contenedores Docker?

Estoy intentando implementar un firewall bastante simple en Fedora, donde la Internet pública puede acceder a SSH, HTTP, HTTPS y Cockpit, pero nada más. Mientras tanto, los servidores ejecutan microservicios a través de Docker que pueden comunicarse entre sí en los puertos 8000-8999.

diagrama de Red

Configuré esto en una nueva instalación del servidor Fedora con los siguientes 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

Cuando reviso mi configuración con --list-all, todo parece correcto:

> 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:

Sin embargo, cuando pruebo esto, puedo presionarhttp://192.168.1.65:8080. Puedo acceder desde una máquina en la misma red interna (192.168.128.128), así como una solicitud pública desde una máquina externa. Como ninguno de los dos figuraba en internal's sources, supuse que firewalld no permitiría el paso de las solicitudes.

Tengo una dockerzona configurada automáticamente con la docker0interfaz, pero eliminarla no parece cambiar mi capacidad para acceder al puerto interno.

¿Por qué puedo solicitar con éxito :8080 de fuentes que no figuran en internal?

Respuesta1

Bueno, resulta que el problema surge del hecho de que estoy usando Docker para mis puertos internos. Para simplificar el proceso de lograr que los contenedores se comuniquen con el mundo y entre sí, Docker tomó la decisión de tomar una gran cantidad de control sobre sus firewalls/redes. Esto significa que si ustednoquiere que sus contenedores sean de acceso públicoYnecesita controlar el acceso público a través del firewall en la misma máquina que su demonio Docker, necesita configurar sus firewalls de manera ligeramente diferente.Docker tiene documentación oficial sobre cómo hacer esto.. Básicamente, tienes las siguientes opciones:

  • Configure una máquina separada solo para su firewall. Probablemente esto sería lo más fácil, ya que Docker y su firewall no tendrían que compartir recursos.
  • Agregue sus iptablesreglas a la DOCKER-USERcadena (esto es más bien una respuesta para iptableslos usuarios; no estoy seguro de cómo firewalldreplicar este enfoque)
  • Deshabilite todo configurándolo iptables=falseen la configuración de su servicio Docker. (esta publicación de bloganaliza esta opción)

Yo tambiénencontré una publicaciónque pensé que era una buena variación de la DOCKER-USERopción de cadena. Básicamente, creas un iptables.confarchivo que puedes cargar sin necesidad de vaciar usando iptables-restore -n. Desafortunadamente, es una iptablessolución, no una firewalldsolución.

Una de las partes difíciles de diagnosticar este problema fue que ejecutaría un script para modificar el firewall para que coincidiera con lo que quería, y eso funcionaría hasta que reiniciara la máquina o iniciara el demonio Docker. Docker sobrescribe la iptablesconfiguración cuando se inicia.

Respuesta2

Logré resolver este problema recreando la DOCKER-USERcadena y me gustaría compartirla en caso de que alguien la encuentre útil.

Esto se basa en una solución que encontré en Github (https://github.com/firewalld/firewalld/issues/869) y en el blog de algún genio (https://roosbertl.blogspot.com/2019/06/securing-docker-ports-with-firewalld.html) pero tuve que adaptarlo/refinarlo un poco.

# 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

información relacionada