jueves, 15 de marzo de 2018

SELinux y DOCKER all rock


 Resultado de imagen de selinux

En google cloud español nos tomamos la seguridad como un punto importante, y he localizado estos apuntes que se aplican en los contenedores y me ha parecido importantes por la utilidad que tienen. Viene de una adaptacion de la web de danwash https://danwalsh.livejournal.com/78312.html

SELinux esta antes de que aparecieran los contenedores por eso cuando nos cambiamos  a trabajar con contenedores hace varios años, pero una de las primeras cosas hay que con los contenedores es agregar el soporte de SELinux. Todos los proyectos de contenedores , incluyendo CRI-O, Podman, Buildah, así como Docker, Moby, Rocket, runc, systemd-nspawn, lxc ... todos tienen soporte SELinux. También  el paquete de políticas container-selinux del que dependen todos estos tiempos de ejecución de contenedores.

Cualquier forma en que los tiempos de ejecución de los contenedores comenzaron a agregar las capacidades de no-nuevos privilegios hace un par de años.

no_new_privs

La característica del kernel no_new_privs funciona de la siguiente manera:


  • Los procesos establecen un bit no_new_privs en el kernel que persiste en fork, clone, y exec.
  • El bit no_new_privs asegura que los procesos / procesos secundarios no obtengan ningún privilegio adicional.
  • El proceso no permite desarmar ningún bit no_new_privs una vez establecido.
  • Los procesos no_new_privs no pueden cambiar uid / gid u obtener otras capacidades, incluso si el proceso ejecuta archivos binarios o ejecutables setuid con bits de capacidad de archivo establecidos.
  • no_new_privs impide que los módulos de seguridad de Linux (LSM) como SELinux pasen a procesar etiquetas que no tienen acceso al proceso actual. Esto significa que un proceso de SELinux solo puede pasar a un tipo de proceso con menos privilegios.

Vaya, esa última bandera es un problema para contenedores y SELinux. Si estoy ejecutando un comando como

 
# podman run -ti --security-opt no-new-privileges fedora sh
 

En el sistema SELinux y, por lo general, el comando podman se ejecuta como unconfined_t, y normalmente podman solicita que el proceso del contenedor se inicie como container_t.

o

docker run -ti --security-opt no-new-privileges fedora sh

En el caso de Docker, el daemon docker generalmente se ejecuta como container_runtime_t. E intentará iniciar el contenedor como container_t.

Pero el usuario también pidió nuevos privilegios. Si se establecen ambos indicadores, el kernel no permitirá que el proceso pase de unconfined_t -> container_t. Y en el caso de Docker, el kernel no permitiría una transición de container_runtime_t -> container_t.

Bueno, puedes decir que es bastante obvio no_new_privileges se supone que es una medida de seguridad que impide que un proceso obtenga más privilegios, pero en este caso, en realidad nos impide disminuir su acceso a SELinux.

En el kernel y la política de SELinux tenían el concepto de "typebounds", donde un escritor de políticas podía escribir que un tipo de tipo tenía otro tipo. Por ejemplo

typebounds container_runtime_t container_t, y el kernel se aseguraría entonces de que container_t no tenga más reglas de permiso que container_runtime_t. Este concepto demostró ser problemático por dos razones.

Typebounds

La política de escritura para los typebounds fue muy difícil y en algunos casos tendríamos que agregar acceso adicional al tipo delimitador.

Un ejemplo de esto es SELinux puede controlar el `entrypoint` de un proceso. Por ejemplo, escribimos una política que dice que httpd_t solo se puede ingresar mediante ejecutables etiquetados con el tipo de punto de entrada httpd_exec_t.

También teníamos una regla de que container_runtime_t solo se puede ingresar a través del tipo de entrpoint de container_runtime_exec_t. Pero queríamos permitir que cualquier proceso se ejecutara dentro de un contenedor, escribimos una reglas que todos los tipos de ejecutables podrían usarse como puntos de entrada para container_t.

Con typebounds, necesitamos agregar todas estas reglas a container_runtime_t, lo que significa que deberíamos permitir que todos los ejecutables se ejecuten como container_runtime_t. y no es lo mas recomendable.

El segundo problema con typebounds y kernel y política solo permitía un único tipo de typebounds. Entonces, si quisiéramos permitir procesos unconfined_t para lanzar procesos container_t, terminaríamos escribiendo reglas como

typebounds unconfined_t container_runtime_t
typebounds container_runtime_t container_t.

Ahora unconfined_t necesitaría hacer crecer todas las reglas allow de container_runtime_t y container_t.

nnp_transitions

Bueno, me estaba quejando de esto a Lucas Vrabec, el tipo que se hizo cargo de la política de selinux, y él me cuenta acerca de esta nueva regla de permiso llamada nnp_transitions. El autor de la política podría escribir una regla en la política para decir que un proceso podría nnp_transition de un dominio a otro.

allow container_runtime_t confined_t: process2 nnp_transition;
allow unconfined_t confined_t: process2 nnp_transition;

Con un kernel bastante reciente, SELinux permitiría la transición incluso si se estableció el indicador de kernel no_new_privs, y las reglas de typebounds NO estaban en su lugar.

ME siento como un SELINux NEWBIE. Agregué las reglas sobre Fedora 27 y de repente todo comenzó a funcionar. A partir de RHEL7.5, esta función se transferirá nuevamente al kernel RHEL7.5 Increíble.

nosuid_transition

Mientras miraba las reglas nnp_transition, noté que también había un permiso nosuid_transition.

Con nosuid permite a las personas montar un sistema de archivos con el tag marcaco  nosuid, esto le dice al kernel que incluso si existe una aplicación setuid en este sistema de archivos, el kernel debería ignorarla y no permitir que un proceso gane privilegios a través del archivo.

Siempre quiere que los sistemas de archivos no confiables, como los dispositivos USB, se monten con esta bandera.

Bien, los sistemas SELinux ignoran de manera similar las reglas de transición en las etiquetas basadas en un sistema de archivos nosuid. Similar a nnp_transition, esto bloquea un proceso de transición de un dominio privilegiado a un dominio menos privilegiado. Pero el indicador nosuid_transtion nos permite decirle al kernel que permita transiciones de un dominio a otro, incluso si el sistema de archivos está marcado como nosuid.

allow container_runtime_t confined_t:process2 nosuid_transition;
allow unconfined_t container_t:process2 nosuid_transition;

Esto significa que incluso si un usuario usara podman para ejecutar un archivo en un sistema de archivos nosuid, se le permitiría la transición de unconfined_t a container_t.

Bueno, es bueno saber que todavía hay cosas que puedo aprender sobre SELinux.