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.
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 docker
zona configurada automáticamente con la docker0
interfaz, 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
iptables
reglas a laDOCKER-USER
cadena (esto es más bien una respuesta paraiptables
los usuarios; no estoy seguro de cómofirewalld
replicar este enfoque) - Deshabilite todo configurándolo
iptables=false
en 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-USER
opción de cadena. Básicamente, creas un iptables.conf
archivo que puedes cargar sin necesidad de vaciar usando iptables-restore -n
. Desafortunadamente, es una iptables
solución, no una firewalld
solució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 iptables
configuración cuando se inicia.
Respuesta2
Logré resolver este problema recreando la DOCKER-USER
cadena 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