Solución 1:
Después de pasar un par de días mirando registros y configuraciones para los componentes involucrados, estaba a punto de tirar la toalla y volver a Fedora 30, donde parece funcionar de inmediato.
Centrándome en el cortafuegos, me di cuenta de que deshabilitar firewalld
parecía hacer el truco, pero preferiría no hacerlo. Al inspeccionar las reglas de la red con iptables
, me di cuenta de que el cambio a nftables
significa que iptables
ahora es una capa de abstracción que solo muestra una pequeña parte del nftables
normas. Eso significa que la mayoría, si no todos, de los firewalld
la configuración se aplicará fuera del alcance de iptables
.
Estaba acostumbrado a poder encontrar toda la verdad en iptables
, por lo que tomará un tiempo acostumbrarse.
Para resumir, para que esto funcione, tuve que habilitar el enmascaramiento. Parecía dockerd
ya lo hice a través de iptables
, pero aparentemente esto debe habilitarse específicamente para la zona de firewall para iptables
disfrazarse para trabajar:
# Masquerading allows for docker ingress and egress (this is the juicy bit)
firewall-cmd --zone=public --add-masquerade --permanent
# Specifically allow incoming traffic on port 80/443 (nothing new here)
firewall-cmd --zone=public --add-port=80/tcp
firewall-cmd --zone=public --add-port=443/tcp
# Reload firewall to apply permanent rules
firewall-cmd --reload
Reiniciar o reiniciar dockerd
, y tanto la entrada como la salida deberían funcionar.
Solución 2:
Lo que falta en las respuestas anteriores es el hecho de que primero debe agregar su interfaz acoplable a la zona que configura, p. público (o agréguelo a la zona "confiable" que se sugirió aquí, pero dudo que sea prudente, desde una perspectiva de seguridad). Porque por defecto no está asignado a una zona. También recuerde recargar el demonio docker cuando haya terminado.
# Check what interface docker is using, e.g. 'docker0'
ip link show
# Check available firewalld zones, e.g. 'public'
sudo firewall-cmd --get-active-zones
# Check what zone the docker interface it bound to, most likely 'no zone' yet
sudo firewall-cmd --get-zone-of-interface=docker0
# So add the 'docker0' interface to the 'public' zone. Changes will be visible only after firewalld reload
sudo nmcli connection modify docker0 connection.zone public
# Masquerading allows for docker ingress and egress (this is the juicy bit)
sudo firewall-cmd --zone=public --add-masquerade --permanent
# Optional open required incomming ports (wasn't required in my tests)
# sudo firewall-cmd --zone=public --add-port=443/tcp
# Reload firewalld
sudo firewall-cmd --reload
# Reload dockerd
sudo systemctl restart docker
# Test ping and DNS works:
docker run busybox ping -c 1 172.16.0.1
docker run busybox cat /etc/resolv.conf
docker run busybox ping -c 1 yourhost.local
Solución 3:
Para poder establecer reglas detalladas para Docker, no necesitaba configurar docker0 en ninguna zona.
# 1. Stop Docker
systemctl stop docker
# 2. Recreate DOCKER-USER chain in firewalld.
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
# (Ignore any warnings)
# 3. Docker Container <-> Container communication
firewall-cmd --permanent \
--direct \
--add-rule ipv4 filter DOCKER-USER 1 \
-m conntrack --ctstate RELATED,ESTABLISHED \
-j ACCEPT \
-m comment \
--comment 'Allow docker containers to connect to the outside world'
firewall-cmd --permanent \
--direct \
--add-rule ipv4 filter DOCKER-USER 1 \
-j RETURN \
-s 172.17.0.0/16 \
-m comment \
--comment 'allow internal docker communication'
# Change the Docker Subnet to your actual one (e.g. 172.18.0.0/16)
# 4. Add rules for IPs allowed to access the Docker exposed ports.
firewall-cmd --permanent \
--direct \
--add-rule ipv4 filter DOCKER-USER 1 \
-o docker0 \
-p tcp \
-m multiport \
--dports 80,443 \
-i eth0 \
-o docker0 \
-s 1.2.3.4/32 \
-j ACCEPT \
-m comment \
--comment 'Allow IP 1.2.3.4 to docker ports 80 and 443'
# 5. log docker traffic (if you like)
firewall-cmd --direct \
--add-rule ipv4 filter DOCKER-USER 0 \
-j LOG \
--log-prefix ' DOCKER: '
# 6. Block all other IPs.
This rule has lowest precedence, so you can add allowed IP rules later.
firewall-cmd --permanent \
--direct \
--add-rule ipv4 filter DOCKER-USER 10 \
-j REJECT \
-m comment \
--comment 'reject all other traffic to DOCKER-USER'
# 7. Reload firewalld, Start Docker again
firewall-cmd --reload
systemctl start docker
Esto termina en reglas definidas en /etc/firewalld/direct.xml:
<?xml version="1.0" encoding="utf-8"?>
<direct>
<chain ipv="ipv4" table="filter" chain="DOCKER-USER"/>
<rule ipv="ipv4" table="filter" chain="DOCKER-USER" priority="0">-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'Allow docker containers to connect to the outside world'</rule>
<rule ipv="ipv4" table="filter" chain="DOCKER-USER" priority="0">-j RETURN -s 172.17.0.0/16 -m comment --comment 'allow internal docker communication'</rule>
<rule ipv="ipv4" table="filter" chain="DOCKER-USER" priority="0">-p tcp -m multiport --dports 80,443 -s 1.2.3.4/32 -j ACCEPT -m comment --comment 'Allow IP 1.2.3.4 to docker ports 80 and 443'</rule>
<rule ipv="ipv4" table="filter" chain="DOCKER-USER" priority="0">-j LOG --log-prefix ' DOCKER TCP: '</rule>
<rule ipv="ipv4" table="filter" chain="DOCKER-USER" priority="10">-j REJECT -m comment --comment 'reject all other traffic to DOCKER-USER'</rule>
</direct>
El inconveniente aún es que necesita instalar containerd.io desde CentOS7 como lo indica Saustrup