Cómo usar la marca --privileged con motores de contenedores

Muchos usuarios están confundidos acerca de la --privileged bandera. Los usuarios a menudo equiparan este indicador con acceso de raíz completo o sin restricciones al sistema host. En este blog, discuto lo que --privileged bandera hecha con motores de contenedores como Podman, Estibador, y Construir.

¿Qué hace que la bandera --privileged haga que los motores de contenedores hagan?

¿Qué privilegios otorga a los procesos contenedores?

Haga funcionar motores de contenedores con el --privileged le dice al motor que inicie el proceso del contenedor sin más bloqueos de "seguridad".

Nota: ejecutar motores de contenedores en modo sin raíz no significa que se ejecuten con más privilegios que el usuario que ejecuta el comando. De cualquier manera, los contenedores están bloqueados para un mayor acceso de Linux. Sus procesos aún se ejecutan como el proceso de usuario que los inició en el host. Entonces, por ejemplo, correr --privileged no permite que el proceso del contenedor se vincule repentinamente a un puerto inferior a 1024. El kernel no permite que los usuarios que no sean root se vinculen a estos puertos, por lo que los usuarios que inician procesos del contenedor tampoco pueden acceder a él.

Lo principal es que usando el --privileged flag no le dice a los motores de contenedores que agreguen restricciones de seguridad adicionales. el --privileged flag no agrega ningún privilegio sobre lo que tienen los procesos que inician los contenedores. Herramientas como Podman y Buildah NO brindan acceso adicional más allá de los procesos iniciados por el usuario.

para entender el --privileged bandera, debe comprender la seguridad habilitada por los motores de contenedores y lo que está deshabilitado.

Sistemas de archivos del kernel de solo lectura

Los sistemas de archivos del kernel proporcionan un mecanismo para que un proceso modifique la forma en que se ejecuta el kernel. También proporcionan información a los procesos del sistema. De forma predeterminada, no queremos que los procesos del contenedor modifiquen el kernel, por lo que montamos sistemas de archivos del kernel de solo lectura en el contenedor. Los montajes de solo lectura evitan que los procesos privilegiados y los procesos con capacidades en el espacio de nombres de usuario escriban en los sistemas de archivos del kernel.

$ podman run fedora mount  | grep '(ro'
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime,seclabel) tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,relatime,context="system_u:object_r:container_file_t:s0:c268,c852",mode=755,uid=3267,gid=3267)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,xattr,name=systemd)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,net_cls,net_prio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,hugetlb)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,devices)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,cpuset)
cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,pids)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,cpu,cpuacct)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,seclabel,perf_event)
proc on /proc/asound type proc (ro,relatime)
proc on /proc/bus type proc (ro,relatime)
proc on /proc/fs type proc (ro,relatime)
proc on /proc/irq type proc (ro,relatime)
proc on /proc/sys type proc (ro,relatime)
proc on /proc/sysrq-trigger type proc (ro,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime,context="system_u:object_r:container_file_t:s0:c268,c852",uid=3267,gid=3267)
tmpfs on /proc/scsi type tmpfs (ro,relatime,context="system_u:object_r:container_file_t:s0:c268,c852",uid=3267,gid=3267)
tmpfs on /sys/firmware type tmpfs (ro,relatime,context="system_u:object_r:container_file_t:s0:c268,c852",uid=3267,gid=3267)
tmpfs on /sys/fs/selinux type tmpfs (ro,relatime,context="system_u:object_r:container_file_t:s0:c268,c852",uid=3267,gid=3267)

Así que cuando corro como --privileged, Recibo:

$ podman run --privileged fedora mount  | grep '(ro'
$

Ninguno de los sistemas de archivos del kernel está montado como de solo lectura en --privileged Moda. Por lo general, esto es necesario para permitir que los procesos dentro del contenedor modifiquen realmente el kernel a través del sistema de archivos del kernel.

Esconderse en los sistemas de archivos del kernel

El sistema de archivos /proc es sensible al espacio de nombres y se pueden permitir algunas escrituras, por lo que no lo montamos como de solo lectura. Sin embargo, los directorios específicos del sistema de archivos /proc deben estar protegidos contra escritura y, en algunos casos, contra lectura. En estos casos, los motores de contenedores montan sistemas de archivos tmpfs en directorios potencialmente peligrosos, evitando que los procesos dentro del contenedor los utilicen.

$ podman run fedora mount  | grep /proc.*tmpfs
tmpfs on /proc/acpi type tmpfs (ro,relatime,context="system_u:object_r:container_file_t:s0:c255,c491",uid=3267,gid=3267)
devtmpfs on /proc/kcore type devtmpfs (rw,nosuid,seclabel,size=7995040k,nr_inodes=1998760,mode=755)
devtmpfs on /proc/keys type devtmpfs (rw,nosuid,seclabel,size=7995040k,nr_inodes=1998760,mode=755)
devtmpfs on /proc/latency_stats type devtmpfs (rw,nosuid,seclabel,size=7995040k,nr_inodes=1998760,mode=755)
devtmpfs on /proc/timer_list type devtmpfs (rw,nosuid,seclabel,size=7995040k,nr_inodes=1998760,mode=755)
devtmpfs on /proc/sched_debug type devtmpfs (rw,nosuid,seclabel,size=7995040k,nr_inodes=1998760,mode=755)
tmpfs on /proc/scsi type tmpfs (ro,relatime,context="system_u:object_r:container_file_t:s0:c255,c491",uid=3267,gid=3267)

Con --privileged, los puntos de montaje no están ocultos en:

$ podman run --privileged fedora mount | grep /proc.*tmpfs
$

Capacidades de Linux

son un mecanismo para limitar la potencia de la raíz. El kernel de Linux divide los privilegios de root (superusuario) en una serie de unidades distintas, llamadas capacidades. En el caso de los contenedores sin raíz, los motores de contenedores siguen utilizando funciones de espacio de nombres de usuario. Estas capacidades limitan el poder de la raíz en el espacio de nombres del usuario. Los motores de contenedores lanzan contenedores con un número limitado de espacios de nombres habilitados para controlar lo que sucede dentro del contenedor de forma predeterminada.

$ podman run -d fedora sleep 100
8b1facf07f11486e6379d14432f7c7f89da262d2aba8b55ff52af8570d0a17a9
$ podman top -l capeff
EFFECTIVE CAPS
AUDIT_WRITE,CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,MKNOD,NET_BIND_SERVICE,NET_RAW,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHROOT

Cuando lanzas un contenedor con --privileged modo, el contenedor se inicia con la lista completa de características.

$ podman run --privileged -d fedora sleep 100
d571acd1ccda2e6eb31602bf509e21d632cca3d8d524781b0a0123fef17e99f4
$ podman top -l capeff
EFFECTIVE CAPS
full

Nota: En los contenedores no rooteados, los procesos de contenedor obtienen la funcionalidad completa del espacio de nombres. Estos no son lo mismo que la funcionalidad raíz completa. Estas NO son capacidades reales, solo capacidades en el espacio de nombres del usuario. Por ejemplo, un proceso con CAP_SETUID puede cambiar su UID a todos los UID asignados en el espacio de nombres del usuario, pero no puede cambiar el UID a un UID no asignado en el espacio de nombres del usuario. Cuando se ejecuta un contenedor raíz sin usar el espacio de nombres de usuario, un proceso con CAP_SETUID PUEDE cambiar su UID a cualquier UID en el sistema.

Puede manipular las funciones disponibles para un contenedor sin ejecutar --privileged modo usando el --cap-add y --cap-drop banderas Por ejemplo, si desea ejecutar el contenedor con todas las funciones, puede ejecutar:

$ podman run --cap-add=all -d fedora sleep 100
9d167c4c0980e70623598dd718b685c0aead6d32c4bb2da35f50f8a58cbc66ea
$ podman top -l capeff
EFFECTIVE CAPS
full

Utilizando --cap-drop=all --cap-add setuid ejecutaría un contenedor solo con la capacidad setuid.

$ podman run --cap-drop=all --cap-add=setuid -d fedora sleep 100
d7f9954649024e20604ae995c9a05b1efcd7194b3e019f3495a24bfe4779c6aa
$ podman top -l capeff
EFFECTIVE CAPS
SETUID

Aquí hay un enlace a un hablar Le di a Devcon.cz formas de aumentar la seguridad en los contenedores. La conferencia cubre muchas de estas características de seguridad y cómo mejorarlas.

Filtrado de llamadas del sistema - SECOMP

Los motores de contenedores controlan las tablas de llamadas del sistema disponibles para los procesos dentro del contenedor. Esto limita la superficie de ataque del kernel de Linux al evitar que los procesos del contenedor ejecuten llamadas al sistema dentro del contenedor. Si una llamada al sistema puede causar una explotación del kernel y permitir que un contenedor se rompa, entonces si la llamada al sistema no está disponible para los procesos en el contenedor, está evitando la explosión. De forma predeterminada, los motores de contenedores descartan muchas llamadas al sistema. Recientemente escribimos un Blog sobre cómo depositar muchos más.

$ podman run -d fedora sleep 100
7ba4decb298a0e38fe0140b8bf039a662f4cd0fd666cd7a7f95d1bc12fdddecc
$ podman top -l seccomp
SECCOMP
filter

Si ejecuta el --privileged indicador, los motores de contenedores no utilizan filtros de llamadas al sistema SECOMP:

$ podman run --privileged -d fedora sleep 100
1469d3629d787e11100e3e9d011c97ff0249df1092b24af874f4e1be167f3852
$ podman top -l seccomp
SECCOMP
disabled

También puede deshabilitar el filtrado de llamadas del sistema usando el --security-opt seccomp:unconfined opciones sin ejecutar todo el --privileged bandera.

$ podman run --security-opt seccomp=unconfined -d fedora sleep 100
c18858a963d2e80e25ed1d118a6e48072047d69fc6efec23b26362408a8a71d3
$ podman top -l seccomp
SECCOMP
disabled

SELinux

SELinux es un sistema de etiquetado. Cada proceso y cada objeto del sistema de archivos tiene una etiqueta. Las políticas de SELinux definen reglas sobre lo que se le permite hacer a una etiqueta de proceso con todas las demás etiquetas en el sistema. Creo que SELinux es la mejor herramienta para controlar las interrupciones del sistema de archivos de contenedores. Los motores de contenedores lanzan procesos de contenedores con una sola etiqueta SELinux confinada, generalmente container_t, luego coloque el contenedor dentro del contenedor a etiquetar container_file_t. Las reglas de la política de SELinux básicamente dicen que el container_t los procesos solo pueden leer/escribir/ejecutar archivos etiquetados container_file_t. Si un proceso contenedor se escapa del contenedor e intenta escribir en el contenido del host, el kernel de Linux deniega el acceso y solo permite que el proceso contenedor escriba en el contenido etiquetado. container_file_t.

$ podman run -d fedora sleep 100
d4194babf6b877c7100e79de92cd6717166f7302113018686cea650ea40bd7cb
$ podman top -l label
LABEL
system_u:system_r:container_t:s0:c647,c780

Cuando corres con el --privileged , las etiquetas de SELinux están deshabilitadas y el contenedor se ejecuta con la etiqueta con la que se ejecutó el motor del contenedor. Esta etiqueta suele ser unconfined y tiene acceso total a las etiquetas que hace el motor del contenedor. En modo sin raíz, el contenedor se ejecuta con container_runtime_t. En modo raíz, funciona con spc_t. Lo principal de estas dos etiquetas es que no hay una contención adicional en el proceso del contenedor que la que había en el proceso del motor del contenedor.

$ podman run --privileged -d fedora sleep 100
23770ed2fef88b6a674af733a7a80b0d29bfa6a6db2888edf810eaa55ee2d93e
$ podman top -l label
LABEL
unconfined_u:system_r:container_runtime_t:s0

Al igual que otros mecanismos de seguridad, la contención de SELinux también se puede desactivar directamente sin necesidad de un --privilege Moda.

$ podman run --security-opt label=disable -d fedora sleep 100
08d6170f71313bc98293c77686e41cebc3041e82eea189bd8c74d5b60290102f
$ podman top -l label
LABEL
unconfined_u:system_r:container_runtime_t:s0

Espacios de nombres

Lo que a veces sorprende a los usuarios es que los espacios de nombres NO se ven afectados por la --privileged bandera. Esto significa que los procesos de contenedores aún viven en el mundo de la virtualización de contenedores. Incluso si no tienen habilitadas las restricciones de seguridad, no ven todos los procesos en el sistema host o la red, por ejemplo. Los usuarios pueden deshabilitar espacios de nombres individuales usando el --pid=host, --net=host, --user=host, --ipc=host, --uts=host banderas de motor de contenedor. Hace años definí estos contenedores como contenedores súper privilegiados.

$ podman top -l | wc -l
2

Como puede ver, por defecto, top muestra un solo proceso ejecutándose en el contenedor, junto con el encabezado:

$ podman run --pid=host -d fedora sleep 100
a90f2ccc335343a649dfdd777e252319a16a786a801da2462d2a4dbe0d8f55ad
$ podman top -l | wc -l
421

Cuando ejecuto el contenedor con --pid=host, el motor del contenedor no usa el espacio de nombres PID y los procesos del contenedor ven todos los procesos en el host, así como los procesos dentro del contenedor.

De la misma forma, --net=host deshabilita el espacio de nombres de la red, lo que permite que los procesos del contenedor utilicen la red del host.

espacio de nombre de usuario

El espacio de nombres de usuario de los motores de contenedores no se ve afectado por la --privileged bandera. Los motores de contenedores NO utilizan el espacio de nombre de usuario predeterminado. Sin embargo, los contenedores sin raíz todavía lo usan para montar sistemas de archivos y usar múltiples UID. En el caso de que no sea raíz, el espacio de nombres de usuario no se puede deshabilitar; es necesario para ejecutar contenedores sin root. Los espacios de nombres de usuario evitan ciertos privilegios y agregan una seguridad considerable.

Uso de versiones recientes de Podman Contenedores.conf, que le permite cambiar el comportamiento predeterminado del motor con respecto a los espacios de nombres. Si desea que todos sus contenedores no usen un espacio de nombres de red predeterminado, puede establecerlo en Contenedores.conf.

Conclusión

Como ingeniero de seguridad, no me gustan para nada los usuarios que usan el --privileged Moda. Me gustaría que averigüen qué privilegios requiere su contenedor y que se ejecuten de la forma más segura posible, o mejor aún, que rediseñen su aplicación para que se ejecute sin requerir tantos privilegios. Es un poco como usar setenforce 0 en el mundo SELinux, y sabes cuánto me encanta. Pero la conclusión es que necesitamos que los usuarios de motores de contenedores entiendan lo que sucede cuando usan el --privileged flag, y por qué a veces necesitan deshabilitar características adicionales para que su contenedor funcione correctamente.

La comunidad de código abierto está trabajando en herramientas además de motores de contenedores para que esto sea posible. Estos son algunos ejemplos de estas herramientas:

  • Údica: Una herramienta para crear una política SELinux personalizada basada en la configuración del contenedor.
  • gancho oci-seccomp-bpf: una herramienta para descubrir las llamadas al sistema utilizadas por un contenedor y generar automáticamente un filtro de reglas seccomp personalizado.

Artículos de interés

Subir