lunes, febrero 22, 2021

Docker SecDevOps y las Linux Capabilites

Hace unos días, nuestro buen amigo Pablo González nos explicó en este artículo cómo se puede realizar una escalada de privilegios utilizando las Linux Capabilities. En dicho artículo comentaba la importancia de su configuración y control para realizar una correcta fortificación de los entornos GNU/Linux. Pues bien, en el mundo de los contenedores Docker las Linux Capabilities son también muy importantes a la hora de gestionar correctamente la seguridad, tanto del contenedor como del host

Figura 1: Docker SecDevOps y las Linux Capabilites

Ya sabemos que, por defecto, cuando levantamos un contenedor Docker, éste se ejecuta como root. Pero claro, todos sabemos que esta práctica no es una buena idea, sobre todo servicios que recibe peticiones ya sean por parte de usuarios o provenientes de otras fuentes. Por cierto, si quieres aprender las bases de Docker y el mundo que les rodea, tenemos este libro en la editorial 0xWord para ayudarte a comprender este fascinante (y cada vez más demandado) mundo de los contenedores:

Figura 2: Libro de Docker:SecDevOps

También es importante mencionar, que, aunque un contenedor ejecutado como root, no tiene los mismos privilegios que el usuario root del host. Esto es porque por defecto, los contenedores Docker se ejecutan con un número limitado de capacidades (Linux capabilities), concretamente con las siguientes:
•	AUDIT_WRITE
•	CHOWN
•	DAC_OVERRIDE
•	FOWNER
•	UID
•	FSETID
•	KILL
•	MKNOD
•	NET_BIND_SERVICE
•	NET_RAW
•	SETFCAP
•	SETGID
•	SETPCAP
•	SETUID
•	SYS_CHROOT
Esto también lo podemos comprobar arrancando un contenedor, conectarnos a una shell y listando las capacidades. Para ver este ejemplo usaremos una imagen Ubuntu e instalaremos el paquete libcap2-bin, el cual contiene utilidades para trabajar con capacidades:
docker run -it ubuntu
root@a8761683eb71:/# apt update
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [109 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
...
...

root@a8761683eb71:/# apt install -y libcap2-bin
...
...
root@a8761683eb71:/# grep Cap /proc/$BASHPID/status
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
...
...
root@a8761683eb71:/# capsh --decode=00000000a80425fb
0x00000000a80425fb=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
Si comparamos la lista de capacidades devuelta por el comando capsh, vemos que corresponde a la lista mostrada anteriormente. Además de dichas capacidades, Docker permite añadir otras capacidades dentro de la siguiente lista:
•	AUDIT_CONTROL
•	AUDIT_READ
•	BLOCK_SUSPEND
•	BPF
•	CHECKPOINT_RESTORE
•	DAC_READ_SEARCH
•	IPC_LOCK
•	IPC_OWNER
•	LEASE
•	LINUX_IMMUTABLE
•	MAC_ADMIN
•	MAC_OVERRIDE
•	NET_ADMIN
•	NET_BROADCAST
•	PERFMON
•	SYS_ADMIN
•	SYS_BOOT
•	SYS_MODULE
•	SYS_NICE
•	SYS_PACCT
•	SYS_PTRACE
•	SYS_RAWIO
•	SYS_RESOURCE
•	SYS_TIME
•	SYS_TTY_CONFIG
•	WAKE_ALARM
Para saber qué tipo de privilegios proveen estas capacidades, puedes consultar la documentación de Docker o directamente la documentación de Linux. Si miras la documentación de Linux, verás que el nombre de las capacidades tienen el prefijo CAP_, mientras que para el uso de éstas en Docker, se omite dicho prefijo, así la capacidad CAP_CHOWN, referenciaría simplemente como CHOWN en Docker.
 
(Revisada y Ampliada) de Carlos Álvarez y Pablo González en 0xWord

Para jugar con las capacidades en Docker lo haremos a través de las opciones --cap-add y --cap-drop. Por ejemplo, si arrancamos un contenedor y le quitamos la capacidad de cambiar el propietario de un fichero (o directorio):
docker run -it --cap-drop=CHOWN ubuntu
root@4c385a7c43c2:/# chown nobody tmp
chown: changing ownership of 'tmp': Operation not permitted
root@4c385a7c43c2:/# id
uid=0(root) gid=0(root) groups=0(root)
Como podemos ver, incluso siendo root no tenemos el permiso para cambiar el propietario de un fichero. Recuerda que en Linux, todo es un fichero, y en el libro de Linux Exploiting le sacan buen partido a esto.

Figura 4: Linux Exploiting

Otro ejemplo, si le quitamos la capacidad de NET_RAW al contenedor, no podríamos por ejemplo hacer ping:
docker run -it --cap-drop=NET_RAW busybox
/ # ping google.com
PING google.com (172.217.15.110): 56 data bytes
ping: permission denied (are you root?)
/ # id
uid=0(root) gid=0(root) groups=10(wheel)
Como buena práctica de seguridad sería el quitar todas las capacidades de un contenedor, para ello, Docker provee de la opción ALL, para referenciar a todas las capacidades:
docker run -it --cap-drop=ALL ubuntu
root@88c31bcf9d36:/# apt update
E: setgroups 65534 failed - setgroups (1: Operation not permitted)
E: setegid 65534 failed - setegid (1: Operation not permitted)
...
...
root@88c31bcf9d36:/# id
uid=0(root) gid=0(root) groups=0(root)
Con eso le quitaríamos todos los permisos al usuario, incluso root. Como se puede ver en el ejemplo, root ni siquiera podría instalar software nuevo, hacer ping, modificar el propietario de un fichero, etcétera. Por supuesto, el ejecutar el contenedor como NO root es también una buena práctica y un de las principales acciones que uno debería tomar para proteger un contenedor. Lo ideal sería ejecutar el contenedor con un usuario sin ninguna capacidad, y si es necesario, añadir únicamente las capacidades que se necesiten. Si usas docker-compose, el equivalente sería:
version: "3.9"
services:
  miservicio:
    ...
    cap_drop:
      - ALL

    cap_add:
      - NET_ADMIN
      - SYS_ADMIN
    ...
Y en Kubernetes, esto se definiría dentro del contexto de seguridad del pod o del contenedor o contenedores:
apiVersion: v1
kind: Pod
metadata:
name: capacidades-ejemplo
spec:
containers:
- name: capacidades-ejemplo
    image: mi-imagen
    securityContext:
    capabilities:
        drop:
          - ALL
        add: 
          - NET_ADMIN
          - SYS_TIME
...
...
Existen muchas otras buenas prácticas de seguridad en relación con proteger tus contenedores e imágenes Docker. Aquí sólo hemos visto un poco lo relacionado al tema de las capacidades de Linux. Volveremos a hablar de la importancia de la seguridad tanto en contenedores como en Kubernetes. Así que atentos ;)

 Happy Hacking Hackers!!! 

Autores:

Fran Ramírez, (@cyberhadesblog) es investigador de seguridad y miembro del equipo de Ideas Locas en CDO en Telefónica, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps", también de "Machine Learning aplicado a la Ciberseguridad” además del blog CyberHades. Puedes contactar con Fran Ramirez en MyPublicInbox.

 Contactar con Fran Ramírez en MyPublicInbox

Rafael Troncoso
(@tuxotron) es Senior Software Engineer en SAP Concur, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" además del blog CyberHades. Puedes contactar con Rafael Troncoso en MyPublicInbox.


Contactar con Rafael Troncoso en MyPublicInbox

No hay comentarios:

Publicar un comentario