Seguridad de Docker

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Seguridad Básica del Motor de Docker

El motor de Docker emplea los Namespaces y Cgroups del núcleo de Linux para aislar contenedores, ofreciendo una capa básica de seguridad. Se proporciona protección adicional a través de Capabilities dropping, Seccomp y SELinux/AppArmor, mejorando el aislamiento de los contenedores. Un auth plugin puede restringir aún más las acciones del usuario.

Seguridad de Docker

Acceso Seguro al Motor de Docker

El motor de Docker se puede acceder localmente a través de un socket Unix o de forma remota utilizando HTTP. Para el acceso remoto, es esencial emplear HTTPS y TLS para garantizar la confidencialidad, integridad y autenticación.

El motor de Docker, por defecto, escucha en el socket Unix en unix:///var/run/docker.sock. En sistemas Ubuntu, las opciones de inicio de Docker se definen en /etc/default/docker. Para habilitar el acceso remoto a la API y al cliente de Docker, exponga el demonio de Docker a través de un socket HTTP añadiendo la siguiente configuración:

bash
DOCKER_OPTS="-D -H unix:///var/run/docker.sock -H tcp://192.168.56.101:2376"
sudo service docker restart

Sin embargo, exponer el daemon de Docker a través de HTTP no se recomienda debido a preocupaciones de seguridad. Es aconsejable asegurar las conexiones utilizando HTTPS. Hay dos enfoques principales para asegurar la conexión:

  1. El cliente verifica la identidad del servidor.
  2. Tanto el cliente como el servidor se autentican mutuamente la identidad del otro.

Se utilizan certificados para confirmar la identidad de un servidor. Para ejemplos detallados de ambos métodos, consulta esta guía.

Seguridad de las Imágenes de Contenedor

Las imágenes de contenedor pueden almacenarse en repositorios privados o públicos. Docker ofrece varias opciones de almacenamiento para imágenes de contenedor:

  • Docker Hub: Un servicio de registro público de Docker.
  • Docker Registry: Un proyecto de código abierto que permite a los usuarios alojar su propio registro.
  • Docker Trusted Registry: La oferta de registro comercial de Docker, que cuenta con autenticación de usuario basada en roles e integración con servicios de directorio LDAP.

Escaneo de Imágenes

Los contenedores pueden tener vulnerabilidades de seguridad ya sea por la imagen base o por el software instalado sobre la imagen base. Docker está trabajando en un proyecto llamado Nautilus que realiza un escaneo de seguridad de los contenedores y lista las vulnerabilidades. Nautilus funciona comparando cada capa de imagen de contenedor con un repositorio de vulnerabilidades para identificar agujeros de seguridad.

Para más información lee esto.

  • docker scan

El comando docker scan te permite escanear imágenes de Docker existentes utilizando el nombre o ID de la imagen. Por ejemplo, ejecuta el siguiente comando para escanear la imagen hello-world:

bash
docker scan hello-world

Testing hello-world...

Organization:      docker-desktop-test
Package manager:   linux
Project name:      docker-image|hello-world
Docker image:      hello-world
Licenses:          enabled

✓ Tested 0 dependencies for known issues, no vulnerable paths found.

Note that we do not currently have vulnerability data for your image.
bash
trivy -q -f json <container_name>:<tag>
bash
snyk container test <image> --json-file-output=<output file> --severity-threshold=high
bash
clair-scanner -w example-alpine.yaml --ip YOUR_LOCAL_IP alpine:3.5

Firma de Imágenes de Docker

La firma de imágenes de Docker garantiza la seguridad e integridad de las imágenes utilizadas en contenedores. Aquí hay una explicación condensada:

  • Docker Content Trust utiliza el proyecto Notary, basado en The Update Framework (TUF), para gestionar la firma de imágenes. Para más información, consulta Notary y TUF.
  • Para activar la confianza en el contenido de Docker, establece export DOCKER_CONTENT_TRUST=1. Esta función está desactivada por defecto en Docker versión 1.10 y posteriores.
  • Con esta función habilitada, solo se pueden descargar imágenes firmadas. El primer envío de imágenes requiere establecer frases de contraseña para las claves raíz y de etiquetado, con Docker también soportando Yubikey para una mayor seguridad. Más detalles se pueden encontrar aquí.
  • Intentar descargar una imagen no firmada con la confianza en el contenido habilitada resulta en un error "No trust data for latest".
  • Para los envíos de imágenes después del primero, Docker solicita la frase de contraseña de la clave del repositorio para firmar la imagen.

Para respaldar tus claves privadas, utiliza el comando:

bash
tar -zcvf private_keys_backup.tar.gz ~/.docker/trust/private

Al cambiar de hosts de Docker, es necesario mover las claves raíz y del repositorio para mantener las operaciones.

Características de Seguridad de Contenedores

Resumen de las Características de Seguridad de Contenedores

Características Principales de Aislamiento de Procesos

En entornos contenedorizados, aislar proyectos y sus procesos es fundamental para la seguridad y la gestión de recursos. Aquí hay una explicación simplificada de conceptos clave:

Namespaces

  • Propósito: Asegurar el aislamiento de recursos como procesos, red y sistemas de archivos. Particularmente en Docker, los namespaces mantienen los procesos de un contenedor separados del host y de otros contenedores.
  • Uso de unshare: El comando unshare (o la llamada al sistema subyacente) se utiliza para crear nuevos namespaces, proporcionando una capa adicional de aislamiento. Sin embargo, aunque Kubernetes no bloquea esto inherentemente, Docker sí lo hace.
  • Limitación: Crear nuevos namespaces no permite que un proceso vuelva a los namespaces predeterminados del host. Para penetrar en los namespaces del host, uno típicamente requeriría acceso al directorio /proc del host, utilizando nsenter para la entrada.

Grupos de Control (CGroups)

  • Función: Utilizados principalmente para asignar recursos entre procesos.
  • Aspecto de Seguridad: Los CGroups en sí no ofrecen seguridad de aislamiento, excepto por la característica release_agent, que, si está mal configurada, podría ser explotada para acceso no autorizado.

Caída de Capacidades

  • Importancia: Es una característica de seguridad crucial para el aislamiento de procesos.
  • Funcionalidad: Restringe las acciones que un proceso raíz puede realizar al eliminar ciertas capacidades. Incluso si un proceso se ejecuta con privilegios de root, la falta de las capacidades necesarias impide que ejecute acciones privilegiadas, ya que las llamadas al sistema fallarán debido a permisos insuficientes.

Estas son las capacidades restantes después de que el proceso elimine las otras:

Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep

Seccomp

Está habilitado por defecto en Docker. Ayuda a limitar aún más las syscalls que el proceso puede llamar.
El perfil de Seccomp predeterminado de Docker se puede encontrar en https://github.com/moby/moby/blob/master/profiles/seccomp/default.json

AppArmor

Docker tiene una plantilla que puedes activar: https://github.com/moby/moby/tree/master/profiles/apparmor

Esto permitirá reducir capacidades, syscalls, acceso a archivos y carpetas...

Namespaces

Namespaces son una característica del núcleo de Linux que particiona los recursos del núcleo de tal manera que un conjunto de procesos ve un conjunto de recursos mientras que otro conjunto de procesos ve un conjunto diferente de recursos. La característica funciona al tener el mismo namespace para un conjunto de recursos y procesos, pero esos namespaces se refieren a recursos distintos. Los recursos pueden existir en múltiples espacios.

Docker utiliza los siguientes Namespaces del núcleo de Linux para lograr la aislamiento de Contenedores:

  • pid namespace
  • mount namespace
  • network namespace
  • ipc namespace
  • UTS namespace

Para más información sobre los namespaces consulta la siguiente página:

Namespaces

cgroups

La característica del núcleo de Linux cgroups proporciona la capacidad de restringir recursos como cpu, memoria, io, ancho de banda de red entre un conjunto de procesos. Docker permite crear Contenedores utilizando la característica cgroup que permite el control de recursos para el Contenedor específico.
A continuación se muestra un Contenedor creado con memoria de espacio de usuario limitada a 500m, memoria del núcleo limitada a 50m, participación de cpu a 512, blkioweight a 400. La participación de CPU es una proporción que controla el uso de CPU del Contenedor. Tiene un valor predeterminado de 1024 y un rango entre 0 y 1024. Si tres Contenedores tienen la misma participación de CPU de 1024, cada Contenedor puede utilizar hasta el 33% de la CPU en caso de contención de recursos de CPU. blkio-weight es una proporción que controla el IO del Contenedor. Tiene un valor predeterminado de 500 y un rango entre 10 y 1000.

docker run -it -m 500M --kernel-memory 50M --cpu-shares 512 --blkio-weight 400 --name ubuntu1 ubuntu bash

Para obtener el cgroup de un contenedor, puedes hacer:

bash
docker run -dt --rm denial sleep 1234 #Run a large sleep inside a Debian container
ps -ef | grep 1234 #Get info about the sleep process
ls -l /proc/<PID>/ns #Get the Group and the namespaces (some may be uniq to the hosts and some may be shred with it)

Para más información, consulta:

CGroups

Capacidades

Las capacidades permiten un control más fino sobre las capacidades que se pueden permitir para el usuario root. Docker utiliza la función de capacidad del núcleo de Linux para limitar las operaciones que se pueden realizar dentro de un contenedor independientemente del tipo de usuario.

Cuando se ejecuta un contenedor de Docker, el proceso elimina capacidades sensibles que el proceso podría usar para escapar de la aislamiento. Esto intenta asegurar que el proceso no podrá realizar acciones sensibles y escapar:

Linux Capabilities

Seccomp en Docker

Esta es una característica de seguridad que permite a Docker limitar las syscalls que se pueden usar dentro del contenedor:

Seccomp

AppArmor en Docker

AppArmor es una mejora del núcleo para confinar contenedores a un conjunto limitado de recursos con perfiles por programa.:

AppArmor

SELinux en Docker

  • Sistema de Etiquetado: SELinux asigna una etiqueta única a cada proceso y objeto del sistema de archivos.
  • Aplicación de Políticas: Aplica políticas de seguridad que definen qué acciones puede realizar una etiqueta de proceso sobre otras etiquetas dentro del sistema.
  • Etiquetas de Proceso de Contenedor: Cuando los motores de contenedores inician procesos de contenedor, generalmente se les asigna una etiqueta SELinux confinada, comúnmente container_t.
  • Etiquetado de Archivos dentro de Contenedores: Los archivos dentro del contenedor suelen etiquetarse como container_file_t.
  • Reglas de Política: La política de SELinux asegura principalmente que los procesos con la etiqueta container_t solo puedan interactuar (leer, escribir, ejecutar) con archivos etiquetados como container_file_t.

Este mecanismo asegura que incluso si un proceso dentro de un contenedor se ve comprometido, está confinado a interactuar solo con objetos que tienen las etiquetas correspondientes, limitando significativamente el daño potencial de tales compromisos.

SELinux

AuthZ y AuthN

En Docker, un plugin de autorización juega un papel crucial en la seguridad al decidir si permitir o bloquear solicitudes al demonio de Docker. Esta decisión se toma examinando dos contextos clave:

  • Contexto de Autenticación: Esto incluye información completa sobre el usuario, como quiénes son y cómo se han autenticado.
  • Contexto de Comando: Esto comprende todos los datos pertinentes relacionados con la solicitud que se está realizando.

Estos contextos ayudan a asegurar que solo se procesen solicitudes legítimas de usuarios autenticados, mejorando la seguridad de las operaciones de Docker.

AuthZ& AuthN - Docker Access Authorization Plugin

DoS desde un contenedor

Si no estás limitando adecuadamente los recursos que un contenedor puede usar, un contenedor comprometido podría causar un DoS al host donde se está ejecutando.

  • DoS de CPU
bash
# stress-ng
sudo apt-get install -y stress-ng && stress-ng --vm 1 --vm-bytes 1G --verify -t 5m

# While loop
docker run -d --name malicious-container -c 512 busybox sh -c 'while true; do :; done'
  • DoS de ancho de banda
bash
nc -lvp 4444 >/dev/null & while true; do cat /dev/urandom | nc <target IP> 4444; done

Banderas de Docker Interesantes

Banderas --privileged

En la siguiente página puedes aprender qué implica la bandera --privileged:

Docker --privileged

--security-opt

no-new-privileges

Si estás ejecutando un contenedor donde un atacante logra acceder como un usuario de bajo privilegio. Si tienes un binario suid mal configurado, el atacante puede abusar de él y escalar privilegios dentro del contenedor. Lo que puede permitirle escapar de él.

Ejecutar el contenedor con la opción no-new-privileges habilitada prevendrá este tipo de escalada de privilegios.

docker run -it --security-opt=no-new-privileges:true nonewpriv

Otro

bash
#You can manually add/drop capabilities with
--cap-add
--cap-drop

# You can manually disable seccomp in docker with
--security-opt seccomp=unconfined

# You can manually disable seccomp in docker with
--security-opt apparmor=unconfined

# You can manually disable selinux in docker with
--security-opt label:disable

Para más opciones de --security-opt consulta: https://docs.docker.com/engine/reference/run/#security-configuration

Otras Consideraciones de Seguridad

Gestión de Secretos: Mejores Prácticas

Es crucial evitar incrustar secretos directamente en las imágenes de Docker o usar variables de entorno, ya que estos métodos exponen tu información sensible a cualquiera con acceso al contenedor a través de comandos como docker inspect o exec.

Los volúmenes de Docker son una alternativa más segura, recomendada para acceder a información sensible. Pueden ser utilizados como un sistema de archivos temporal en memoria, mitigando los riesgos asociados con docker inspect y el registro. Sin embargo, los usuarios root y aquellos con acceso exec al contenedor aún podrían acceder a los secretos.

Los secretos de Docker ofrecen un método aún más seguro para manejar información sensible. Para instancias que requieren secretos durante la fase de construcción de la imagen, BuildKit presenta una solución eficiente con soporte para secretos en tiempo de construcción, mejorando la velocidad de construcción y proporcionando características adicionales.

Para aprovechar BuildKit, se puede activar de tres maneras:

  1. A través de una variable de entorno: export DOCKER_BUILDKIT=1
  2. Prefijando comandos: DOCKER_BUILDKIT=1 docker build .
  3. Habilitándolo por defecto en la configuración de Docker: { "features": { "buildkit": true } }, seguido de un reinicio de Docker.

BuildKit permite el uso de secretos en tiempo de construcción con la opción --secret, asegurando que estos secretos no se incluyan en la caché de construcción de la imagen o en la imagen final, utilizando un comando como:

bash
docker build --secret my_key=my_value ,src=path/to/my_secret_file .

Para los secretos necesarios en un contenedor en ejecución, Docker Compose y Kubernetes ofrecen soluciones robustas. Docker Compose utiliza una clave secrets en la definición del servicio para especificar archivos secretos, como se muestra en un ejemplo de docker-compose.yml:

yaml
version: "3.7"
services:
my_service:
image: centos:7
entrypoint: "cat /run/secrets/my_secret"
secrets:
- my_secret
secrets:
my_secret:
file: ./my_secret_file.txt

Esta configuración permite el uso de secretos al iniciar servicios con Docker Compose.

En entornos de Kubernetes, los secretos son compatibles de forma nativa y se pueden gestionar aún más con herramientas como Helm-Secrets. Los controles de acceso basados en roles (RBAC) de Kubernetes mejoran la seguridad de la gestión de secretos, similar a Docker Enterprise.

gVisor

gVisor es un núcleo de aplicación, escrito en Go, que implementa una parte sustancial de la superficie del sistema Linux. Incluye un runtime de Open Container Initiative (OCI) llamado runsc que proporciona un límite de aislamiento entre la aplicación y el núcleo del host. El runtime runsc se integra con Docker y Kubernetes, lo que facilita la ejecución de contenedores en sandbox.

{% embed url="https://github.com/google/gvisor" %}

Kata Containers

Kata Containers es una comunidad de código abierto que trabaja para construir un runtime de contenedores seguro con máquinas virtuales ligeras que se sienten y rinden como contenedores, pero que proporcionan un aislamiento de carga de trabajo más fuerte utilizando tecnología de virtualización de hardware como una segunda capa de defensa.

{% embed url="https://katacontainers.io/" %}

Resumen de Consejos

  • No utilice la bandera --privileged ni monte un socket de Docker dentro del contenedor. El socket de Docker permite crear contenedores, por lo que es una forma fácil de tomar el control total del host, por ejemplo, ejecutando otro contenedor con la bandera --privileged.
  • No ejecute como root dentro del contenedor. Use un usuario diferente y namespaces de usuario. El root en el contenedor es el mismo que en el host a menos que se remapee con namespaces de usuario. Está solo ligeramente restringido por, principalmente, namespaces de Linux, capacidades y cgroups.
  • Elimine todas las capacidades (--cap-drop=all) y habilite solo las que son necesarias (--cap-add=...). Muchas cargas de trabajo no necesitan ninguna capacidad y agregarlas aumenta el alcance de un posible ataque.
  • Utilice la opción de seguridad “no-new-privileges” para evitar que los procesos obtengan más privilegios, por ejemplo, a través de binarios suid.
  • Limite los recursos disponibles para el contenedor. Los límites de recursos pueden proteger la máquina de ataques de denegación de servicio.
  • Ajuste seccomp, AppArmor (o SELinux) perfiles para restringir las acciones y syscalls disponibles para el contenedor al mínimo requerido.
  • Utilice imágenes oficiales de Docker y requiera firmas o construya las suyas propias basadas en ellas. No herede ni use imágenes con puerta trasera. También almacene claves raíz y frases de paso en un lugar seguro. Docker tiene planes para gestionar claves con UCP.
  • Reconstruya regularmente sus imágenes para aplicar parches de seguridad al host y a las imágenes.
  • Gestione sus secretos sabiamente para que sea difícil para el atacante acceder a ellos.
  • Si expone el demonio de Docker, use HTTPS con autenticación de cliente y servidor.
  • En su Dockerfile, prefiera COPY en lugar de ADD. ADD extrae automáticamente archivos comprimidos y puede copiar archivos de URLs. COPY no tiene estas capacidades. Siempre que sea posible, evite usar ADD para no ser susceptible a ataques a través de URLs remotas y archivos Zip.
  • Tenga contenedores separados para cada microservicio.
  • No ponga ssh dentro del contenedor, “docker exec” se puede usar para ssh al contenedor.
  • Tenga imágenes de contenedor más pequeñas.

Docker Breakout / Escalación de Privilegios

Si está dentro de un contenedor de Docker o tiene acceso a un usuario en el grupo de Docker, podría intentar escapar y escalar privilegios:

Docker Breakout / Privilege Escalation

Bypass del Plugin de Autenticación de Docker

Si tiene acceso al socket de Docker o tiene acceso a un usuario en el grupo de Docker pero sus acciones están siendo limitadas por un plugin de autenticación de Docker, verifique si puede eludirlo:

AuthZ& AuthN - Docker Access Authorization Plugin

Endurecimiento de Docker

Referencias

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks