securityContext de Kubernetes: capacidades de Linux en Kubernetes
26 de enero de 2021
0 minutos de lecturaHace mucho tiempo, en los anales de la historia, los sistemas operativos Unix tenían un modelo de permisos relativamente sencillo. O eras un usuario normal o eras root, el superusuario con permisos para hacer cualquier cosa.
Aunque se podían otorgar permisos elevados a usuarios normales en archivos o directorios, casi todas las funciones a nivel del kernel estaban restringidas al usuario root. Si tu aplicación necesitaba una sola llamada al kernel para funcionar, había que otorgarle privilegios SUID, lo que en la práctica daba privilegios de root al proceso si lo iniciaba un usuario normal.
Este modelo funcionaba bastante bien en la época en que las máquinas físicas eran utilizadas por grupos relativamente pequeños de usuarios, pero esa granularidad limitada ya no era adecuada para la era moderna. Para ofrecer más flexibilidad y seguridad, los desarrolladores del kernel de Linux idearon una solución mucho más granular: agrupar las llamadas al kernel en capacidades y permitir que estas se asignaran a procesos individuales. Así, si una aplicación necesitaba una sola llamada al kernel, bastaba con asignarle esa capacidad específica, lo que limitaba la exposición de seguridad del sistema.
Administración de privilegios en contenedores de Kubernetes
Entonces, ¿cómo funciona esto en los contenedores?
Un contenedor no es más que un proceso que se ejecuta en el sistema y está aislado mediante cgroups y espacios de nombres del kernel. Esto significa que se le pueden asignar capacidades igual que a cualquier otro proceso. El entorno de ejecución del contenedor se encarga de hacerlo cuando lo crea.
Por lo general, el entorno de ejecución del contenedor le asigna un conjunto predeterminado de capacidades (puedes consultar aquí el conjunto predeterminado que proporciona Docker) y también ofrece un mecanismo para agregar o quitar capacidades. Si ejecutas un contenedor con la marca --privileged, le otorgas todas las capacidades. Si lo ejecutas como root, esto puede generar un problema de seguridad grave, un tema que analicé en una publicación anterior.
Cómo configurar las capacidades de un contenedor en securityContext de Kubernetes
Al usar el entorno de ejecución de contenedores en Kubernetes, el plano de control de Kubernetes utiliza estos controles para definir con qué capacidades debe iniciarse nuestro contenedor. La configuración de las capacidades se presenta al usuario mediante varios ajustes en la sección securityContext del YAML de un contenedor. La configuración es similar a la siguiente:
En este caso, quitaríamos todas las capacidades y luego agregaríamos la capacidad CAP_NET_ADMIN. Si la sección de capacidades en securityContext está vacía, obtendremos el conjunto predeterminado que define el entorno de ejecución del contenedor. Por lo general, este conjunto es bastante amplio y puede incluir muchas más capacidades de las que necesita nuestra aplicación. Iniciemos un pod en Kubernetes para ver qué capacidades obtenemos.
Primero, crearemos un archivo YAML para implementar un Pod. En esta configuración, usamos una imagen de Docker Hub, basada en Alpine, que incluye la herramienta capsh. Esta nos permitirá ver qué capacidades tiene nuestro contenedor.
Ten en cuenta que no definimos ninguna sección securityContext para este contenedor, así que se aplicarán los valores predeterminados del sistema. Recuerda que, si no especificamos un usuario en Kubernetes, el contenedor se ejecutará como el usuario predeterminado indicado en el Dockerfile con el que se creó. En muchos contenedores, ese usuario es root.
Cuando el pod esté en ejecución, podemos abrir una shell dentro de él y ejecutar capsh para revisar las capacidades:
Cómo quitar capacidades en securityContext de Kubernetes
Como podemos ver, de forma predeterminada se ejecuta como root y cuenta con bastantes capacidades. Este es el conjunto definido de forma predeterminada por el entorno de ejecución del contenedor. Ahora intentemos quitar una de esas capacidades en la configuración de securityContext. Quitaremos la capacidad de crear nodos del sistema de archivos, es decir, CAP_MKNOD:
Si lo comparamos con el primer pod, podemos ver que este ya no tiene la capacidad cap_mknod. Además de quitar capacidades individuales, también podemos quitarlas todas mediante securityContext:
Si no tenemos ninguna capacidad, algunas funciones del sistema fallarán al intentar ejecutarlas. Por ejemplo, intentemos instalar bash con apk:
Aunque nuestro contenedor se ejecuta como root, la instalación del paquete bash falla porque necesita configurar los permisos del sistema de archivos y quitamos esa capacidad del contenedor. Si quisiéramos impedir que alguien instalara software en nuestro contenedor, administrar esos permisos mediante capacidades sería una opción.
Aunque capsh nos muestra las capacidades del contenedor en un formato fácil de leer, no es la única manera de averiguar cuáles están disponibles. También podemos consultar esta información directamente desde el sistema de archivos proc, sin instalar software adicional:
En el archivo /proc/1/status, las capacidades se muestran como un mapa de bits. Como este contenedor no tiene ninguna capacidad habilitada, todos los valores son cero. Si revisamos el mismo archivo en el contenedor original, donde todas las capacidades están habilitadas:
No es tan fácil de leer como el resultado de capsh, pero cada bit que aparece aquí representa una capacidad específica, tal como se define en el archivo de encabezado correspondiente del kernel.
Obtén más información sobre cómo mejorar la seguridad de Kubernetes al quitar las capacidades predeterminadas de un contenedor.
Cómo agregar capacidades en securityContext de Kubernetes
Además de quitar capacidades, también podemos volver a agregarlas. Tomemos el ejemplo anterior y agreguemos una sola capacidad a la configuración de securityContext, nuevamente CAP_MKNOD:
En este ejemplo, podemos ver que ahora solo tenemos habilitada esa capacidad.
Principio de privilegio mínimo en securityContext de Kubernetes
En esta publicación vimos cómo funcionan las capacidades en los contenedores, cómo se configuran en securityContext de Kubernetes y cómo las controla el entorno de ejecución del contenedor.
Si seguimos el principio de privilegio mínimo, la práctica recomendada desde el punto de vista de la seguridad es proporcionar solo las capacidades que realmente necesita nuestro contenedor. Analicé este tema en una publicación anterior. Puede sorprender que la mayoría de los procesos no necesiten ninguna capacidad del kernel, ya que incluso los permisos elevados suelen poder controlarse mediante permisos a nivel de archivo.
Empieza por quitar todas las capacidades en securityContext y luego ve agregando solo las que necesites. Puedes depurar los errores consultando los resultados de herramientas como SELinux para identificar qué capacidades podrían estar causando el problema. También debemos tener en cuenta que los contenedores de Kubernetes pueden ejecutarse como root, a menos que especifiquemos otro usuario.
Cómo aplicar la configuración de capacidades de securityContext en Kubernetes
Si queremos asegurarnos de que se configuren opciones de securityContext, como las capacidades y la ejecución como usuario no root, podemos usar controladores de admisión en nuestro clúster de Kubernetes para evitar que se inicien contenedores sin la configuración de seguridad adecuada. Kubernetes incluye el controlador PodSecurityPolicy, que permite aplicar la configuración de securityContext.
Sin embargo, ten en cuenta que se dejará de usar a partir de la versión 1.21, en favor de proyectos mantenidos externamente, como Open Policy Agent.
También necesitamos integrar la visibilidad y la corrección de este tipo de configuraciones de seguridad directamente en el proceso de desarrollo. Snyk puede analizar tus archivos YAML de Kubernetes, detectar configuraciones inseguras de capacidades y otros ajustes de securityContext, y ofrecer recomendaciones para corregirlas directamente en los flujos de trabajo de los desarrolladores. Esta funcionalidad está disponible mediante Snyk CLI y también puede integrarse directamente con sistemas de administración de código fuente y de integración continua:

¿Te interesa probar esta función? ¡Crea hoy una cuenta gratis!
Empieza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag viendo nuestro taller virtual introductorio a pedido.
