K8s: Seguridad en Pods

Federico Catinello
Ingenia, Architectural Journeys

--

La unidad mínima de abstracción que nos ofrece Kubernetes para ejecutar aplicaciones se denomina Pod, y está influenciada principalmente por los contenedores; encuadrando conceptos como inmutabilidad, escalabilidad, agilidad, flexibilidad y portabilidad, entre otros.

Estos Pods se crean y se ejecutan en el contexto de un nodo worker, que se traduce básicamente en una máquina virtual o cualquier dispositivo con capacidades de cómputo y almacenamiento.

Con el fin de securizar las aplicaciones que ejecutan en la plataforma, Kubernetes ofrece dos estrategias, en forma nativa, cuyo principal propósito es implementar un modelo robusto y controlado de seguridad tanto en la creación como así también en la ejecución y utilización de recursos.

  • Una de ellas existe sólo en el contexto de ejecución de un Pod, y su principal propósito es limitar las capacidades que tiene un Pod sobre la infraestructura donde reside. Ésta se denomina Security Context.
  • Por otro lado, Kubernetes también ofrece validaciones a la hora de crear o actualizar un Pod, en donde se pueden especificar ciertos requerimientos que el manifiesto debe cumplir para crear o no el recurso. Éste caso se conoce como PodSecurityPolicy.

Desafortunadamente, PodSecurityPolicy se encuentra deprecado desde la versión 1.21, y será completamente eliminado a partir de la versión 1.25.

A causa de esta modificación, el Kubernetes Auth Special Interest Group, particularmente involucrado en estas cuestiones, ideó un nuevo estándar de seguridad para hacer frente a estas necesidades denominado Pod Security Standards (PSS), acompañado de una estrategia de implementación mediante controles de admisión denominada Pod Security Admission (PSA).

A continuación, se describirán estas estrategias con un nivel más de detalle.

Security Contexts

Kubernetes ofrece la posibilidad de manejar los mismos estándares de seguridad que ofrece Linux, como así también hereda y escala las utilidades de Docker, tales como el ID de usuario y las capacidades de Linux que se pueden agregar o eliminar del contenedor.

Algunas de las características ofrecidas son:

  • Discretionary Access Control: permisos para acceder a objetos (UID o GID).
  • Security Enhanced Linux (SELinux)
  • Modos privilegiados y no privilegiados de ejecución.
  • Capacidades de Linux
  • AppArmor: Perfiles que restringen las capacidades de programas en firma individual.
  • Seccomp: Filtra llamadas de sistema de un proceso.
  • AllowPrivilegeEscalation: Controla si un proceso puede obtener más privilegios que su proceso principal
  • readOnlyRootFilesystem: Monta el file sistem como sólo lectura.

Todas éstas funcionalidades se configuran mediante los SecurityContexts como parte de las especificaciones de un Pod y su/s contenedor/es, y el objetivo es controlar el accionar del mismo en tiempo de ejecución.

Veamos algunos ejemplos:

En este caso, el Pod de la imagen debe ejecutar en modo no privilegiado, utilizando el usuario de Linux con UID 1001.

En éste último caso, el contexto se define a nivel de contenedor dentro del Pod y nuevamente configura un UID específico de ejecución así también como una capacidad particular de Linux a incorporar.

Pod Security Standards (PSS) and Pod Security Admission (PSA)

De acuerdo a la definición formal que brinda Kubernetes, el PSS “define tres políticas distintas para cubrir ampliamente el espectro de seguridad. Éstas políticas son acumulables y van desde altamente permisivas hasta altamente restrictivas.”

Éstas políticas se definen de la siguiente manera:

  • Privileged: no hay restricción alguna, permitiendo el mayor nivel de permisos posibles. Es común utilizarlo en casos de agentes de logs, CNIs, drivers de almacenamiento.
  • Baseline: Restricción mínima que previene escaladas de privilegios conocidas. Acepta la configuración que viene por defecto en un Pod. Por otro lado, prohibe el uso de hostNetwork, hostPID, hostIPC, hostPath, hostPort, la incapacidad de agregar capacidades de Linux, y algunas otras restricciones extras.
  • Restricted: Restricción fuerte, consideradas las mejores prácticas para Pod hardening. Agrega algunas restricciones a la baseline como la incapacidad de correr con id de usuario o grupo privilegiado. Cuidado ya que algunas restricciones pueden impactar en la ejecución básica de las aplicaciones. Son específicamente útiles para aplicaciones de seguridad críticas.

Ahora bien, estas políticas de seguridad definen tres perfiles de ejecución de Pods, ordenados por niveles de privilegio vs restricción de acceso.

Para implementar estos controles definidos por el PSS, el control de admisión PSA opera en tres modos:

  • enforce: Violaciones de las políticas harán que el Pod sea rechazado y por ende, no creado.
  • audit: Violaciones de las políticas dispararán una anotación en el evento registrado en el log de auditoría. Pero de todas formas son permitidas.
  • warn: Violaciones de las políticas mostrarán una advertencia de cara al usuario, pero de todas formas son permitidas.

Éstos modos, junto con el perfil o restriccion que atacan, son configurados a nivel de Kubernetes Namespace, para lo cual se utilizarán labels o etiquetas, como se ve en el siguiente ejemplo:

apiVersion: v1 
kind: Namespace
metadata:
name: my-namespace
labels:
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restricted

Éste sería un escenario de extrema seguridad, donde se fuerza el rechazo de Pods que no cumplan con las políticas mas severas, así como también se audita y se notifica al usuario para tomar acción en forma inmediata.

Para el siguiente escenario:

apiVersion: v1
kind: Namespace
metadata:
name: my-namespace
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted

No se presenta un nivel de rechazo fuerte (sólo debería cumplir con políticas de base), pero si se notifica cualquier otra restricción fuerte que no se cumpla.

Qué sucede en el caso donde en el Namespace haya Pods existentes creados con anterioridad, previo a configurar el control de admisión?

El comportamiento de los modos audit y warn será sin dudas similar, notificando al log de auditoría y al usuario cuando las políticas no puedan ser validadas con éxito. Acá se pueden visualizar los mensajes de warning correspondientes:

Warning: existing pods in namespace “my-namespace” violate the new PodSecurity enforce level “restricted:latest”Warning: test-688f68dc87-htm8x: allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfilenamespace/my-namespace configured

Por otro lado, el modo enforce, no podrá eliminar Pods que ya hayan sido creados.

Qué otra opción tenemos para controlar políticas de seguridad en el cluster a nivel Pods?

Si no existiese la limitación de usar herramientas nativas a Kubernetes, se podría elegir entre un abanico de posibilidades que ofrece la comunidad y permiten reemplazar, o bien complementar los niveles de seguridad vistos recientemente.

Éstas herramientas, denominadas Policy-as-code (PaC), proveen recursos extras para guiar a usuarios al uso correcto del cluster, así como a prevenir comportamientos inadecuados.

Policy-as-Code

Al igual que PSA, PaC se basa en Kubernetes Dynamic Admission Controllers, los cuales interceptan las peticiones que arriban al Kubernetes API Server, pudiendo validar o transformar información en base a políticas escritas y almacenadas como código. Esta tranformación o mutación sucede en forma previa a que se realicen cambios a nivel cluster, por lo que lo convierte el lugar óptimo.

Existen varias soluciones de código abierto basadas en PaC disponibles para Kubernetes, entre las que se encuentran:

Conclusion

No importa cuál herramienta sea elegida para implementar un modelo de seguridad eficiente y robusto en Kubernetes, incluso muchas de ellas pueden ofrecer funcionalidades complejas si se utilizan en forma compuesta.

Lo que sí resulta de suma importancia, es tener claro qué se necesita hacer, entender cuáles son los objetivos a nivel seguridad para setear expectativas acordes, e identificar buenas prácticas en el proceso.

A continuación, algunas recomendaciones mínimas recopiladas a partir de la experiencia, que guiarán a una correcta definición de un modelo de seguridad de base en aplicaciones distribuidas que corren en Kubernetes:

  • Restringir los contenedores que ejecuten en modo privilegiado.
  • Ejecutar aplicaciones en modo usuario en la medida de lo posible (con UID y GID no root)
  • Evitar utilizar DinD (docker-in-docker) o montar el socket en el contenedor-. Existen alternativas para atacar esta problemática como kaniko, buildah, img, etc.
  • Restringir el uso de hostPath en lo posible, pero si resultase obligatorio, configurar el volumen como sólo lectura y restringir los prefijos que pueden ser utilizados.
  • Setear mínimos y máximos de uso de recursos para evitar resource contention y ataques DDoS. (recordar que un Pod sin limitaciones de consumo podría utilizar el máximo teórico de un nodo).
  • No permitir escalado de privilegios, salvo que sea realmente necesario.
  • Deshabilitar el montaje automático de ServiceAccount token, específicamente para pods que no necesitan acceso al API Server.
  • Deshabilitar service discovery, pero SOLO en aquellos casos donde no se requiera comunicación interna o DNS lookup, justamente para evitar sobrecargar al Pod de mucho contenido innecesario (variables con IP y DNS de sus Pods “vecinos”).

--

--