
Los contenedores Docker son la tecnología de contenedorización más popular. Inicialmente, se usaba principalmente para entornos de desarrollo y prueba, y con el tiempo cambió a producción. Los contenedores Docker comenzaron a reproducirse en el entorno de producción, como los hongos después de la lluvia, pero pocos que usan esta tecnología han pensado en cómo publicar de manera segura los contenedores Docker.
Basado en
OWASP , hemos preparado una lista de reglas, cuya implementación protegerá significativamente su entorno, construido en contenedores Docker.
Regla 0
La máquina host y Docker deben contener todas las actualizaciones actuales.
Para protegerse contra vulnerabilidades conocidas que conducen a escapar del entorno del contenedor al sistema host, lo que generalmente resulta en una escalada de privilegios en el sistema host, es extremadamente importante instalar todos los parches para el sistema operativo host, el motor Docker y la máquina Docker.
Además, los contenedores (a diferencia de las máquinas virtuales) usan el kernel junto con el host, por lo que el exploit del kernel que se ejecuta dentro del contenedor se ejecuta directamente en el kernel del host. Por ejemplo, un exploit de escalada de privilegios del kernel (como Dirty COW) que se ejecuta dentro de un contenedor bien aislado dará como resultado el acceso a la raíz en el host.
Regla 1
No le dé acceso al socket del demonio Docker
El servicio Docker (daemon) usa el socket /var/run/docker.sock de UNIX para las conexiones API entrantes.
El propietario de este recurso debe ser el usuario root. Y de ninguna otra manera. Cambiar los derechos de acceso a este socket es esencialmente equivalente a otorgar acceso de root al sistema host.
Además, no debe manipular el zócalo /var/run/docker.sock con contenedores, donde puede prescindir de él, porque en este caso, comprometer el servicio en el contenedor conducirá a un control completo sobre el sistema host. Si tiene contenedores que usan algo como esto:
-v /var/run/docker.sock://var/run/docker.sock
o para docker-compose:
volumes: - "/var/run/docker.sock:/var/run/docker.sock"
urgente necesidad de cambiar esto.
Y lo último: nunca escuche,
nunca use el socket Docker TCP sin la absoluta certeza de que lo necesita, especialmente sin el uso de métodos de protección adicionales (al menos autorización). De manera predeterminada, el socket Docker TCP abre un puerto en la interfaz externa 0.0.0.0:2375 (2376, en el caso de HTTP) y permite el control total de los contenedores, y con él el posible sistema host.
Regla 2
Configurar un usuario sin privilegios dentro del contenedor
Configurar un contenedor para usar un usuario sin privilegios es la mejor manera de evitar un ataque de escalada de privilegios. Esto se puede hacer de varias maneras:
1. Usando la opción "-u" del comando "docker run":
docker run -u 4000 alpine
2. Durante la construcción de la imagen:
FROM alpine RUN groupadd -r myuser && useradd -r -g myuser myuser < root-, , > USER myuser
3. Habilite la compatibilidad con el "espacio de nombres de usuario" (entorno de usuario) en Docker daemon:
--userns-remap=default
Lea más sobre esto en la
documentación oficial .
En Kubernetes, este último se configura en el
contexto de seguridad a través de la opción runAsNonRoot:
kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... runAsNonRoot: true ...
Regla 3
Limitar las capacidades del contenedor
En Linux, comenzando con el kernel 2.2, hay una manera de controlar las capacidades de los procesos privilegiados llamados
Capacidades del kernel de Linux (para más detalles, vea el enlace).
Docker usa un conjunto predefinido de estas características del núcleo de forma predeterminada. Y le permite cambiar este conjunto utilizando los comandos:
--cap-drop — --cap-add —
La mejor configuración de seguridad es deshabilitar primero todas las funciones (--cap-drop all), y luego conectar solo las necesarias. Por ejemplo, así:
docker run --cap-drop all --cap-add CHOWN alpine
Y lo más importante (!): ¡Evite ejecutar contenedores con la bandera privilegiada!
En Kubernetes, la restricción de capacidades del kernel de Linux se configura en el contexto de seguridad a través de la opción de capacidades:
kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... capabilities: drop: - all add: - CHOWN ...
Regla 4
Utilice el indicador de no-nuevos-privilegios
Al iniciar un contenedor, es útil usar el indicador --security-opt = no-new-privileges que evita la escalada de privilegios dentro del contenedor.
En Kubernetes, la restricción de capacidades del kernel de Linux se configura en el contexto de seguridad mediante la opción allowPrivilegeEscalation:
kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... allowPrivilegeEscalation: false ...
Regla 5
Apague la comunicación entre contenedores
De manera predeterminada, la comunicación entre contenedores está habilitada en Docker, lo que significa que todos los contenedores pueden comunicarse entre sí (utilizando la red docker0). Esta característica se puede deshabilitar ejecutando el servicio Docker con el indicador –icc = false.
Regla 6
Utilice los módulos de seguridad de Linux (Módulo de seguridad de Linux - seccomp, AppArmor, SELinux)
Por defecto, Docker ya usa perfiles para módulos de seguridad de Linux. Por lo tanto, ¡
nunca deshabilite los perfiles de seguridad! Lo máximo que se puede hacer con ellos es ajustar las reglas.
El perfil predeterminado para seccomp está disponible
aquí .
Docker también usa AppArmor para protección, y Docker Engine genera un perfil predeterminado para AppArmor cuando se inicia el contenedor. En otras palabras, en lugar de:
$ docker run --rm -it hello-world
se pone en marcha:
$ docker run --rm -it --security-opt apparmor=docker-default hello-world
La
documentación también proporciona un ejemplo de un perfil de AppArmor para nginx, que es bastante posible (¡necesario!) Usar:
Regla 7
Limite los recursos del contenedor
Esta regla es bastante simple: para evitar que los contenedores devoren todos los recursos del servidor durante el próximo ataque DoS / DDoS, podemos establecer límites de uso de memoria para cada contenedor individualmente. Puede limitar: cantidad de memoria, CPU, número de reinicios del contenedor.
Así que vamos en orden.
El recuerdoLa opción -m o --memoryLa cantidad máxima de memoria que puede usar un contenedor. El valor mínimo es de 4 m (4 megabytes).
Opción - intercambio de memoriaOpción para configurar el intercambio (archivo de intercambio). Configurado astutamente:
- Si --memory-swap> 0, también se debe establecer el indicador –memory. En este caso, el intercambio de memoria muestra la cantidad de memoria total disponible para el contenedor junto con el intercambio.
- Un ejemplo más simple. Si --memory = "300m", y --memory-swap = "1g", entonces el contenedor puede usar 300MB de memoria y 700MB de intercambio (1g - 300m).
- Si --memory-swap = 0, la configuración se ignora.
- Si --memory-swap se establece en el mismo valor que --memory, entonces el contenedor no tendrá swap.
- Si --memory-swap no se especifica, pero --memory se especifica, entonces el número de intercambio será igual al doble de la cantidad de memoria especificada. Por ejemplo, si --memory = "300m", y --memory-swap no está configurado, entonces el contenedor usará 300MB de memoria y 600MB de intercambio.
- Si --memory-swap = -1, el contenedor usará todo el intercambio que sea posible en el sistema host.
Nota para la anfitriona: la utilidad gratuita lanzada dentro del contenedor no muestra el valor real del intercambio disponible para el contenedor, sino el número de intercambio de host.
Opción --oom-kill-disableLe permite habilitar o deshabilitar el asesino OOM (sin memoria).
Atencion Puede desactivar OOM Killer solo con la opción --memory especificada, de lo contrario puede suceder que con falta de memoria dentro del contenedor, el núcleo comenzará a matar los procesos del sistema host.
Otras opciones de configuración de administración de memoria, como --memory-swappiness, --memory-reservetion, y --kernel-memory, son más para ajustar el rendimiento del contenedor.
CPUOpción --cpusLa opción establece cuántos recursos de procesador disponibles puede usar el contenedor. Por ejemplo, si tenemos un host con dos CPU y configuramos --cpus = "1.5", se garantiza que el contenedor utilizará procesadores de uno y medio.
Opción --cpuset-cpusConfigura el uso de núcleos o CPU específicos. El valor se puede especificar con un guión o una coma. En el primer caso, se indicará el rango de núcleos permitidos, en el segundo - núcleos específicos.
Número de reinicios del contenedor --restart=on-failure:<number_of_restarts>
Esta configuración establece cuántas veces Docker intentará reiniciar el contenedor si se bloquea inesperadamente. El contador se reinicia si el estado del contenedor ha cambiado a en ejecución.
Se recomienda establecer un pequeño número positivo, por ejemplo, 5, que evitará reinicios interminables de un servicio que no funciona.
Regla 8
Utilice sistemas de archivos y volumen de solo lectura
Si el contenedor no debe escribir nada en alguna parte, debe usar el sistema de archivos de solo lectura tanto como sea posible. Esto complicará en gran medida la vida de un intruso potencial.
Un ejemplo de inicio de un contenedor con sistema de archivos de solo lectura:
docker run --read-only alpine
Un ejemplo de conexión de volumen en modo de solo lectura:
docker run -v volume-name:/path/in/container:ro alpine
Regla 9
Usar herramientas de análisis de seguridad de contenedores
Las herramientas deben usarse para detectar contenedores con vulnerabilidades conocidas. Todavía no hay muchos, pero son:
• Gratis:
• Comercial:
Y para Kubernetes, hay herramientas para detectar errores de configuración: