Moby: Docker Network evita el firewall, no hay opción para deshabilitar

Creado en 14 abr. 2016  ·  114Comentarios  ·  Fuente: moby/moby

Salida de docker version :

Client:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 15:54:52 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 15:54:52 2016
 OS/Arch:      linux/amd64

Salida de docker info :

Containers: 14
 Running: 5
 Paused: 0
 Stopped: 9
Images: 152
Server Version: 1.10.3
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 204
 Dirperm1 Supported: false
Execution Driver: native-0.2
Logging Driver: json-file
Plugins: 
 Volume: local
 Network: bridge null host
Kernel Version: 3.13.0-58-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 7.793 GiB
Name: brm-pheonix-dev
ID: Y6Z4:6D53:RFOL:Z3CM:P7ZK:H6HL:RLV5:JT73:LZMC:DTBD:7ILK:2RS5
Username: benjamenmeyer
Registry: https://index.docker.io/v1/

Detalles adicionales del entorno (AWS, VirtualBox, físico, etc.):
Rackspace Cloud Server, Ubuntu 14.04, pero eso realmente no debería importar

Pasos para reproducir el problema:

  1. Configure el sistema con un firewall bloqueado
  2. Crea un conjunto de contenedores Docker con puertos expuestos
  3. Compruebe el cortafuegos; Docker utilizará "cualquier lugar" como fuente, por lo que todos los contenedores están expuestos al público.

Describe los resultados que recibiste:
root @ brm-pheonix-dev : ~ / rse # iptables --list DOCKER
Cadena DOCKER (1 referencias)
destino de origen opt opt ​​destino
ACEPTAR tcp - en cualquier lugar 172.17.0.2 tcp dpt: 6379

Describe los resultados que esperabas:
root @ brm-pheonix-dev : ~ / rse # iptables --list DOCKER
Cadena DOCKER (1 referencias)
destino de origen opt opt ​​destino
ACEPTAR tcp - 127.0.0.0/24 172.17.0.2 tcp dpt: 6379
ACEPTAR tcp - 172.16.0.0/16 172.17.0.2 tcp dpt: 6379

Información adicional que considere importante (por ejemplo, el problema ocurre solo ocasionalmente):

De forma predeterminada, la ventana acoplable controla el firewall de una manera que rompe la seguridad: permite que todo el tráfico de todos los dispositivos de red acceda a los puertos expuestos en los contenedores. Considere un sitio que tiene 2 contenedores: el contenedor A expone 443 ejecutando Nginx, y el contenedor B ejecuta una API en el puerto 8000. Es deseable abrir el contenedor A al público para su uso, pero ocultar el contenedor B por completo para que solo pueda hablar con el host local. (para que lo pruebe el usuario) y la red de Docker (para hablar con el Contenedor A). También podría ser deseable para propósitos de prueba que el Contenedor C sea una base de datos utilizada por el Contenedor B con el mismo tipo de restricciones.

Encontré esto debido a los registros de monitoreo en un servicio que _pensé_ no estaba abierto al público. Después de encontrar entradas de registro de fuentes que intentaban ingresar, verifiqué las reglas del firewall y descubrí que no había límite en las direcciones o interfaces de origen. Utilizo UFW y solo permito SSH en esta caja en particular, y preferiría mantenerlo así. Esto puede afectar drásticamente el uso de contenedores Docker para implementar servicios y generar posibles problemas de seguridad si las personas no tienen cuidado.

La mejor práctica de seguridad sería limitar de forma predeterminada la red para que funcione como el ejemplo de efecto deseado anterior y luego permitir que el usuario agregue las reglas de firewall adecuadas, etc. para anular dicho comportamiento, o tener una opción para volver al comportamiento actual. Sé que por razones heredadas eso no es probable, ya que rompería muchas cosas en la actualización; por lo que al menos tener una opción para habilitar lo anterior que se pueda activar ahora sería un buen primer paso, y quizás más tarde, después de muchas advertencias, convertirlo en el comportamiento predeterminado. Suponiendo que el comportamiento predeterminado es seguro, tener la funcionalidad para administrar esto (firewall-> habilitar puerto público, ip) en el yml de docker-compose sería una excelente manera de dar a conocer visiblemente lo que está sucediendo.

Encontré la opción --iptables = false, sin embargo, no quiero tener que establecer todas las reglas yo mismo. Lo único a lo que me opongo es a la configuración de origen de las reglas.

Si bien no lo he verificado, sospecho que todos los firewalls compatibles con Docker tendrán el mismo problema.

arenetworking versio1.10

Comentario más útil

El tema que Ben está sacando a la luz es real y sorprendente (una mala combinación). Muchos administradores, como yo, están usando el cortafuegos ufw probado y verdadero. Docker está ejecutando ufw y está alterando las reglas de iptables de tal manera que 1) hace que ufw informe erróneamente el estado actual de las reglas de filtrado de paquetes, y 2) expone servicios aparentemente privados a la red pública. Para que Docker permanezca en la buena disposición de la comunidad de administradores de sistemas, se debe idear otro enfoque. En este momento hay muchos administradores que, como Ben y yo, abrieron puertos inadvertidamente a Internet en general. Sin embargo, a diferencia de Ben y yo, aún no lo han descubierto.

Todos 114 comentarios

Nota: noté en https://github.com/docker/docker/blob/master/vendor/src/github.com/docker/libnetwork/iptables/iptables.go que ni siquiera existe una opción para configurar la fuente , por lo que solo usa los valores predeterminados de iptables para el dispositivo / ip de origen.

No es del todo # 14041, ya que este problema se refiere a puertos expuestos. Exponer puertos tiene la intención de hacerlos accesibles al público, ya que así es como expones los servicios al mundo exterior. Si está trabajando en un entorno de desarrollo, puede deshabilitar el acceso a los puertos desde fuera de su computadora con un servidor de seguridad de host, o simplemente no exponer los puertos y acceder a los servicios directamente, o desde otros contenedores en la misma red.

Le recomendaría que use las funciones de red de la ventana acoplable más nuevas para configurar redes privadas para servicios que no desea que se expongan en absoluto, consulte https://docs.docker.com/engine/userguide/networking/

Eso es lo que pensé primero; pero estaba un poco confundido, porque _exponer_ un puerto ( EXPOSE ), en realidad no hace nada, pero _publicar_ un puerto ( -p / -P ) en realidad lo expone en el anfitrión.

Si realmente está hablando de _publishing_, entonces esto es como se diseñó;

En su ejemplo, los contenedores B y C no deberían publicar sus puertos, y el contenedor A puede comunicarse con ellos a través de la red Docker, p. Ej.

docker network create mynet

docker run -d --net=mynet --name=api api-image
docker run -d --net=mynet --name=db database-image
docker run -d --net=mynet --name=web -p 443:443 nginx

Esto solo publica el contenedor "web" en el host. El contenedor web puede acceder a los contenedores "API" y "base de datos" a través de su nombre (es decir, http: // api : 80 / y db: 3306 (asumiendo MySQL))

@justincormack, así que no creo que usar una red privada resuelva el problema. En mi caso, estoy usando una red privada entre los contenedores y todavía están expuestos públicamente porque el firewall del host no está configurado para limitar la exposición a la red privada.

@thaJeztah, el problema aún se reduce al soporte de firewall: no hay soporte de firewall en la ventana acoplable para limitarlo a una red específica. Probablemente aún pueda acceder a esos contenedores desde otro sistema, ya que el firewall no evitará que otros sistemas accedan al puerto en el host.

Ahora estoy ejecutando esto a través de docker-compose; sin embargo, no es del todo un problema de composición de la ventana acoplable, ya que la funcionalidad libnetwork no tiene la capacidad de limitar la red en las reglas de firewall; las reglas de iptables no tienen una especificación de origen, por lo que independientemente de cómo se configure la red, siempre que se confíe en la ventana acoplable para crear la reglas del cortafuegos (cuál debería ser porque es más probable que las haga bien) entonces esto se convierte en un problema. Considere lo siguiente en un archivo docker-compose.yml:

nginx:
    build: ./docker/nginx/.
    ports:
        - "127.0.0.1:8080:80"
        - "127.0.0.1:443:443"
    environment:
        DESTINATION_HOST: repose
    links:
        - repose
repose:
    build: ./docker/repose/.
    ports:
        - "127.0.0.1:80:8080"
    environment:
        DESTINATION_HOST: phoenix
        DESTINATION_PORT: 8888
    links:
        - phoenix
curryproxy:
    build: ./docker/curryproxy/.
    ports:
        - "127.0.0.1:8081:8081"
    external_links:
        - rse_rse_1
        - rse_rse_2
        - rse_rse_3
phoenix:
    build: .
    ports:
        - '127.0.0.1:88:8888'
    links:
        - curryproxy:curry
    external_links:
        - rse_rse_1:rse
        - rse_rse_2
        - rse_rse_3
        - rse_cache_1:cache
    volumes:
        - .:/home/phoenix

Lo anterior es un extracto de uno de mis proyectos. Si bien quiero poder probarlos todos localmente desde mi host, no quiero que nadie más pueda acceder a nada más que a la instancia nginx.

No estoy seguro de cómo esto se traduce en su nomenclatura ... puede ser que esto sea parte del aspecto de "publicación", y la capacidad de publicación debe expandirse para hacer lo que estoy diciendo.

Si esto es por diseño, entonces es un modelo de seguridad deficiente, ya que ahora expone a todos los desarrolladores a riesgos extremos cuando se encuentran en redes desconocidas (por ejemplo, viajando).

Como dije, no espero que el valor predeterminado cambie de inmediato, pero tener la opción sería un buen primer paso.

Entonces estoy un poco confundido, ¿puede dar algunos ejemplos externos de lo que puede conectarse? Los servicios de backend estarán (de forma predeterminada) en la red 172.17.0.0/16 , a la que no podrá acceder externamente, no lo pensaría primero porque no tendrá una ruta a eso definido desde un host externo.

Existe un problema potencial si su IP externa también es una IP privada, por lo que el tráfico que se enruta a las redes internas no se eliminará (mientras que debería ser de público a privado). ¿Ese es el problema?

@justincormack, por lo que principalmente estoy configurando el proxy adecuado para que algunos servicios solo se puedan acceder a través del proxy (nginx - terminación ssl), que luego se filtra a través de un proxy de autenticación (reposo) y finalmente a otro servicio (phoenix). Me importaría menos si todos ellos están vinculados a la interfaz 0.0.0.0; pero solo quiero que nginx sea accesible externamente (o al menos la parte de reposo si no tuviera nginx en su lugar aquí). Una solución fácil, por ejemplo, sería no tener que establecer "127.0.0.1" en la configuración, sino tener una sección de firewall donde es fácil especificar que permitir el paso del firewall con una configuración base de solo la red docker y local. interfaces de host (loopback) habilitadas para hablar, algo como:

firewall:
    external:
        ports:
            - 80
            - 443

Ahora la situación se puede mitigar un poco limitando el mapeo de red en el _host_ a 127.0.0.1 en lugar del mapa predeterminado 0.0.0.0. Tenga en cuenta que esto es lo que realmente lo mitiga porque, de lo contrario, el puente reenviará el puerto host a la red de la ventana acoplable.

Y sí, verifiqué que esa limitación funciona; sin embargo, todavía deja vulnerabilidades potenciales en su lugar y las reglas del firewall no coinciden con lo que realmente se está haciendo.

Como otro ejemplo, hubo una vulnerabilidad del Kernel de Linux hace un tiempo (teniendo problemas para encontrarla en este momento) que estaba relacionada con los puertos que estaban marcados en IPtables como abiertos para su uso por aplicaciones, pero que en realidad no estaban conectados a una aplicación. - por ejemplo, estar en un puerto de host local pero no en un puerto IP público. Esto potencialmente configura eso, y sería una mejor práctica limitar las reglas de IPtables a las redes esperadas en lugar de dejarlas abiertas para conectarse desde cualquier lugar. Como dije, al menos tengo la opción de especificar. Es probable que hayan solucionado ese problema en particular, pero ¿por qué dejar abierta la posibilidad?

OIA, se trata de seguridad.

@BenjamenMeyer si no quieres que los otros servicios sean accesibles, ¿por qué publicas sus puertos? es decir, "127.0.0.1:8081:8081" no es necesario si solo se accede a través de la red docker (otros servicios se conectan directamente a través de la red docker)

Un problema que tengo relacionado con este es que me gustaría publicar puertos, pero solo permitir que ciertas direcciones IP accedan a ellos.

Por ejemplo, ejecuto un entorno Jenkins en un par de contenedores. El nodo maestro está "publicado", pero tengo que hacer algunas reglas de iptables bastante complicadas para bloquearlo y que solo las 2 oficinas que tenemos puedan acceder a él.

¿Hay alguna forma de evitar esto actualmente integrado en Docker? ¿O al menos una práctica recomendada? He visto en la documentación cómo puede restringir el acceso a 1 dirección IP; pero no varios. El otro problema con esto es que si tiene un servidor que ya tiene una configuración de iptables, es posible que esté restableciendo todas las reglas antes de aplicar sus reglas (de ahí las reglas complicadas que tuve que configurar).

Tengo un problema similar al indicado por @SeerUK. Existe una violación discordante de las expectativas cuando las reglas de firewall preexistentes no se aplican a los puertos de contenedor publicados. El comportamiento deseado es el siguiente (al menos para mí)

  1. El intento de conexión del usuario se filtra según las configuraciones de ENTRADA, etc.
  2. El reenvío de tráfico ocurre como de costumbre según las reglas FORWARD agregadas por la ventana acoplable

¿Existe una forma sucinta de lograr esto en iptables, o no permite fácilmente tal construcción? Estoy particularmente limitado en mi conocimiento de iptables, así que tengan paciencia conmigo. Recientemente, adquirí conocimientos al respecto mientras trataba de comprender las interacciones de Docker con él.

A lo que realmente he recurrido por el momento, dado que en realidad estoy ejecutando estos contenedores en un servidor dedicado bastante poderoso, configuré una máquina virtual KVM que ejecuta Docker, luego utilicé algunas reglas de iptables más estándar para restringir el acceso desde el host . La máquina virtual tiene su propia interfaz de red a la que solo se puede acceder desde el servidor, por lo que tengo que agregar reglas para permitir explícitamente el acceso a los puertos en iptables en el host. He perdido un poco de rendimiento, pero no mucho.

@thaJeztah Quiero poder acceder a él desde el sistema local y probarlo fácilmente. Por ejemplo, configurar una API HTTP RESTful que tenga un punto final de salud y poder ejecutar de manera confiable curl contra mediante el uso de localhost (tengo que documentar esto para otros y tener direcciones IP que cambian no es confiable ). En la mayoría de los casos, para mi entorno de desarrollo, solo quiero que los contenedores se comuniquen entre sí, pero también quiero poder acceder a ellos desde el host.

Para el caso de @SeerUK , poder establecer un bloque de IP (5.5.0.0/16 - un parámetro válido para una dirección de origen en las reglas de iptables) sería algo muy bueno. IPtables ya tiene la capacidad de hacer la limitación, pero Docker no la aprovecha.

@thaJeztah Configuré "127.0.0.1:8081:8081" explícitamente para mantenerlo fuera de la red externa; Encontré registros en mis contenedores docker de personas que intentaban entrar en los contenedores a través de los puertos expuestos.

Mi trabajo en este momento es apagar los contenedores de la ventana acoplable antes de irme por el día porque no puedo asegurar que el entorno que quiero que sea externo en realidad _ sea_ externo, o que el entorno esté adecuadamente limitado por motivos de seguridad.

@BenjamenMeyer una forma de hacer esto es ejecutar esas pruebas en un contenedor, por ejemplo

docker run --net -it --rm --net=mynetwork healthchecker 

El tema que Ben está sacando a la luz es real y sorprendente (una mala combinación). Muchos administradores, como yo, están usando el cortafuegos ufw probado y verdadero. Docker está ejecutando ufw y está alterando las reglas de iptables de tal manera que 1) hace que ufw informe erróneamente el estado actual de las reglas de filtrado de paquetes, y 2) expone servicios aparentemente privados a la red pública. Para que Docker permanezca en la buena disposición de la comunidad de administradores de sistemas, se debe idear otro enfoque. En este momento hay muchos administradores que, como Ben y yo, abrieron puertos inadvertidamente a Internet en general. Sin embargo, a diferencia de Ben y yo, aún no lo han descubierto.

@thaJeztah que asume que lo estoy haciendo a través de la línea de comandos y no usando otra herramienta en la que solo tengo que configurar una dirección IP.

Por ejemplo, estoy trabajando en una API. Tengo una herramienta que puedo usar para trabajar con esa API en producción para respaldarla; para el desarrollo de la herramienta y la API, solo quiero apuntar la herramienta a la API acoplada. La herramienta no sabe nada sobre Docker, ni debería hacerlo. Y no necesariamente quiero poner la herramienta en la ventana acoplable solo para usarla; apuntarla a un puerto expuesto solo al host local debería ser suficiente.

@jcheroske Estoy de acuerdo, pero no sé si hay una buena solución para ese aspecto. Para eso, ufw probablemente deba hacerse más inteligente para poder buscar e informar sobre reglas en las que no participó en la creación. Hay mucho software que puede ajustar las reglas iptables de formas que ufw (o AFAIK firewalld, etc.) no conocerá. En realidad, tampoco existe una solución sencilla para solucionarlo.

Dicho esto, sería bueno si Docker pudiera integrarse con ellos para descargar los archivos de configuración apropiados para poder habilitarlos / deshabilitarlos, o integrarse con esas herramientas de manera que se enganche y descargue la información de manera apropiada, sin embargo, dado hay mejores soluciones, no creo que ese aspecto se resuelva realmente. Aquí, se trata más de limitar el alcance de las reglas iptables que se están generando para al menos minimizar los impactos potenciales al permitir la especificación de la fuente (lo, eth0, 127.0.0.0/24, etc.).

Si está dispuesto a hacerlo, usar iptables lo hace totalmente posible.

Este es un ejemplo reducido de cómo puede usarlo: https://gist.github.com/SeerUK/b583cc6f048270e0ddc0105e4b36e480

Puede ver que justo en la parte inferior, 1.2.3.4 tiene acceso explícito al puerto 8000 (que está expuesto por Docker), luego se descarta cualquier otra cosa en ese puerto. La cadena PRE_DOCKER se inserta para estar antes de la cadena DOCKER para que se golpee primero, lo que significa que DROP evita que las solicitudes bloqueadas lleguen a la cadena DOCKER.

Es un poco molesto que Docker no tenga esta funcionalidad incorporada, pero es posible solucionarlo ahora mismo.

Otra alternativa sería utilizar un cortafuegos externo. Algunos lugares como AWS y Scaleway ofrecen cosas como grupos de seguridad donde puede administrar el acceso a sus cajas desde el exterior, desde allí, todos los puertos se comportan de la misma manera.

En realidad, nunca logré descubrir cómo hacer que esto funcione con UFW. Aunque por ahora, estoy contento con el uso de iptables como solución. Parece que me está funcionando muy bien hasta ahora.

Obviamente, esta no es una gran solución si ya ha creado un conjunto razonablemente complicado de reglas de firewall en torno a UFW. Sin embargo, hace que usar cosas como iptables-persistent sea bastante fácil. También puede utilizar formas alternativas de permitir el acceso a esta forma que parecen más "normales" en iptables.

@BenjamenMeyer, ¿ ha pensado en usar un docker network definido por el usuario con una opción de subred y rango de ip y asignar una dirección IP estática para los contenedores y usarlos para el desarrollo local para que no tenga que depender de una ip estática virtual como 127.0.0.1? Eso evitará la necesidad de tener un mapeo de puertos todos juntos para aquellos contenedores que son privados para un host.

docker network create --subnet=30.1.0.0/16 --ip-range=30.1.0.0/24 mynetwork
docker run --net=mynetwork --ip=30.1.1.1 --name=myservice1 xxxx
docker run --net=mynetwork --ip=30.1.1.2 --name=myservice2 yyyy

Con esta configuración, myservice2 puede llegar a myservice1 por el nombre myservice1 y ni siquiera hay necesidad de depender de la ip estática. Además, el host puede acceder a la IP estática libremente sin la necesidad de tener un mapeo de puertos.

También con compose 1.7, puede especificar una dirección IP estática para contenedores y especificar rangos y subredes de red.

Descubrí una solución simple.

1) Edite / etc / default / docker: DOCKER_OPTS="--iptables=false"

2) Agregue la regla ufw: ufw allow to <private_ip> port <port>

Tan simple que realmente me pregunto por qué la opción --iptables=false no es la predeterminada. ¿Por qué crear una situación así cuando todo lo que tiene que hacer la ventana acoplable es decir: "Oye, si estás ejecutando un cortafuegos, tendrás que perforarlo!" ¿Qué me estoy perdiendo?

https://fralef.me/docker-and-iptables.html
http://blog.viktorpetersson.com/post/101707677489/the-dangers-of-ufw-docker

No puedo hacer que Docker deje de modificar iptables para salvar mi vida. Intenté actualizar / etc / default / docker sin éxito en Ubuntu 16.04

@enzeart Prueba /lib/systemd/system/docker.service .

@SeerUK Bendice tu alma

@enzeart para configurar un demonio que se ejecuta en un host que usa systemd, es mejor no editar el archivo docker.unit en sí, sino usar un archivo "drop in". De esa manera, no tendrá problemas al actualizar la ventana acoplable (en caso de que haya un archivo docker.unit más nuevo). Consulte https://docs.docker.com/engine/admin/systemd/#custom -docker-daemon-options para obtener más información.

También puede utilizar un archivo de configuración daemon.json, consulte https://docs.docker.com/engine/reference/commandline/daemon/#daemon -configuration-file

@mavenugo Ya existe una red acoplable.

@jcheroske eso funciona, pero como señalé, significaría que el _end-user_ (yo) tendría que asegurarse de que todas las reglas iptables fueran correctas, lo cual no es óptimo y no es tan probable que suceda como hacer que docker haga automáticamente, por lo tanto este problema.

Hola, por favor. Creo que su problema también. La cadena de contenedores en Iptables debe seguir las reglas principales y no estar expuesta al mundo de forma predeterminada.

Realmente me gustaría que Docker (y docker-compose) tenga la capacidad de incluir en la lista blanca o en la lista negra las direcciones IP que pueden acceder a ese puerto.

Toma por ejemplo:

nginx:
    ports:
      - "8000:8000"
    whitelist:
      - 10.6.20.2

Implicaría que solo una IP de origen de 10.6.20.2 podría acceder al puerto 8000 en este host.

@StefanPanait Me gusta mucho esa idea. También podría funcionar con una sintaxis similar a los volúmenes y las listas de acceso / denegación, algo como:

nginx:
  access:
  - "10.0.1.6:allow"
  - "deny"

Por supuesto, todavía tendría que permitir cosas como la comunicación entre contenedores.

La comunicación entre contenedores de

Aunque supongo que la forma correcta de hacerlo sería separar las redes de la ventana acoplable ... aún así, creo que poder hacer algo como:

nginx: access: - "10.0.1.6:allow" - "webapi:allow" - "database:deny" - "deny"
Podría ser útil ... la pregunta es, ¿es lo suficientemente útil para justificar la implementación en ese grado? No sé.

En la actualidad, me gustaría ver el problema original resuelto, luego se pueden agregar características como esta si no tienen sentido para diseñar en la resolución para empezar (podría ser).

¿Por qué no debería poder prohibir que un contenedor hable con él? Eso podría ser extremadamente útil para depurar

¿Para eso es docker network disconnect ? Puede desconectar un contenedor de la red para depurarlo y volver a adjuntarlo con docker network attach

Para aquellos que acaban de descubrir que una tonelada de puertos estaban abiertos en sus servidores expuestos a Internet, después de utilizar UFW, cavé y descubrí lo siguiente:

Ubuntu 16.04 con UFW y Docker presenta nuevos desafíos. Hice todos los pasos como se muestra aquí: https://svenv.nl/unixandlinux/dockerufw PERO NO pude hacer que Docker plus UFW funcionara en 16.04. En otras palabras, no importa lo que hice, todos los puertos docker quedaron expuestos globalmente a Internet. Hasta que encontré esto: http://blog.samcater.com/how-to-set-docker-1-12-to-not-interfere-with-iptables-firewalld/
Tuve que crear el archivo: /etc/docker/daemon.json y poner lo siguiente en:

{
"iptables": falso
}

Luego emití sudo service docker stop luego sudo service docker start FINALMENTE docker simplemente sigue las reglas apropiadas en UFW.

Datos adicionales: https://chjdev.com/2016/06/08/docker-ufw/

@thaJeztah

¿Por qué no debería poder prohibir que un contenedor hable con él? Eso podría ser extremadamente útil para depurar
¿Para eso es la desconexión de la red de Docker? Puede desconectar un contenedor de la red para depurarlo y volver a conectarlo con la conexión de red de la ventana acoplable

¿Y si se deseara seguir teniendo conectividad de red? Ejemplo: Prueba de falla de verificación de estado del servidor en el contenedor B para el contenedor A mientras los contenedores C, D y E siguen brindando servicios. dependen del acceso al Contenedor B para que se apruebe su Comprobación de estado.

Aún así, eso no resiste el "solucionemos el problema original".

@ gts24 hallazgo interesante.

En mi humilde opinión, todo el problema es que Docker, como literalmente todos los demás programas, no debería tocar el firewall (iptables o de otro tipo) en absoluto. Cuando instalo (por ejemplo) Apache y la cuento para escuchar en 0.0.0.0:80 es mi decisión de abrir el puerto 80 en el servidor de seguridad o no, donde puedo especificar las reglas para ello que deseo.

En lugar de reinventar las reglas del firewall en los archivos de configuración de la ventana acoplable (y / o componer), toda la función PUBLISH debería quedar obsoleta y una nueva función LISTEN creada para funcionar como todos los demás programas. En el mejor de los casos, la ventana acoplable podría crear servicios de firewalld deshabilitados por defecto para cada puerto / contenedor, en los sistemas que lo usan.

@gcscaglia estableció --iptables=false en el demonio y debería obtenerlo

@thaJeztah Esa característica es inútil porque Docker no ofrece ninguna alternativa para obtener las reglas de firewall necesarias (o ganchos de script de activación más específicos con los parámetros relevantes) del demonio. Es apenas utilizable si tiene contenedores estáticos y nunca cambia nada sobre ellos (por ejemplo, puertos publicados), pero para todos los demás casos puede olvidarse de él.

@thaJeztah exactamente lo que dijo taladar.

--iptables=false debería cambiarse de nombre a --networking=false ya que ni siquiera las redes internas de contenedor a contenedor funcionan con esta función desactivada. Una opción para que un contenedor escuche en alguna combinación de puerto / interfaz sin perforar las reglas de firewall de entrada (es decir, LISTEN ) resolvería todo esto, sería compatible con versiones anteriores y permitiría que se usara --iptables=true .

Con tal modo de escucha, aquellos que quieran que "simplemente funcione" pueden seguir usando PUBLISH , y aquellos que quieran control pueden usar LISTEN .

@gcscaglia ok, así que si quieres que Docker configure las reglas básicas y maneje la red contenedor-contenedor pero no la "publicación". Puede mantener --iptables habilitado, pero _no_ use -p / --publish . Debería poder configurar manualmente las reglas de IPTables para reenviar puertos a contenedores. El contenedor ya escucha en su propia dirección IP privada y en los puertos en los que escucha el servicio en su contenedor.

@thaJeztah No, no puedes. Porque no tiene idea de que incluso hay un contenedor que acaba de iniciarse y necesita reglas de firewall. Tampoco tiene forma de decirle a quien haya utilizado la API para iniciarla en qué puerto host está escuchando.

Ejemplo sencillo. Usamos el contenedor Docker para ejecutar trabajos de Jenkins. Exponen su puerto SSH para que Docker pueda contactarlos. Se destruyen tan pronto como se hace el trabajo. Docker no ofrece ninguna forma de hacer que esto funcione con --iptables = false porque no tiene forma de decirle a Jenkins (usando la API de Docker para iniciar el contenedor) el puerto de host y no tiene forma de activar un script para configurar las reglas de firewall necesarias.

Su idea solo funciona para el caso de uso ridículamente simple de tener contenedores permanentes, que nunca cambian, lanzados manualmente. Incluso un simple --restart = always en el contenedor interrumpirá esta configuración a menos que el contenedor tenga una IP estática.

@taladar Estoy respondiendo al caso de uso de @gcscaglia , que solicitó una función en la que Docker _no_ administra IPTables para abrir puertos, y donde tiene el control sobre IPTables; es decir, no se expone ningún recipiente, a menos que se haga manualmente. Además, en mi respuesta le expliqué que _no_ usa --iptables=false , pero para mantenerlo habilitado, pero simplemente no use la función -p / --publish (que le dice a Docker que haga aquellos puertos accesibles a través de IPTables).

@thaJeztah Si bien tiene mi caso de uso correcto, AFAIK, la solución propuesta no funcionará para mí por las mismas razones por las que no funcionará para el caso de taladar: un solo reinicio de cualquier cosa y, de repente, mis reglas manuales deben actualizarse. No hay ganchos o activadores que pueda usar para recibir una notificación de un reinicio para poder automatizar dicha actualización (sin mencionar que estaba reinventando la rueda).

Actualmente, la única solución para mi caso es tener otro firewall (que la ventana acoplable no puede tocar) entre el host de la ventana acoplable y todas las redes externas. Pero si Docker simplemente hiciera todo lo que ya hace, excepto abrir puertos al mundo, podría eliminar por completo el segundo firewall.

Supongo que no puedes tenerlo todo, pero seguramente es frustrante. ¿Crees que vale la pena abrir una solicitud de función sobre mi idea LISTEN , o el equipo de Docker no estaría interesado en una función de este tipo?

¿Qué haría el LISTEN ? Hay EXPOSE , que le permite anotar en qué puertos escucha el contenedor. Para los desencadenantes, puede escuchar la ventana acoplable events .

No digo que no hay margen de mejora aquí (sé que se está investigando), solo me pregunto qué esperarías

Actualmente, como dijiste, todos los servicios que se ejecutan en un contenedor se vinculan a la IP privada del contenedor (si no usas --net=host o similar). Esto es bueno y deseable ya que dicho aislamiento entre el host y los contenedores es exactamente el punto de venta de Docker.

Pero, actualmente, si quiero que una aplicación que se ejecute fuera de cualquier contenedor (ya sea en el host o en otro lugar de la red) tenga acceso a un servicio que se ejecuta dentro de un contenedor, necesito medios para que ese servicio escuche en una de las interfaces de red del host. Para resolver este problema sin exponer las interfaces de ningún host al contenedor, Docker creó la función -p / --publish , que:

  1. Crea reglas de iptables para reenviar un puerto elegido en la interfaz de un host elegido a un puerto elegido en la IP privada del contenedor (que todos esperamos, ya que eso es lo que pedimos)
  2. Le dice a iptables que permita a cualquier persona desde cualquier parte del mundo acceder a ese puerto en la interfaz del host elegido (lo cual es innecesario para que el reenvío funcione y, por lo tanto, nos toma por sorpresa a muchos de nosotros)

Lo que propongo es una función (llamada LISTEN o de otro modo) que solo hace

En cuanto a EXPOSE , AFAIK son solo metadatos en imágenes de Docker para que el demonio sepa qué hacer cuando el usuario especifica -P (publicar todo). ¿Quizás me equivoque al respecto y los puertos "expuestos" se pueden reenviar de una manera resistente al reinicio (sin acceso mundial)?

--sin relación--

En mi humilde opinión, el OP de este problema pregunta exactamente por qué -p hace "2" y cómo evitar que Docker lo haga. Si bien una configuración a nivel de demonio (que no sea deshabilitar la red) podría resolverlo, para mantener las cosas compatibles con versiones anteriores, lo mejor sería una nueva característica (es decir, LISTEN o algún otro nombre).

Si bien a muchos usuarios les gusta este efecto "funciona fuera de la caja", ningún administrador de sistemas espera que otros programas que no sean iptables / firewalld abran puertos en el firewall, menos aún en una forma en la que su software de administración de firewall no informa.

Veo que hay tres problemas principales:

  1. La forma de publicar / exponer puertos de Docker pasa por alto las reglas de firewall comunes como UFW.
  2. El hecho anterior no parece estar bien documentado (es completamente inesperado, ya que no conozco ningún otro servicio de Linux que omita las reglas del firewall).
  3. No existe una forma sencilla de adaptar iptables a la forma de publicar / exponer puertos de Docker o las formas que la gente conoce no son fáciles de automatizar y ninguna está documentada.

Quizás técnicamente no es factible implementar la exposición de puertos en la tabla FILTER y, por lo tanto, la fijación 1 no es posible. Como mínimo, esto debería recibir una gran advertencia en alguna parte (solución 2), pero idealmente 3 podrían resolverse con nuevas opciones, como las personas aquí han sugerido, como allow o deny que agregarían reglas de firewall adicionales. automáticamente para permitir o denegar direcciones IP específicas para los puertos expuestos / publicados.

Por ejemplo, allow: "tcp:{your_trusted_ip}" para un contenedor llamado "elasticsearch", el puerto de publicación 9200 podría hacer algo como:

iptables -t mangle -N DOCKER-elasticsearch
iptables -t mangle -A DOCKER-elasticsearch -s {your_trusted_ip} -j RETURN
iptables -t mangle -A DOCKER-elasticsearch -j DROP
iptables -t mangle -I PREROUTING -p tcp --dport 9200 -j DOCKER-elasticsearch

Encontré esto bastante útil de los documentos de Docker: https://docs.docker.com/engine/userguide/networking/default_network/container-communication/#communicating -to-the-outside-world

Las reglas de reenvío de Docker permiten todas las direcciones IP de origen externo de forma predeterminada. Para permitir que solo una IP o red específica acceda a los contenedores, inserte una regla negada en la parte superior de la cadena de filtros 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 CAÍDA

@jmimico sí, me he encontrado con eso antes. ¿Cómo restringiría el acceso desde 2 o más direcciones IP?

¿Qué tiene de difícil Docket simplemente agregar una opción para ejecutar un script de shell en cualquier lugar donde ahora crea reglas de iptables con toda la información interna de Docker pasada al script como parámetros? Eso permitiría a todos crear exactamente las reglas que necesitan. Agregue alguna forma de activar una nueva ejecución de los scripts para los contenedores activos a los que las personas pueden llamar después de realizar una restauración de iptables o vaciar las cadenas por otras razones y ya está.

Docker no necesita recrear todo tipo de escenarios de firewall preconstruidos como sugieren algunas personas aquí, que podrían construirse sobre un sistema como ese mediante conjuntos de distribución de scripts de gancho. A lo sumo, algo como exponer solo en localhost y exponer globalmente (como lo hace Docker ahora) podría tener sentido. IPTables tiene demasiada flexibilidad para que Docker espere modelar todos los escenarios directamente en la configuración.

Este ticket ha existido aparentemente desde siempre, el comportamiento actual hace que Docker sea inutilizable (parece ser la forma estándar de implementar características en este proyecto, por ejemplo, la falta de un GC integrado adecuado o apenas un almacenamiento utilizable, libre de errores del kernel y de rendimiento backend, ...) y existe una solución sencilla que permite a las personas implementar sus propias soluciones que se adapten a sus entornos.

@StefanPanait Buena pregunta. Mi apuesta es que necesitaría aprovechar el uso de grupos de objetos. Complete los grupos de objetos con IP en la lista blanca y luego use ese grupo de objetos en la primera línea de la cadena DOCKER.

Ejemplo:
iptables -N docker-allow
iptables -A docker-allow -s 1.1.1.1 -j ACEPTAR
iptables -A docker-allow -s 2.2.2.2 -j ACEPTAR
iptables -A docker-allow -s 3.3.3.3 -j ACEPTAR
iptables -A docker-allow -j DROP

iptables -I DOCKER -i ext_if -j docker-allow

¿No son las reglas agregadas a la cadena DOCKER alguna vez golpeadas por cosas como el reinicio del demonio? Creo que el problema con las soluciones manuales es que son difíciles de hacer bien y, por lo tanto, hay un caso sólido para respaldar mejor los casos comunes (bloquear / permitir IP individuales) de manera más directa.

¿Quizás un complemento sería el lugar apropiado para admitir esto? Por ejemplo, tal vez un complemento "ufw" podría agregar reglas de una manera compatible con ufw para que el usuario pudiera administrar el firewall de manera efectiva con su cadena de herramientas de firewall regular y los servicios de la ventana acoplable se comportarían más como los servicios de host normales.

¿No son las reglas agregadas a la cadena DOCKER alguna vez golpeadas por cosas como el reinicio del demonio? Creo que el problema con las soluciones manuales es que son difíciles de hacer bien y, por lo tanto, hay un caso sólido para respaldar mejor los casos comunes (bloquear / permitir IP individuales) de manera más directa.

Agregar la regla de caída a la cadena DOCKER-USER parece funcionar mejor para hacerla persistente en los reinicios de la ventana acoplable.

Con Docker v.17.06 hay una nueva cadena de iptables llamada DOCKER-USER. Este es para sus reglas personalizadas, vea mi respuesta sobre serverfault: https://serverfault.com/questions/704643/steps-for-limiting-outside-connections-to-docker-container-with-iptables/886257#886257

Como comenté en SF, extraño por qué esta cadena DOCKER-USER es diferente a cualquier otra cadena agregada por el usuario. No tiene ningún filtro preaplicado y filtra todo el tráfico y no solo el tráfico destinado al contenedor de la ventana acoplable. todavía tiene que especificar los nombres de la interfaz usted mismo y todavía es propenso a que los expertos que no son de iptables cometan errores graves.

Por otro lado, todavía va con la mentalidad de "Docker es el único usuario de iptables" que apesta a las personas que quieren usar iptables para algo más que Docker. Por lo tanto, es malo para toda la gama de usuarios potenciales, además de quizás para las personas que dedican hosts completos a nada más que a Docker.

Bien, entonces el uso de DOCKER-USER resuelve el problema del orden de las inserciones asegurándose de que siempre viene en la cadena antes que las otras reglas relacionadas con Docker. Sin embargo, no facilita mucho el tráfico de la lista blanca a un contenedor por número de puerto, ya que en este punto --dport es el puerto del servicio dentro del contenedor de la ventana acoplable, no el puerto expuesto. Ejemplo:

Publique el puerto 9900 para exponer un servicio de Docker que escucha internamente en 9000.

$ sudo iptables -A DOCKER-USER -m limit --limit 20/min -j LOG --log-prefix "IPTables: "
$ docker run --rm -it -p '192.168.56.101:9900:9000' alpine nc -l 9000

Desde otra máquina en la red:

$ telnet 192.168.56.101 9900

Qué se registra:

IPTables: IN=enp0s8 OUT=docker0 MAC=08:00:27:b6:8d:d6:0a:00:27:00:00:04:08:00 SRC=192.168.56.1 DST=172.17.0.2 LEN=52 TOS=0x00 PREC=0x00 TTL=127 ID=14127 DF PROTO=TCP SPT=51208 DPT=9000 WINDOW=64240 RES=0x00 SYN URGP=0
IPTables: IN=docker0 OUT=enp0s8 PHYSIN=veth05ba007 MAC=02:42:0f:f9:76:4c:02:42:ac:11:00:02:08:00 SRC=172.17.0.2 DST=192.168.56.1 LEN=40 TOS=0x00 PREC=0x00 TTL=63 ID=23041 DF PROTO=TCP SPT=9000 DPT=51208 WINDOW=0 RES=0x00 ACK RST URGP=0

Entonces, como puede ver, no hay oportunidad en este punto de filtrar el tráfico al puerto 9900. Claro, podría filtrar el tráfico a 9000 pero esto es un problema porque el puerto interno podría superponerse involuntariamente entre varios contenedores o incluso servicios que se ejecutan en el host. Este es uno de los grandes puntos de venta de Docker: puede ejecutar varios servicios en un host y no preocuparse por los conflictos de puertos. Por lo tanto, muchos contenedores están diseñados para escuchar en un puerto y el usuario puede usar la opción --publish para cambiar qué puerto está expuesto en qué interfaz:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis

Sin embargo, no puedo usar DOCKER-USER (corríjame si me equivoco) para afectar el tráfico a los datos1 sin afectar también el tráfico a los datos2 a menos que use algún tipo de introspección para descubrir la IP del contenedor de destino que es transitoria y lo lleva de vuelta a Cuadrado uno en encontrar una manera simple y confiable de firewall servicios publicados sin scripts e introspección.

Para ser claros, esto NO funciona:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 7777 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 8888 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 7777 -j DROP
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 8888 -j DROP

Esto funciona, pero el resultado es que ambos servicios están expuestos a ambos CIDR incluidos en la lista blanca:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 6379 -j DROP

Por lo tanto, parece que DOCKER-USER solo es útil para exponer todos los puertos a IP específicas pero no exponer puertos específicos a IP específicas a menos que no le importe escribir sus reglas de iptables para hacer referencia a números de puerto internos y no tenga varios contenedores usando el mismo números de puerto internos. Todo el mundo parece estar perdiendo estos puntos y corriendo con DOCKER-USER como solución y creo que esto merece una nueva y mejor solución.

@SeerUK
La solución publicada en https://gist.github.com/SeerUK/b583cc6f048270e0ddc0105e4b36e480 no funcionó para mí. ¿Podrias ayudarme por favor?

El error de Docker es usar iptables (firewall del sistema) para realizar el enrutamiento de una aplicación. Creará para siempre problemas y caos. Cambiar iptables es muy peligroso. Agregar esta función incrustada en la ventana acoplable no es tan caro. Además, también podría tener un estado inconsistente simultáneo si ejecuta Docker de forma simultánea.

Parece que iptables tiene comportamientos extraños si cambia iptables cuando Docker se está ejecutando. Si detiene la ventana acoplable, configure iptables y la ventana acoplable de reinicio funcione como se prevé en. Mi preocupación es ... ¿si tengo que cambiar iptables en producción?

Parece que iptables tiene comportamientos extraños si cambia iptables cuando Docker se está ejecutando.

iptables es iptables, no le importa si Docker se está ejecutando o no.

Es mejor si hace muchas pruebas antes ... decirle a iptables es iptables es solo una tautología. Es mi sugerencia :)

Creo que su punto fue que iptables no se comporta de manera diferente cuando Docker se está ejecutando como sugirió su comentario. Este es claramente un problema de Docker con la forma en que usan iptables como si nadie más lo necesitara en el mismo sistema.

No veo por qué DOCKER-USER no resuelve esto.
Utiliza iptables para filtrar como desee: puerto de origen, puerto de destino, dirección de origen, tráfico no local, etc.

El objetivo de DOCKER-USER es que ejecuta las reglas que el usuario quiera ejecutar antes de que se ejecuten las reglas de la ventana acoplable, esto le permite hacer lo que quiera con el tráfico antes de que llegue a la ventana acoplable.

@ cpuguy83 el problema principal es que la

DOCKER-USER no puede resolver eso porque tampoco vincula las redes docker a una interfaz de red específica o una IP específica (fe 127.0.0.1).

Según mi solicitud original:
1.Docker debe vincularse a una IP específica, basada en la configuración de la red de Docker, y luego permitir que el usuario agregue reglas para exponer el contenedor fuera del sistema (utilizando las herramientas de su elección); por defecto, el contenedor no debe exponerse fuera del sistema.

  1. Eso no se puede hacer sin un período de transición debido a que al menos algunas personas confían en el comportamiento histórico.

En mi humilde opinión, todo el problema es que Docker, como literalmente todos los demás programas, no debería tocar el firewall (iptables o de otro tipo) en absoluto. Cuando instalo (por ejemplo) apache y le digo que escuche en 0.0.0.0:80, es mi decisión abrir el puerto 80 en el firewall o no, donde puedo especificar las reglas que desee.
En lugar de reinventar las reglas del firewall en los archivos de configuración de la ventana acoplable (y / o componer), toda la función PUBLICAR debería quedar obsoleta y se debería crear una nueva función LISTEN para que funcione como todos los demás programas. En el mejor de los casos, Docker podría crear servicios firewalld desactivados por defecto para cada puerto / contenedor, en los sistemas que lo utilizan.

¡Bien dicho @gcscaglia ! Aún más desconcertante, hay poca o ninguna mención de todo esto en https://docs.docker.com/engine/reference/run/#expose -incoming-ports, que creo que es donde la mayoría de los que se preocupan buscar un poco de documentación para complementar el aprendizaje con el ejemplo terminará en. Debería haber un cuadro rojo brillante en algún lugar que explique los riesgos de que Docker anule las reglas de iptables preexistentes.

Si no desea que Docker administre iptables, configure --iptables-=false
Si solo desea que Docker abra puertos en una interfaz específica, también puede configurarlo en la configuración del demonio.

@BenjamenMeyer
Puede bloquear todo el tráfico en DOCKER-USER y dejar pasar solo lo que desee.

Puede bloquear todo el tráfico en DOCKER-USER y dejar pasar solo lo que desee.

@ cpuguy83 Por favor, dígame si algo de lo que digo aquí es incorrecto:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis

Sin embargo, no puedo usar DOCKER-USER (corríjame si me equivoco) para afectar el tráfico a los datos1 sin afectar también el tráfico a los datos2 a menos que use algún tipo de introspección para descubrir la IP del contenedor de destino que es transitoria y lo lleva de vuelta a Cuadrado uno en encontrar una manera simple y confiable de firewall servicios publicados sin scripts e introspección.

Para ser claros, esto NO funciona:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 7777 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 8888 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 7777 -j DROP
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 8888 -j DROP

Esto funciona, pero el resultado es que ambos servicios están expuestos a ambos CIDR incluidos en la lista blanca:

$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 192.168.56.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp -s 10.0.24.0/24 --dport 6379 -j RETURN
$ sudo iptables -A DOCKER-USER -p tcp -m tcp --dport 6379 -j DROP

Por lo tanto, no hay forma de controlar el tráfico hacia / desde el contenedor de datos1 independientemente del contenedor de datos2 usando DOCKER-USER . Para mí, esto hace que DOCKER-USER no sea una gran solución.

@colinmollenhour

Si tiene una regla de caída genérica en DOCKER-USER , ¿en qué se diferencia de si la ventana acoplable anexa frente a la anteposición de la regla de salto principal?

@ cpuguy83 mi punto no está en contra de la anulación per se. Lo que estoy diciendo es que hacer que Docker anule las reglas de iptables preexistentes definitivamente debería ser una función de suscripción voluntaria, no una función de exclusión voluntaria.

E incluso como una opción de exclusión, que no debería ser, debería estar extremadamente bien documentado, ya que es completamente inesperado y bastante contrario a la intuición de depurar. Especialmente considerando cuántos usan ufw . No conozco ningún otro software que haga algo como esto sin advertencias muy explícitas y personalmente me mantendría alejado de dicho software.

Además, esto se ve agravado por el hecho de que la mayoría de los usuarios de Docker, al menos en mi experiencia, comienzan a usarlo en entornos donde la infraestructura de red adicional enmascara el problema, lo que refuerza la suposición de que la máquina está configurada como ellos creen.

Espero que Docker adopte un enfoque de solo ESCUCHAR, como lo ejemplifica

Si tiene una regla de caída genérica en DOCKER-USER, ¿en qué se diferencia de si la ventana acoplable se agrega o se antepone a la regla de salto principal?

No creo haber entendido su pregunta ... Por favor, ignore mi comentario del 22 de abril de 2017, ya que es solo en lo que respecta a la persistencia que DOCKER-USER realmente resuelve. El problema que estoy señalando no es la persistencia.

@taladar ¿Qué estás

@jacoscaz

Primero, creo que todos los mantenedores están de acuerdo en que el comportamiento existente no es ideal. Desafortunadamente, el comportamiento ha existido desde siempre. Cambiar un valor predeterminado por algo que usan millones de personas no es realmente algo que podamos hacer aquí. Esta es una de las razones por las que agregamos DOCKER-USER para que al menos las personas puedan inyectar las reglas que necesiten.

Sin embargo, no podemos evitar la necesidad de usar iptables (o ebpf, o alguna otra solución nativa) Y proporcionar la funcionalidad que proporciona -p ... otro lado, si desea evitar que las personas hagan agujeros en el firewall, no les permita usar -p .

En resumen, puede:

  1. Dígale a Docker que (de forma predeterminada) se vincule a una dirección específica (la dirección predeterminada es, por supuesto, 0.0.0.0 , o todas las interfaces) ... sin embargo, esto no evitará que alguien especifique manualmente una dirección en -p spec (por ejemplo, -p 1.2.3.4:80:80 )
  2. Inyecte reglas personalizadas en DOCKER-USER , incluida una denegación de todo
  3. Deshabilite la administración de iptables con --iptables=false

¿Hay algo más que pueda sugerir que no incluya romper con los usuarios existentes?

@ cpuguy83 Lo entiendo. No estoy abogando por que tal cambio suceda drásticamente de una versión a otra. Creo que sería bueno planificar un cambio en el transcurso de varias versiones. Tampoco creo que Docker deba dejar de usar iptables por completo. El reenvío es diferente de permitir y, por lo que puedo entender, Docker debería poder reenviar de forma predeterminada sin permitir el acceso a nadie en lugar de forma predeterminada.

En cuanto a las cosas que se pueden hacer ahora, la documentación adicional sobre esto debería ser lo primero y más importante. ¿Cuál sería el canal más apropiado para plantear esto a los mantenedores de docker.com?

En ausencia de interés por su parte, mantener viva esta conversación es probablemente la mejor manera de maximizar las posibilidades de que este comportamiento sea más conocido.

Estoy de acuerdo con la llamada para agregar documentación adicional para esta funcionalidad. Por lo que puedo decir, solo se menciona en https://docs.docker.com/network/iptables/. Solo me enteré de este problema después de intentar averiguar por qué un servicio que había restringido para que estuviera disponible para una IP específica a través del firewall estaba disponible para el público. Este comportamiento fue completamente inesperado para mí, ya que es contrario a todos los demás servicios en red con los que he tratado.

Entiendo que existen soluciones alternativas, pero creo que esto debería considerarse para un cambio a largo plazo en el funcionamiento de Docker. Me gusta la idea de configurarlo para ESCUCHAR en un puerto y permitir que las reglas definidas por el usuario se desarrollen encima de eso.

Como sugerí, agregué una regla DROP a DOCKER-USER para evitar que los contenedores se expongan al exterior accidentalmente (lo que, de manera alarmante, sucedió).

Sin embargo, ahora tengo un servicio que deseo exponer. Pero como ha explicado @colinmollenhour , debido a que NAT ocurre antes del filtrado, solo puedo filtrar por la ip de la ventana acoplable (que no es fija) y el número de puerto interno (que podría ser el mismo para varios contenedores).

Entonces, ¿cómo puedo exponer este servicio?

@SystemParadox esa es una de las muchas razones por las que DOCKER-USER no es una solución real al problema.

@ cpuguy83
Me gusta la solución LISTEN propuesta y nunca he abogado por un cambio radical de un lanzamiento a otro; sino que recomendé hacer el cambio en una serie de versiones con los avisos apropiados, porque me doy cuenta de que mucha gente usa Docker y tener un cambio rotundo de una versión a otra sería perjudicial para todos.

También estoy de acuerdo en que la actualización de la documentación de Docker con respecto a -p y EXPOSE, etc. debería ser una prioridad que se haga de inmediato para al menos generar conciencia sobre el problema. En mi experiencia, la mayoría de las personas que usan Docker no son expertos en firewall, por lo que confían en Docker para que haga lo que esperan, que no está en el diseño actual.

Además, las soluciones de recapitulación en https://github.com/moby/moby/issues/22054#issuecomment -425580301 tampoco funcionan realmente. ¿Por qué? No ejecuto Docker directamente, ejecuto Docker Compose, basado en YAML; Las direcciones IP son dinámicas (controladas por Docker) y, a menudo, implemento varios servicios dentro de la misma red Docker que necesitan interactuar entre sí. Por lo tanto, tanto el uso de -p uso de enlace de direcciones (opción 1 en el resumen) no son soluciones. DOCKER-USER realmente no resuelve nada como otros han señalado (opción 2 en el resumen) y deshabilitar las tablas de IP por completo (opción 3 en el resumen) tampoco ayuda en nada porque ahora todo está roto (las IP son dinámicas, por lo que Es difícil crear una solución; la red entre contenedores está rota porque Docker se basa en IPTables para moverse entre los contenedores; etc.).

Nuevamente, no hay ninguna llamada en este hilo para un cambio rotundo entre dos versiones; pero se acercó un llamado a una fase planificada que permita a las personas migrar de manera adecuada.

Como solución alternativa, puede acceder al puerto de destino original usando -m conntrack --ctorigdstport .

Entonces en mi caso tengo lo siguiente:

-A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow docker out
-A DOCKER-USER -s 172.17.0.0/16 -j ACCEPT
# Allow access to docker service mapped to host 8702 (the service is actually listening on port 8088 in the container)
-A DOCKER-USER -p tcp -m conntrack --ctorigdstport 8702 -j ACCEPT
# Prevent access to docker from outside
-A DOCKER-USER -j DROP

@SystemParadox

¡Hurra por la solución correcta de iptables! 🍻

Nunca había oído hablar de --ctorigdstport pero supongo que se debe a que no he leído ni probado todas las posibles extensiones de iptables y, que yo sepa, usted es el primero en mencionarlo en este contexto.

Probé y esto de hecho funciona:

$ docker run -d -p 7777:6379 --name data1 redis
$ docker run -d -p 8888:6379 --name data2 redis
$ sudo iptables -N DOCKER-USER-redis1
$ sudo iptables -A DOCKER-USER-redis1 -s 192.168.56.0/24 -p tcp -m tcp -j RETURN
$ sudo iptables -A DOCKER-USER-redis1 -j REJECT --reject-with icmp-port-unreachable
$ sudo iptables -N DOCKER-USER-redis2
$ sudo iptables -A DOCKER-USER-redis2 -s 10.0.24.0/24 -p tcp -m tcp -j RETURN
$ sudo iptables -A DOCKER-USER-redis2 -j REJECT --reject-with icmp-port-unreachable
$ sudo iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 7777 -j DOCKER-USER-redis1
$ sudo iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8888 -j DOCKER-USER-redis2

Creo que un ejemplo como este pertenece a los documentos, ya que probablemente cubre lo que el 99% de los usuarios están buscando: la capacidad de exponer puertos usando -p pero aún poder controlar el tráfico hacia ellos usando filtros comunes como -s .

Creé una solicitud para actualizar la documentación de Docker con respecto a iptables.

https://github.com/docker/docker.github.io/issues/8087

La solución enumerada allí en https://unrouted.io/2017/08/15/docker-firewall/
parece ser algo similar, creando una cadena de iptables adicional llamada FILTROS
a donde saltan las cadenas INPUT y DOCKER-USER.

@SystemParadox @colinmollenhour Después de probar --ctorigdstport puedo confirmar que funciona, pero con una pequeña advertencia.

En mi caso, tengo una aplicación PHP acoplada en Apache escuchando en el puerto 80. Mis reglas que permiten solo 1.2.3.4 son las siguientes:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j ACCEPT
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 -j DROP

Entonces, mi regla de eliminación es un poco más específica que la tuya, solo elimina los paquetes que llegan a mi servidor web, o eso pensé. De hecho, estaba descartando paquetes dirigidos a mi servidor web, así como paquetes que regresaban con respuestas de solicitudes realizadas por la aplicación PHP a servidores de terceros.

Esto se debe al hecho de que --ctorigdstport no coincide con el puerto de destino del paquete que se está filtrando, sino con el paquete que inició la conexión. Por lo tanto, las respuestas a las solicitudes que salen de Docker a otros servidores tendrán SPT=80 y también coincidirán con --ctorigdstport 80 .

Si alguien quiere tener un control más estricto en las reglas DROP, también se debe agregar --ctdir :

-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j DROP

De hecho, todas las reglas que permiten la conexión también deben tener --ctdir agregado para expresar exactamente su significado:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j ACCEPT

@jest wow ¡eso es realmente importante saberlo! No me había dado cuenta de que podía coincidir con los paquetes en la otra dirección. Tiene sentido cuando lo piensa, ya que coincide con el estado de toda la conexión, pero es fácil pasarlo por alto al leer los documentos.

@SystemParadox sí, no tuve la oportunidad de informarme a través de los documentos y me sorprendieron las solicitudes de Docker que estaban esperando respuestas. :)

Sigo dando vueltas en círculos con las razones por las que necesito --ctdir ORIGINAL . Por un lado, la explicación de @jest tiene mucho sentido y, por otro lado, normalmente nunca tengo que lidiar con paquetes de respuesta, así que ¿por qué debería ser diferente aquí?

Creo que la diferencia es que tengo -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT como la primera regla, por lo que el resto de mis reglas nunca ven ningún paquete de respuesta. En este caso, creo que --ctdir ORIGINAL no es estrictamente necesario, aunque probablemente sería más seguro incluirlo de todos modos.

@jest , ¿ ESTABLISHED,RELATED -j ACCEPT anticipada, por lo que esto hace una diferencia para usted.

@jest Tu publicación fue de gran ayuda, gracias.

¿Su enfoque es necesario solo para las cosas administradas por Docker o para todo? Por ejemplo, mi puerto ssh (22) no tiene nada que ver con la ventana acoplable. ¿Necesito usar -m tcp --dport 22 como de costumbre, o debo usar -m conntrack --ctorigdstport 22 --ctdir ORIGINAL ?

Supongo que su enfoque solo es necesario para el tráfico administrado por la ventana acoplable, ya que esos paquetes se manipulan / natting antes de que lleguen a mí en la tabla de filtros. PERO, soy nuevo en iptables, ¡así que quiero estar seguro de alguien que sepa más que yo!

@ lonix1 Docker agrega las reglas a la cadena DOCKER solo si expone un puerto, y probablemente solo cuando el contenedor se está ejecutando. Si en ninguno de sus contenedores expone el puerto 22, debería tener las reglas de su firewall funcionando sin modificaciones.

@SystemParadox Ha pasado algún tiempo y no tengo acceso a ese servidor para verificar, pero si recuerdo correctamente, había reglas ESTABLISHED,RELATED (administradas por UFW, en la cadena ufw-before-input ). Sin embargo, en mi caso, no coincidirían en el primer paquete (SYN) de conexiones realizadas desde la ventana acoplable a los hosts de Internet en el puerto 80. Y la regla correspondiente los eliminaría en DOCKER-USER cuando no hubiera --ctdir .

@ Ionix1 , los paquetes para servicios en el host solo pasan por INPUT, mientras que los paquetes para servicios de Docker solo pasan por FORWARD y DOCKER-USER.

Por ejemplo, dada una IP externa de 10.0.0.1 y dos contenedores con -p 4000:80 y -p 4001:80 , verá paquetes con las siguientes propiedades:

INPUT:
dst 10.0.0.1 dport 80 ctorigdst 10.0.0.1 ctorigdstport 80
FORWARD/DOCKER-USER:
dst 172.17.0.5 dport 80 ctorigdst 10.0.0.1 ctorigdstport 4000
dst 172.17.0.6 dport 80 ctorigdst 10.0.0.1 ctorigdstport 4001

Por lo tanto, podría usar --dport 80 para las reglas INPUT, ya que están en cadenas totalmente separadas. Como puede ver, --ctorigdstport 80 aún coincidiría, pero a menos que también esté alterando la entrada por alguna razón, probablemente no haría eso.

También puede notar que en realidad podría usar --dport 80 con --dst 172.17.0.5 para filtrar paquetes para un contenedor docker específico, pero esa IP no es predecible, por lo que estamos usando --ctorigdstport .

En última instancia, debe saber qué paquetes puede coincidir con una regla determinada, según la cadena en la que se encuentre, cuál es el destino y si se está produciendo alguna alteración.

@jest gracias, eso parece confirmar mi pensamiento.

Entonces, necesito una pequeña dirección ... para cambiar de UFW a iptables ...
-¿Sólo apago UFW a través de "deshabilitar ufw"?
-¿Creo mi propio archivo .sh o hay uno existente (soy Ubuntu en DigitalOcean)?
-Sólo necesito decirle a Docker "--iptables = false"? (¿eso es todo para Docker?)
-DOCKER-USER ya está creado y encadenado por Docker?

@fredjohnston puede continuar usando UFW si lo desea. Los perfiles se almacenan en /etc/ufw . El problema aquí es que Docker no se mostrará porque no hay un perfil de aplicación listado en /etc/ufw/applications.d (lo mismo para cualquier otra herramienta de firewall y su configuración).

Deshabilitar IPTables en Docker significa que no tendrá muchas redes en Docker, solo las direcciones IP y los contenedores no podrán comunicarse entre sí. DOCKER_USER es un truco para darle algo de control, pero realmente no resuelve el problema, que en realidad se trata de no hacer públicos los contenedores Docker en la red de forma predeterminada, pero bloqueados a la dirección IP del contenedor.

Por el momento, le recomiendo que continúe utilizando la herramienta de firewall con la que se sienta más cómodo (ufw, etc.), pero tenga en cuenta que los contenedores Docker serán públicos en su red.

Ya que estoy aquí de cualquier manera, en realidad me encuentro con un problema relacionado ahora donde esto también es un problema en plataformas distintas de Linux. Considera lo siguiente:

  • El proyecto A tiene dos contenedores, uno para una base de datos y otro para una aplicación.
  • El proyecto B tiene dos contenedores, uno para una base de datos y otro para una aplicación.
  • Ambos proyectos están aislados entre sí (repositorios de origen separados, configuraciones, etc.)
  • Ambos proyectos se gestionan bajo Docker Compose
  • Ambos proyectos exponen sus puertos de base de datos con fines de desarrollo local.
  • Ambos proyectos usan el mismo servidor de base de datos (postgres, mysql, etc.)

Ahora suponga que desea ejecutar ambos proyectos localmente, por ejemplo, trabajar en una biblioteca compartida que usan ambos proyectos para que pueda incorporarla fácilmente en su código para realizar pruebas.

Bajo el diseño de interacción de firewall actual, y lo que en parte conduce a los problemas anteriores sobre la exposición del contenedor a la red pública sin el conocimiento del usuario, las ventanas acoplables de base de datos de ambos proyectos no se pueden ejecutar al mismo tiempo ya que estarán en disputa por el puerto expuesto para la base de datos. Lo mismo si quisiera exponer ambos puertos de aplicación y ambos usaron el mismo puerto para el servidor de aplicaciones, algo común ya que las API y aplicaciones basadas en HTTP son extremadamente comunes ahora, especialmente en aplicaciones orientadas a la nube.

Seguro que puedes hackear tu camino para configurar ambos en un contenedor de base de datos; pero no los está aislando según el diseño de su proyecto, y debe tener aún más cuidado con las configuraciones, etc.

En una solución adecuada, aquí hay dos partes:

  1. Los contenedores solo estarían vinculados a sus direcciones IP y sus puertos expuestos solo estarían vinculados a sus direcciones IP dentro de sus respectivas redes Docker, no en el sistema coincidirían con todas las direcciones IP ( 0.0.0.0 , :: ).
  2. Docker tampoco expondría públicamente la ruta a las redes Docker fuera del sistema de forma predeterminada. Docker Networking se puede utilizar para establecer conexiones entre redes (red de docker a red de docker) como se diseñó actualmente, y luego también permitir conexiones de host locales de forma predeterminada.
  3. Los usuarios estarían en el gancho para agregar las reglas de firewall apropiadas para exponer el contenedor al mundo exterior cuando y si lo desean, por ejemplo, mediante el reenvío de puertos del puerto 443 al puerto 443 de su contenedor de elección.

Nuevamente, esto podría hacerse gradualmente:

  • Versión 1: Implementar los pasos 1 y 2; pero agregue también enrutamiento que no sea localhost (temporalmente) con una advertencia. Se mantiene el comportamiento actual del orden de llegada para obtener un puerto; y se emite una advertencia sobre la desaparición de este comportamiento.
  • Release 1 + N: elimine la advertencia y elimine el enrutamiento de host no local. Exija el Paso 3 para los usuarios que deseen que Docker exponga los puertos fuera del sistema y asegúrese de que esté bien documentado.

DOCKER_USER es un truco para darle algo de control, pero en realidad no resuelve el problema, que en realidad se trata de no hacer públicos los contenedores Docker en la red de forma predeterminada, pero bloqueados a la dirección IP del contenedor.

Por el momento, le recomiendo que continúe utilizando la herramienta de firewall con la que se sienta más cómodo (ufw, etc.), pero tenga en cuenta que los contenedores Docker serán públicos en su red.

Hay una solicitud de actualización del documento aquí https://github.com/docker/docker.github.io/pull/8357 para crear una configuración estática de iptables atornillada, pero no estoy seguro del estado de la misma.

Editar: Me di cuenta de que no entendiste el significado de DOCKER-USER. No se usa para "hacer que los contenedores estén bloqueados a la dirección IP del contenedor", sino para filtrar el acceso al contenedor con reglas de iptables.

@ aki-k por DOCKER_USER Soy consciente de que DOCKER_USER no bloquea el contenedor contra la dirección IP y nunca afirmó que lo hiciera. DOCKER_USER simplemente entrega el problema de seguridad al usuario para que lo administre, lo que significa que el usuario debe saber cómo manipular su firewall para asegurarse de que realmente tiene un entorno seguro. Convertirlo en un problema para el usuario es igualmente inaceptable porque la mayoría de los usuarios no saben cómo administrar su cortafuegos; las reglas de cortafuegos son difíciles, e incluso aquellos de nosotros que sabemos un poco sobre cómo escribir reglas de cortafuegos a menudo podemos equivocarnos.

Mi solicitud, y el punto principal del problema, es que Docker debe ser seguro de forma predeterminada y no exponer los contenedores al mundo exterior sin el conocimiento del usuario o la intervención explícita para hacerlo. Y según mi último comentario (https://github.com/moby/moby/issues/22054#issuecomment-552951146) hacerlo también tendrá otros beneficios técnicos.

Mi solicitud, y el punto principal del problema, es que Docker debe ser seguro de forma predeterminada y no exponer los contenedores al mundo exterior sin el conocimiento del usuario o la intervención explícita para hacerlo.

El primer informe de problemas que encontré sobre esto es del 18 de marzo de 2014:

https://github.com/moby/moby/issues/4737

Probablemente sea una decisión de diseño que no quieren cambiar e intentar arreglar con la cadena de iptables DOCKER-USER. Puede usar la opción -p para que la ventana acoplable se ejecute para publicar el puerto solo en el host de la ventana acoplable (-p 127.0.0.1:port:port) pero eso tampoco resuelve el problema.

Seamos claros, Docker es seguro por defecto. Tienes que decirle a Docker que realice el reenvío de puertos.

En cuanto a los servicios que necesitan comunicarse entre sí, debe usar redes ( docker network ) para limitar el acceso, no el reenvío de puertos.

Seamos claros, Docker _es_ seguro por defecto. Tienes que decirle a Docker que realice el reenvío de puertos.

No descarte un problema fáctico con la ventana acoplable que se ejecuta sobre su protección iptables establecida. Estoy seguro de que ha visto los innumerables informes de problemas que discuten este tema.

Seamos claros, Docker _es_ seguro por defecto. Tienes que decirle a Docker que realice el reenvío de puertos.

En cuanto a los servicios que necesitan comunicarse entre sí, debe usar redes ( docker network ) para limitar el acceso, no el reenvío de puertos.

Seamos claros: Docker es seguro hasta que desee acceder a él fuera de la red de Docker. Una vez que desee acceder a él incluso desde el host local (127.0.0.1), Docker no es seguro de forma predeterminada, ya que se une a 0.0.0.0 y expone los contenedores fuera del sistema, omitiendo cualquier cosa que el usuario haga para proteger su sistema, sin aparecer. en herramientas que no sean el uso directo de iptables . DOCKER_USER no es ni será nunca una solución adecuada porque requiere que el usuario sepa demasiado sobre el sistema de firewall subyacente (iptables en Linux, lo que sea que use Mac y el Firewall de Windows en Windows). Eso todavía debe ser seguro de forma predeterminada. Hay una manera muy fácil de hacerlo, como describí anteriormente en https://github.com/moby/moby/issues/22054#issuecomment -552951146 y he llamado varias veces a lo largo de estos problemas (aunque quizás no tan claramente como en ese comentario).

Tenga en cuenta también que la documentación para exponer un puerto tampoco menciona este problema de seguridad, sin hacer referencia a cómo Docker interactúa con el firewall del sistema al exponer un puerto, lo que reduce la seguridad en los sistemas que habían sido diseñados para ser seguros hasta que se utilizó Docker. .

Seamos claros, Docker _es_ seguro por defecto. Tienes que decirle a Docker que realice el reenvío de puertos.
En cuanto a los servicios que necesitan comunicarse entre sí, debe usar redes ( docker network ) para limitar el acceso, no el reenvío de puertos.

Seamos claros: Docker es seguro hasta que desee acceder a él fuera de la red de Docker. Una vez que desee acceder a él incluso desde el host local (127.0.0.1), Docker es _no_ seguro de forma predeterminada, ya que se vincula con 0.0.0.0 [...]

Para ser precisos, Docker no se vincula a 0.0.0.0. Esa es la expectativa (razonable) del usuario: que especificar --publish en CLI, por lo tanto, operar en el espacio de usuario, inicia algún tipo de demonio proxy que escucha en el puerto especificado, acepta conexiones entrantes y empuja hacia adelante y hacia atrás todos los paquetes entre Docker y el mundo exterior.

Pero en cambio, Docker inyecta reglas mágicas de DNAT / masquarading en el firewall para reescribir las direcciones en el paquete, rompiendo así al azar cualquier sistema de reglas preinstalado.

En mi opinión, alterar los niveles de abstracciones aquí es el mayor fastidio y confunde al usuario. No sé qué escenarios consideró el equipo de Docker al diseñar la maquinaria --publish que vemos aquí, no veo ninguno que justifique la decisión (tal vez además de las razones de rendimiento).

Seamos claros, Docker es seguro por defecto. Tienes que decirle a Docker que realice el reenvío de puertos.

... que resulta ser una de las funciones más utilizadas de Docker. Efectivamente, acaba de afirmar que una anulación no documentada de las reglas de firewall

Bueno, supongo que lo que sea que funcione para ti. Como no me funciona, empezaré a buscar plataformas de contenedores alternativas. Los problemas de seguridad se pueden solucionar, pero el desprecio flagrante de las expectativas de seguridad razonables es una bestia diferente.

Evaluemos la situación tal como es hoy:

De forma predeterminada, no se exponen puertos. Tienes que decirle a Docker que exponga un puerto.
En el pasado, Docker configuraba iptables de manera que cualquier cosa que supiera cómo enrutar a la red puente pudiera acceder a las IP del contenedor (estableciendo la política de reenvío en "aceptar"), pero esto ya no es cierto.

He visto a algunas personas decir que están usando -p para exponer servicios entre sí, lo que no debería ser necesario.
En la red predeterminada, puede usar --link para conectar los servicios juntos, el contenedor está disponible a través de DNS.
En redes no predeterminadas (redes definidas por el usuario), los contenedores también pueden acceder entre sí mediante DNS, incluida la configuración de alias a través de --link , o incluso a una red con un alias específico.

Parece que en muchos casos realmente solo desea conectarse al servicio desde el cliente, en cuyo caso se recomienda usar otro contenedor con acceso al servicio al que desea conectarse en lugar de exponer un puerto.

-p está diseñado específicamente para la entrada, ya que permite que las cosas externas accedan a este servicio.
El valor predeterminado para -p es permitir el tráfico desde cualquier lugar. Puede cambiar esto especificando manualmente la dirección para permitir desde por -p o como una configuración para todo el demonio.
Dado que -p usa iptables, la cadena DOCKER-USER se creó para que los usuarios puedan agregar sus propias reglas de filtrado antes de que llegue al contenedor.

Dado que -p está diseñado para la entrada, creo que es razonable que esto exponga el tráfico como lo hace. Siento que es desafortunado que las reglas de Docker se inserten en la parte superior de la tabla de filtros; sin embargo, cambiar esto sería un cambio radical para un gran grupo de usuarios que SÍ desean este comportamiento.

Hay un par de otras alternativas a -p :

  1. No use -p , conéctese directamente a la IP del contenedor. Esto requiere un poco de trabajo adicional ya que debe buscar la IP, pero estos datos están disponibles en la API. También deberá asegurarse de que la política de reenvío en el firewall lo permita (asumiendo que se está conectando desde un host diferente, el mismo host debería estar bien)
  2. Utilice la red macvlan o ipvlan para los servicios a los que desee acceder desde la red del host (es decir, el ingreso). Estas opciones de red le dan al contenedor una IP directamente desde la interfaz de red del host (usted elige a qué interfaz está vinculado).
  3. Use --net=host , esto ejecuta el servicio en el espacio de nombres de la red del host que le da acceso al servicio a la infraestructura de red que ya existe en el host.

Dice "hacer esto seguro de forma predeterminada", pero exponer un puerto es, por definición, una acción potencialmente insegura. También parece haber una idea de que exponer solo a localhost es seguro, pero no es que nada que se ejecute en el host pueda acceder a localhost (incluido javascript en un navegador si es un escritorio).

¿Qué caso de uso está tratando de resolver usando -p ?
¿Tiene algunas ideas sobre el cambio real que le gustaría ver?

Estoy bien cambiando algo para mejorar esto para su flujo de trabajo, pero hay muchos casos de uso diferentes aquí y un tamaño nunca sirve para todos (vea las quejas sobre -p ).

@ cpuguy83 todo está bien, pero no cambia nada en esta solicitud.

Las personas usan la ventana acoplable y quieren conectarse a aplicaciones que se ejecutan en la ventana acoplable desde su sistema local; este es un caso de uso extremadamente común para los desarrolladores, especialmente cuando intentan diagnosticar algo o escribir un servicio que no está en la ventana acoplable pero necesita conectarse a servicios alojados en la ventana acoplable. (para completar un entorno de desarrollo). Desarrollar con Docker no siempre es una experiencia buena, divertida o útil, por lo que dice:

se recomienda utilizar otro contenedor con acceso al servicio al que desea conectarse en lugar de exponer un puerto.

Es simplemente un no-go. No todas las herramientas son compatibles con Docker, ni deberían tener que serlo. No se debe exigir al usuario que esté completamente en Docker para utilizar los servicios que se ejecutan en Docker. Incluso entonces, si se usa Docker para operar servidores, el administrador no puede controlarlos fácilmente a través de la configuración del firewall y la funcionalidad del puerto de exposición, sin embargo, está orquestada (línea de comandos, Dockerfile, Docker Compose Config) está completamente rota.

Además, la gente usa Docker Compose para administrar gran parte del entorno y especifica a través de docker-compose.yml o Dockerfile que un puerto debe estar expuesto para que puedan acceder a él localmente. Por lo tanto, decir el parámetro use the -p es incorrecto, ya que nunca interactúan con el comando de la ventana acoplable directamente de tal manera que eso funcione.

Exponer un puerto no significa que deba romperse la seguridad. He descrito cómo puede exponer un puerto al sistema local sin romper la seguridad (https://github.com/moby/moby/issues/22054#issuecomment-552951146) y pondría la gestión de la exposición externa (desactivada sistema) en manos del usuario de una manera que pueda controlar fácilmente con sus herramientas existentes.

Solución:

  • Usar la red Docker
  • Exponga los puertos en la red Docker solo y el host local
  • Deje de vincular el puerto en 0.0.0.0, o de hacerlo de manera efectiva
  • Exigir a los usuarios que utilicen sus propias herramientas de firewall para exponer el puerto fuera del sistema (ufw, firewalld, etc.)
  • Proporcione integraciones para firewalls comunes para facilitar esto

En otras palabras, en lugar de acceder a un servicio en un contenedor docker a través de 127.0.0.1:<port> requiera <docker container ip>:<service port> incluso desde el host local. Si las personas desean exponer el servicio fuera del sistema, pueden agregar una regla de firewall a través de sus herramientas (ufw, etc.) para reenviar el puerto desde un puerto determinado a <docker container ip>:<service port> .

Alternativamente, siga el enfoque de Kubernetes con su diseño Proxy, haciendo efectivamente lo que @jest sugirió en https://github.com/moby/moby/issues/22054#issuecomment -554665865. Nuevamente, esto aún tendría que ser un sistema local solo hasta que el usuario lo exponga intencionalmente a la red externa.

El mayor problema aquí es que Docker está comprometiendo la integridad del firewall para proporcionar sus servicios, sin decirle nada al usuario y sin integrarse en las herramientas del usuario para que el usuario no lo sepa ni pueda controlarlo.

Me doy cuenta de que hay un montón de interfaces de firewall en el mercado, incluso con iptables hay firewalld, ufw y una docena más. Realmente no espero que Docker se integre con ellos (aunque eso sería bueno), pero sí espero que Docker funcione de una manera que no los omita ni rompa la seguridad que el usuario configuró.

Como caso de prueba básico:

  • Configurar un servidor basado en Debian (Debian, Ubuntu)
  • Instale ufw, servidor OpenSSH
  • ejecutar ufw allow OpenSSH
  • ejecutar ufw enable

En este punto, tiene un servidor bastante seguro; el único tráfico permitido es (a) relacionado con el tráfico saliente o (b) tráfico del servidor SSH.

Ahora, inicie un contenedor docker con un puerto expuesto.

Una solución aceptable cumpliría lo siguiente:

  • El puerto no debe ser accesible desde otra computadora; el cortafuegos debería seguir bloqueándolo de los sistemas externos.
  • El puerto debe ser accesible desde la computadora local (localhost).
  • Varios contenedores Docker deberían poder exponer el mismo puerto y hacer que todo funcione; todos los contenedores con un puerto expuesto deben ser accesibles solo desde el sistema local.
  • El usuario no debería tener que conocer los detalles de configuración de su firewall para que esto suceda.

Intente lo mismo para un sistema basado en CentOS / Fedora / RHEL con firewalld .

@ cpuguy83 En mi opinión, la gente espera que -p funcione a nivel de aplicación. Es decir, si -p 80:80 , espero el comportamiento como si la aplicación vinculada al puerto 80 en el contenedor se estuviera ejecutando en el host.

Los puertos del modelo VirtualBox o SSH se reenvían de tal manera, por lo que la gente asume que lo mismo ocurre en el caso de Docker.

Para utilizar un paralelo de expectativas más amplio: desde el punto de vista del usuario, debería funcionar como volúmenes vinculados al host. Simplemente apunte al directorio del host, y "como magia" es visible dentro del contenedor; cualquier modificación realizada al sistema de archivos desde el otro lado funciona igual, incluidos los permisos, la cuota, etc.

Reduciendo los problemas de -p a los casos de firewall: en el mundo normal, el usuario espera que una aplicación vinculada a 0.0.0.0:80 sea visible para el mundo exterior. Si el usuario desea limitar el acceso, se le instruye en numerosas guías para usar un firewall y configurar una regla en la cadena INPUT :

-P INPUT DROP
-A INPUT -s 1.2.3.4/32 -p tcp --dst-port 80 -j ACCEPT

o use herramientas como UFW:

ufw enable
ufw allow http

Pero con Docker, el usuario debe poder construir tales reglas de la nada:

-A DOCKER-USER -s 1.2.3.4/32 -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j ACCEPT
-A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 80 --ctdir ORIGINAL -j DROP

Por favor, muéstreme una guía completa para un usuario promedio sobre cómo resolver escenarios tan comunes con Docker.

Y para, por ejemplo, un desarrollador medio, a quien le ha estado vendiendo todo el concepto de "contenedorización de aplicaciones", las consecuencias de exponer un puerto son simplemente impredecibles.

En estos días, simplemente no expongo los puertos debido a mi falta de conocimiento, y en su lugar utilizo servicios como Traefik. Tal vez --expose tampoco debería proponerse como un propósito general en los documentos CLI, porque en mi opinión, simplemente no sirve de nada para un usuario promedio.

O bien, proporcione a cualquier persona sin un conocimiento profundo de estas herramientas una computadora portátil Linux, como Dell o Lenovo, y sea testigo de cómo sus auténticos esfuerzos para configurar correctamente sus firewalls no hacen absolutamente ninguna diferencia cuando alguien logra acceder a su base de datos local mientras tomando un café en Starbucks.

Este problema exacto nos causó una vulnerabilidad del sistema que descubrí durante el fin de semana. Según @jacoscaz , pude acceder a la base de datos local de otro desarrollador porque tiene un puerto publicado. Sería genial si todos pudieran incluir esto en la documentación de introducción para que otros no hagan lo mismo. Necesitamos un servicio no contenedorizado para conectarnos a un contenedor sin que todos los demás en la red obtengan acceso, por lo que las redes Docker están fuera de discusión. Parece que la mejor opción por ahora es conectarse a la IP del contenedor local, a menos que alguien tenga una idea mejor.

@dentonmwood Eso suena exactamente como lo que debería hacer. Como mencioné anteriormente, incluso puede configurar el servicio para que se ejecute usando macvlan o ipvlan, lo que le dará una IP directamente en su red normal.

@BenjamenMeyer @jest @jacoscaz

Gracias por los comentarios adicionales.

Estoy de acuerdo en que el conocimiento que estamos pidiendo a los usuarios que posen en este sentido no es muy bueno. Actualmente ponemos la responsabilidad en el usuario (ya sea un administrador de sistemas o un desarrollador) de comprender lo que hace -p y de tomar las precauciones adecuadas para garantizar que el servicio solo esté expuesto a quienes creen que está expuesto. Llevamos eso un paso más allá incluso esperando que los desarrolladores que a menudo no tienen ninguna razón para conocer iptables intervengan y lo arreglen para su propio entorno (a través de DOCKER-USER ).
Básicamente estamos en esta situación por temor a romper la compatibilidad.

Tengo algunas ideas que aún no he tenido tiempo de analizar por completo, pero básicamente cambiaría el comportamiento de -p según la versión API del cliente y manejaría el ingreso por separado. Sigue siendo un cambio importante que me preocupa, pero al menos el comportamiento antiguo se conserva en las versiones anteriores de la API.

Pensé que podríamos hacer un proxy local (ala kubectl proxy ) para el caso de uso del acceso local, sin embargo, esto nuevamente pone la responsabilidad en el desarrollador de saber y comprender más de lo que realmente debería necesitar. .

¿Pensamientos?

Creo que la introducción de proxy es un paso en la buena dirección.

Reconozco que el "reenvío de puertos" es una responsabilidad compartida entre un contenedor y un orquestador, similar en espíritu al modo Swarm. Pero siempre se requiere la mínima integración de host y cuando se le da una opción como -p al usuario (no experto), la solución propuesta debe ser comprensible para dicha persona.

Como las consecuencias de -p para el entorno no están especificadas (al menos en el nivel de CLI, creo), existe la posibilidad de introducir una configuración fácil de usar de la forma en que funciona.

Por ejemplo, en este momento hay iptables en /etc/docker/daemon.json que determina si manipular DOCKER o no. La configuración podría extenderse con otra entrada, de modo que una combinación como iptables==true && expose_with_network_proxy==true activaría el comportamiento similar al proxy.

Esto debería ser generalmente seguro para nuevas instalaciones, ya que prefería overlay2 aufs algún tiempo (si mal no recuerdo). Y se implementa fácilmente como tal, ya que las actualizaciones no afectan los archivos de configuración existentes, pero las nuevas instalaciones son gratuitas.

Básicamente estamos en esta situación por temor a romper la compatibilidad.

Realmente me gustaría enfatizar el hecho de que la gravedad de este problema, al menos en mi opinión, tiene más que ver con la falta de documentación que con los fundamentos técnicos actuales de -p . Sí, no creo que se pueda decir que el mecanismo actual sea _seguro_, pero las fallas de seguridad se pueden arreglar y no estoy en condiciones de criticar las razones que llevaron al escenario actual. La retrospectiva siempre es 20/20, después de todo, y admiro el compromiso con la compatibilidad con versiones anteriores y la voluntad de enfrentar los desafíos técnicos que conlleva ese compromiso.

Lo que no entiendo es por qué todavía no hay cuadros rojos grandes y brillantes en la documentación de Docker que advierten explícitamente sobre los efectos secundarios del uso de -p . Si me hubiera encontrado con tal advertencia, no creo que ni siquiera estaría aquí en primer lugar.

¿Pensamientos?

La opción de proxy parece razonable. Además, me gusta la idea de poder exponer y no exponer puertos cuando sea necesario en lugar de estar obligado a hacerlo mientras se lanza docker run .

@BenjamenMeyer @jest @jacoscaz

Gracias por los comentarios adicionales.

Gracias por finalmente mirar esto más de cerca.

Estoy de acuerdo en que el conocimiento que estamos pidiendo a los usuarios que posen en este sentido no es muy bueno. Actualmente ponemos la responsabilidad en el usuario (ya sea un administrador de sistemas o un desarrollador) de comprender lo que hace -p y de tomar las precauciones adecuadas para garantizar que el servicio solo esté expuesto a quienes creen que está expuesto. Llevamos eso un paso más allá incluso esperando que los desarrolladores que a menudo no tienen ninguna razón para conocer iptables intervengan y lo arreglen para su propio entorno (a través de DOCKER-USER ).
Básicamente estamos en esta situación por temor a romper la compatibilidad.

Tengo algunas ideas que aún no he tenido tiempo de analizar por completo, pero básicamente cambiaría el comportamiento de -p según la versión API del cliente y manejaría el ingreso por separado. Sigue siendo un cambio importante que me preocupa, pero al menos el comportamiento antiguo se conserva en las versiones anteriores de la API.

¿Pensamientos?

  1. Comience actualizando la documentación para que los usuarios conozcan el efecto de EXPOSE de todos los métodos (-p, Dockerfile, docker-compose.yml). Eso se puede hacer rápidamente. También puede ayudar a llamar la atención sobre el problema para que más personas ofrezcan asesoramiento / experiencia para encontrar una buena solución y ayuden a responder si también existe un deseo de la comunidad de compatibilidad con versiones anteriores.
  2. Planifique un método para llegar desde donde está Docker ahora hasta donde debe estar Docker. Esto puede incluir un cambio radical en algún momento.

Creo que la gravedad de la situación ciertamente permite un cambio radical para solucionarlo; simplemente no lo hagas sin informar a la gente. Establezca un cronograma (¿6 meses? ¿12 meses?) En el que se debata mucho el cambio, etc. y prepare a la comunidad; luego, en una versión "principal", realice el cambio. Según el esquema de su versión, parece que el primer conjunto es el año (19); dado que estamos a fines de 2019, use el resto de 2019 y luego 2020 para encontrar la solución y publicitarla; presentarlo como una característica opcional en algún momento de 2020 y luego promoverlo al primer lugar / uso predeterminado en 2021.

La versión de Dockerfile y la versión de esquema de composición de Docker también pueden ser buenas herramientas en lo que respecta a establecer un comportamiento predeterminado, pero no bloquearía las versiones anteriores para que no puedan aprovecharlo, y pondría una fuerte advertencia sobre la necesidad de actualizar en tal caso también.

Independientemente de cómo se implemente, creo que la comunidad en general apoyará mucho más tal cambio si entienden por qué y qué está sucediendo y no sienten que los ha tomado por sorpresa.

Pensé que podríamos hacer un proxy local (ala kubectl proxy ) para el caso de uso del acceso local, sin embargo, esto nuevamente pone la responsabilidad en el desarrollador de saber y comprender más de lo que realmente debería necesitar. .

Me gusta la idea del proxy, y la sugerencia de @jest de cómo habilitarlo también puede ser un gran método para realizar la migración.

Honestamente, la mayoría de las personas que buscarán exponerlo fuera del sistema estarán o deberían estar familiarizados con los firewalls hasta cierto punto, incluso si solo se trata de cómo configurar UFW o firewalld para hacerlo. Entonces, si la solución solo la pone a disposición del host local, creo que es aceptable que la gente aprenda a usar sus herramientas de firewall para hacer un reenvío de puertos a través del firewall.

Creo que es importante que dicha funcionalidad se realice a través de las herramientas de firewall que el usuario ha decidido usar, ya que hará que todo sea visible para ellos. Dicha funcionalidad no debe pasar por alto sus herramientas. También me doy cuenta de que existen tantas herramientas de firewall que no es razonable que Docker se integre con todas. Entonces, sugeriría tomar una ruta de documentación y resaltar cómo hacerlo con algunos de los más populares para comenzar, y dejar que la comunidad agregue más y los actualice.

Me gusta la idea del proxy, y la sugerencia de @jest de cómo habilitarlo también puede ser un gran método para realizar la migración.

Honestamente, la mayoría de las personas que buscarán exponerlo fuera del sistema estarán o deberían estar familiarizados con los firewalls hasta cierto punto, incluso si solo se trata de cómo configurar UFW o firewalld para hacerlo. Entonces, si la solución solo la pone a disposición del host local, creo que es aceptable que la gente aprenda a usar sus herramientas de firewall para hacer un reenvío de puertos a través del firewall.

Me hago eco de esto. Con la solución de proxy, creo que sería factible idear algo que permitiera a los usuarios vincular el proxy a cualquier combinación de interfaz (s) y puerto (s) que quisieran. Sin embargo, si se ve obligado a comprometerse en nombre de la compatibilidad con versiones anteriores (¡lo que yo apoyo!) O por cualquier otro motivo, se debe dar prioridad para cumplir con las expectativas de seguridad razonables, incluso a costa de delegar la exposición fuera del sistema a los usuarios.

Además de mi comentario, el proxy podría funcionar de diferentes maneras dependiendo de las herramientas de firewall disponibles. Si se detecta una herramienta de configuración de firewall compatible, el proxy podría usarla para establecer las reglas de reenvío adecuadas. La presencia de una herramienta de este tipo podría agregarse como un requisito para los entornos de producción. Si dicha herramienta no estuviera disponible, el proxy generaría de forma predeterminada un servidor proxy a nivel de aplicación.

Docker omite el firewall de macOS de la misma manera que lo hace en Linux.

En mi máquina de desarrollo (Docker Desktop para Mac) agregué "ip": "127.0.0.1" a la configuración del demonio Docker. De esta manera, cualquier base de datos de desarrollo, etc., será accesible de forma predeterminada solo desde localhost. Para esos casos raros en los que necesito hacer que una aplicación sea visible para otros, puedo publicarla explícitamente con -p 0.0.0.0:8080:8080 .

Editar: parece que Docker Compose todavía se vincula a 0.0.0.0 de forma predeterminada, independientemente de la configuración del demonio de Docker. Entonces, este truco solo funciona cuando se ejecuta Docker desde la línea de comandos. Y de todos modos, dado que los archivos de Redacción a menudo se comparten con compañeros de equipo que pueden tener una configuración de sistema diferente, es mejor agregar 127.0.0.1 explícitamente al archivo de Redacción.

@luontola Qué elegante solución.

Editar: Lo que también estaba pensando era quizás una forma de tener una lista de direcciones permitidas / bloqueadas que podrían definirse, por ejemplo, en Docker compose que luego se agregaría automáticamente a la cadena de iptables DOCKER-USER.

Una pequeña margarita que destaca la importancia de este problema: https://github.com/docker/for-linux/issues/810 (resumen: DOCKER-USER se eliminó, por lo que incluso si configuró iptables para bloquear el acceso externo de los dockers, ignoraría esa configuración adicional y expondría todos los contenedores)

Los comentarios de

¡Hmm lectura interesante!

Estoy ejecutando Mac OS X y también tengo un firewall local llamado LittleSnitch. Simplemente apareció un cuadro de diálogo preguntando si estaba bien que 185.156.177.252 se conectara al proceso com.docker.backend. Hice clic en negar.

Me preguntaba cómo podría abrir un puerto hacia adelante en mi enrutador, pero me acabo de dar cuenta de que esto, por supuesto, se hace a través de UPnP. Todos los enrutadores admiten esto.

Lo que me pregunto ahora es el por qué. ¿Por qué algo de 185.156.177.252 intenta conectarse desde el exterior? Si mi proceso local de Docker necesita algo, debería llamar a casa desde el interior, no abrir un puerto en el exterior.

@ cpuguy83 todo está bien, pero no cambia nada en esta solicitud.

Las personas usan la ventana acoplable y quieren conectarse a aplicaciones que se ejecutan en la ventana acoplable desde su sistema local; este es un caso de uso extremadamente común para los desarrolladores, especialmente cuando intentan diagnosticar algo o escribir un servicio que no está en la ventana acoplable pero necesita conectarse a servicios alojados en la ventana acoplable. (para completar un entorno de desarrollo). Desarrollar con Docker no siempre es una experiencia buena, divertida o útil, por lo que dice:

se recomienda utilizar otro contenedor con acceso al servicio al que desea conectarse en lugar de exponer un puerto.

Es simplemente un no-go. No todas las herramientas son compatibles con Docker, ni deberían tener que serlo. No se debe exigir al usuario que esté completamente en Docker para utilizar los servicios que se ejecutan en Docker. Incluso entonces, si se usa Docker para operar servidores, el administrador no puede controlarlos fácilmente a través de la configuración del firewall y la funcionalidad del puerto de exposición, sin embargo, está orquestada (línea de comandos, Dockerfile, Docker Compose Config) está completamente rota.

Además, la gente usa Docker Compose para administrar gran parte del entorno y especifica a través de docker-compose.yml o Dockerfile que un puerto debe estar expuesto para que puedan acceder a él localmente. Por lo tanto, decir el parámetro use the -p es incorrecto, ya que nunca interactúan con el comando de la ventana acoplable directamente de tal manera que eso funcione.

Exponer un puerto no significa que deba romperse la seguridad. He explicado cómo puede exponer un puerto al sistema local sin romper la seguridad ( # 22054 (comentario) ) y pondría la gestión de la exposición externa (fuera del sistema) en manos del usuario de una manera que pueda fácilmente controlar con sus herramientas existentes.

Solución:

* Use Docker Network

* Expose the Ports on the Docker Network alone and local host

* Stop binding the port on 0.0.0.0 - or effectively doing so

* Require users to use their own firewall tooling to expose the port off system (ufw, firewalld, etc)

* Provide integrations for common firewalls to make this easy

En otras palabras, en lugar de acceder a un servicio en un contenedor docker a través de 127.0.0.1:<port> requiera <docker container ip>:<service port> incluso desde el host local. Si las personas desean exponer el servicio fuera del sistema, pueden agregar una regla de firewall a través de sus herramientas (ufw, etc.) para reenviar el puerto desde un puerto determinado a <docker container ip>:<service port> .

Alternativamente, siga el enfoque de Kubernetes con su diseño Proxy, haciendo efectivamente lo que @jest sugirió en # 22054 (comentario) . Nuevamente, esto aún tendría que ser un sistema local solo hasta que el usuario lo exponga intencionalmente a la red externa.

El mayor problema aquí es que Docker está comprometiendo la integridad del firewall para proporcionar sus servicios, sin decirle nada al usuario y sin integrarse en las herramientas del usuario para que el usuario no lo sepa ni pueda controlarlo.

Me doy cuenta de que hay un montón de interfaces de firewall en el mercado, incluso con iptables hay firewalld, ufw y una docena más. Realmente no espero que Docker se integre con ellos (aunque eso sería bueno), pero sí espero que Docker funcione de una manera que no los omita ni rompa la seguridad que el usuario configuró.

Como caso de prueba básico:

* Setup a Debian-based server (Debian, Ubuntu)

* Install ufw, OpenSSH Server

* run `ufw allow OpenSSH`

* run `ufw enable`

En este punto, tiene un servidor bastante seguro; el único tráfico permitido es (a) relacionado con el tráfico saliente o (b) tráfico del servidor SSH.

Ahora, inicie un contenedor docker con un puerto expuesto.

Una solución aceptable cumpliría lo siguiente:

* The port should not be accessible from another computer; the firewall should continue to block it from outside systems.

* The port should be accessible from the local computer (localhost).

* Multiple docker containers should be able to expose the same port and have everything work; all containers with an exposed port should be accessible from the local system only.

* The user should not have to know about their firewall configuration details to make this happen.

Intente lo mismo para un sistema basado en CentOS / Fedora / RHEL con firewalld .

Me encontré con este problema mientras intentaba averiguar si hay una manera de configurar la ventana acoplable para que no omita las reglas de entrada de mi firewall cada vez que se publica un puerto. El comentario anterior hace un buen trabajo al explicar todo el asunto y también es un enfoque más preferible, en mi opinión.

Ayer, tropecé con este tema por accidente. Pasé semanas personalizando meticulosamente las reglas de firewall de iptables para mi VPS. He tratado de ser cauteloso y restrictivo. La entrada y la salida del filtro están configuradas para caer de forma predeterminada. Permito explícitamente cierto tráfico y bloqueo todo lo demás. El tráfico está registrado y he estado probando y monitoreando.

Sabía que Docker hizo sus propios cambios en la cadena Forward y en la tabla NAT. Así que he sido extremadamente cuidadoso en respetar esos cambios a lo largo de mi proceso. Además, todos mis contenedores están conectados a redes definidas por el usuario. La red de host predeterminada nunca se utiliza. La documentación oficial nos dice que no lo hagamos.

Mi primera sorpresa fue que mi Nginx Proxy en contenedor era accesible a toda Internet. Tengo una opción en las reglas de mi firewall para permitir el tráfico web entrante del mundo. Pero todavía no había activado esa función. En ninguna parte de mis iptables es obvio que se permiten HTTP 80/443.

Algunas de mis aplicaciones web se basan en bases de datos como MySQL. Inicialmente, _no_ usé la opción -p en Docker Compose. Sabía que no era necesario, ya que mi aplicación web y el servidor de base de datos compartían la misma red Docker definida por el usuario. Pero como ex DBA, siempre estoy pensando en las copias de seguridad. Así que activé la opción -p para permitir que los trabajos cron y las herramientas de respaldo de mi

Después de mi sorpresa en Nginx, decidí sabiamente volver a verificar que MySQL no estuviera expuesto también. Intenté conectarme (desde mi computadora portátil) a la base de datos MySQL en mi VPS remoto. Y me sorprendió de nuevo cuando me conecté con éxito de inmediato.

Nada de lo que digo no se ha discutido extensamente en publicaciones anteriores. Muchas gracias a @BenjamenMeyer @jest @jacoscaz por su útil investigación y sugerencias. ¿Pero para aquellos que buscan experiencias y casos de uso modernos? Aqui lo tienes. Más de 4 años después de que comenzara este hilo, personas como yo todavía se encuentran con este comportamiento. Y todavía saliendo sintiéndome conmocionado.

Sí, hay muchas soluciones alternativas. Buscaré algunos de inmediato. Pero para implementarlos, primero debe saber que este problema existe. Eso es lo que más me decepciona. No es que los desarrolladores de Docker hayan tomado ciertas decisiones de diseño, ya sean buenas o malas.

Pero que este "comportamiento de omisión del firewall" no se menciona claramente, se advierte en voz alta y se documenta de manera más amplia. Cuando se trata de seguridad de la red, las sorpresas nunca son buenas.

Quería compartir mi solución para este problema en caso de que otros lo encuentren útil. Usando iptables e ipset. Probado en Ubuntu, CentOS 7 y RHEL 7. ipset se usa ya que las direcciones IP "confiables" no siempre están en el mismo rango de direcciones IP (corregir la limitación de iptables).
Funciona con Docker normal y Docker Swarm (también conocido como SwarmKit).
Garantiza que esté seguro de forma predeterminada, solo permite que las IP que especifique para poder conectarse a los puertos del sistema operativo y los puertos Docker (usando iptables e ipset) que especifique deben estar abiertas. También tiene la opción de hacer que un puerto de SO o Docker sea "público" (abierto a todas las direcciones IP)

Ansible no es necesario, pero lo hace más fácil. Función de Ansible: https://github.com/ryandaniels/ansible-role-iptables-docker
Pasos manuales para CentOS / RHEL:
https://github.com/ryandaniels/ansible-role-iptables-docker#manual -commands-centosrhel
Y Ubuntu 18.04 / 20.04:
https://github.com/ryandaniels/ansible-role-iptables-docker#manual -commands-ubuntu-2004

Deberá instalar / configurar ipset además de iptables (consulte los enlaces anteriores si tiene problemas).
Ejemplo de configuración de iptables (con el puerto ssh 22 abierto para todos):

*filter
:DOCKER-USER - [0:0]
:FILTERS - [0:0]
#Can't flush INPUT. wipes out docker swarm encrypted overlay rules
#-F INPUT
#Use ansible or run manually once instead to add -I INPUT -j FILTERS
#-I INPUT -j FILTERS
-A DOCKER-USER -m state --state RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -i docker_gwbridge -j RETURN
-A DOCKER-USER -s 172.18.0.0/16 -j RETURN
-A DOCKER-USER -i docker0 -j RETURN
-A DOCKER-USER -s 172.17.0.0/16 -j RETURN
#Below Docker ports open to everyone if uncommented
#-A DOCKER-USER -p tcp -m tcp -m multiport --dports 8000,8001 -j RETURN
#-A DOCKER-USER -p udp -m udp -m multiport --dports 9000,9001 -j RETURN
-A DOCKER-USER -m set ! --match-set ip_allow src -j DROP
-A DOCKER-USER -j RETURN
-F FILTERS
#Because Docker Swarm encrypted overlay network just appends rules to INPUT
-A FILTERS -p udp -m policy --dir in --pol ipsec -m udp --dport 4789 -m set --match-set ip_allow src -j RETURN
-A FILTERS -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FILTERS -p icmp -j ACCEPT
-A FILTERS -i lo -j ACCEPT
#Below OS ports open to everyone if uncommented
-A FILTERS -p tcp -m state --state NEW -m tcp -m multiport --dports 22 -j ACCEPT
#-A FILTERS -p udp -m udp -m multiport --dports 53,123 -j ACCEPT
-A FILTERS -m set ! --match-set ip_allow src -j DROP
-A FILTERS -j RETURN
COMMIT
¿Fue útil esta página
0 / 5 - 0 calificaciones