Pasos para limitar las conexiones externas al contenedor docker con iptables?

Mi objetivo es limitar el acceso a los contenedores docker a unas pocas direcciones IP públicas. ¿Existe un proceso simple y repetible para lograr mi objetivo? Comprender solo los conceptos básicos de iptables mientras uso las opciones predeterminadas de Docker, me resulta muy difícil.

Me gustaría ejecutar un contenedor, hacerlo visible para la Internet pública, pero solo permitir conexiones desde hosts seleccionados. Esperaría establecer una política de ENTRADA predeterminada de RECHAZO y luego solo permitir conexiones desde mis hosts. Pero las reglas y cadenas NAT de Docker se interponen en el camino y mis reglas de ENTRADA se ignoran.

¿Puede alguien proporcionar un ejemplo de cómo lograr mi objetivo dadas las siguientes suposiciones?

  • Host IP pública 80.80.80.80 en eth0
  • Host IP privada 192.168.1.10 en eth1
  • docker run -d -p 3306:3306 mysql
  • Bloquear todas las conexiones al host / contenedor 3306 excepto de los hosts 4.4.4.4 y 8.8.8.8

Me complace vincular el contenedor solo a la dirección IP local, pero necesitaría instrucciones sobre cómo configurar correctamente las reglas de reenvío de iptables que sobreviven al proceso de docker y los reinicios del host.

¡Gracias!

Dos cosas a tener en cuenta al trabajar con las reglas de firewall de docker:

  1. Para evitar que sus reglas sean golpeadas por docker, use el DOCKER-USER cadena
  2. Docker hace el mapeo de puertos en el PREROUTING la cadena de la nat tabla. Esto sucede antes de la filter reglas, entonces --dest y --dport verá la IP interna y el puerto del contenedor. Para acceder al destino original, puede usar -m conntrack --ctorigdstport.

Por ejemplo:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPTiptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPTiptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

NOTA: Sin --ctdir ORIGINAL, esto también coincidiría con los paquetes de respuesta que regresan para una conexión desde el contenedor al puerto 3306 en algún otro servidor, que es casi seguro que no es lo que desea. No lo necesitas estrictamente si, como yo, tu primera regla es -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, ya que eso se ocupará de todos los paquetes de respuesta, pero sería más seguro seguir usando --ctdir ORIGINAL Por cierto.

Con Docker v. 17.06 hay una nueva cadena de iptables llamada DOCKER-USER. Este es para sus reglas personalizadas: https://docs.docker.com/network/iptables/

A diferencia de la ventana acoplable en cadena, no se restablece al crear / iniciar contenedores. Por lo tanto, puede agregar estas líneas a su configuración/script de iptables para aprovisionar el servidor incluso antes de instalar docker e iniciar los contenedores:

-N DOCKER-N DOCKER-ISOLATION-N DOCKER-USER-A DOCKER-ISOLATION -j RETURN-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP-A DOCKER-USER -j RETURN

Ahora el puerto para MySQL está bloqueado para el acceso externo (eth0) incluso si docker abre el puerto para el mundo. (Estas reglas asumen que su interfaz externa es eth0.)

Eventualmente, tendrás que limpiar ip y reiniciar el servicio docker primero, si lo arruinaste demasiado tratando de bloquear el puerto como lo hice yo.

ACTUALIZAR: Si bien esta respuesta sigue siendo válida, la respuesta de @SystemParadox usa DOCKER-USER en combinación con --ctorigdstport es mejor.

Aquí hay una solución que persiste bien entre reinicios y le permite afectar el expuesto puerto en lugar de la interno portuario.

iptables -t mangle -N DOCKER-mysqliptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURNiptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURNiptables -t mangle -A DOCKER-mysql -j DROPiptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql

He creado una imagen de Docker que usa este método para administrar automáticamente las iptables por usted, usando variables de entorno o dinámicamente con etcd (o ambos):

https://hub.docker.com/r/colinmollenhour/confd-firewall/

ACTUALIZAR: Si bien es válida en 2015, esta solución ya no es la forma correcta de hacerlo.

La respuesta parece estar en la documentación de Docker en https://docs.docker.com/articles/networking/#the-world

Las reglas de reenvío de Docker permiten todas las direcciones IP de origen externas de forma predeterminada. Para permitir que solo una IP o red específica acceda a los contenedores, inserte una regla anulada en la parte superior de la cadena de filtros de DOCKER. Por ejemplo, para restringir el acceso externo de modo que solo la IP de origen 8.8.8.8 pueda acceder a los contenedores, se podría agregar la siguiente regla: iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

Lo que terminé haciendo fue:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPTiptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPTiptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

No toqué el --iptables o --icc opcion.