Skip to main content

Implicaciones de seguridad de los operadores de Kubernetes

Escrito por
Headshot of Chris Laux

Chris Laux

Snyk logo overlapping with Kubernetes logo

15 de febrero de 2022

0 minutos de lectura

Administrar recursos en las primeras versiones de Kubernetes era sencillo: podíamos definir recursos con formato YAML y enviar esas definiciones al clúster. Sin embargo, esto requería demasiado trabajo manual y a un nivel demasiado bajo.

El siguiente paso en la evolución de Kubernetes fue usar gráficos de Helm. A veces llamado «el administrador de paquetes para Kubernetes», Helm permitía a los desarrolladores compartir configuraciones completas de aplicaciones mediante un lenguaje de plantillas. Así, compartir configuraciones era fácil y podíamos implementar gráficos cómodamente con un solo comando.

Pero Helm es un complemento externo y sus capacidades se limitan a lo que podemos implementar mediante la API existente de Kubernetes. Por eso, el siguiente paso lógico para Kubernetes fue ampliar su API mediante operadores. Esto nos permitió agregar funcionalidades personalizadas desde el clúster.

Los operadores de Kubernetes proporcionan bucles de control para realizar tareas que antes llevaban a cabo operadores humanos. Para crear un operador de Kubernetes, el desarrollador define código personalizado que interactúa con la API de Kubernetes y guía automáticamente el ciclo de vida de la aplicación. Este código personalizado suele ejecutarse en uno o más pods del clúster, aunque también puede interactuar con el clúster desde el exterior con la autenticación adecuada.

¿Te parece que esta configuración podría ser un punto vulnerable? Así es. Como siempre, las nuevas abstracciones traen nuevos problemas de seguridad de Kubernetes.

En esta publicación, veremos ejemplos de cómo limitar correctamente los permisos de los operadores, las funciones complementarias de quienes los crean y quienes los usan para garantizar la seguridad, y algunas formas de usar operadores para proteger mejor los servicios de Kubernetes.

Seguridad de Kubernetes con RBAC

Si implementamos un operador de Kubernetes en un clúster, debemos considerar las prácticas generales de seguridad de Kubernetes como base para los aspectos específicos del operador. Primero, veamos estas consideraciones generales.

El principal sistema de permisos de Kubernetes es la autorización mediante control de acceso basado en roles (RBAC), que debe habilitarse al iniciar el clúster. Este sistema ofrece recursos de permisos adicionales: Roles y RoleBindings, que solo se aplican al espacio de nombres donde se definen; y ClusterRolesy ClusterRoleBindings, que se aplican a todo el clúster (y solo deben usarse si es necesario).

Los Roles definen cómo los actores con privilegios pueden acceder a los recursos, y los RoleBindings vinculan actores humanos o componentes de software con los Roles. Al igual que con la seguridad general de los sistemas operativos, es importante otorgar únicamente los permisos estrictamente necesarios y revisar periódicamente los permisos concedidos para confirmar que todavía se necesitan.

Este es un ejemplo de un rol que permite el acceso de lectura a los recursos de pods en el espacio de nombres especificado:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: my-webserver
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

La cadena vacía de apiGroups indica la API principal. Para que coincida, podemos definir un vínculo de rol que asigne este rol a una cuenta de usuario específica:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
Metadata:
  namespace: my-webserver
  name: read-pods
subjects:
- kind: User
  name: emilio
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Las implementaciones de operadores suelen incluir sus propios conjuntos de roles y vínculos. El usuario debe revisarlos, junto con la documentación, para saber exactamente qué permisos concede un tercero en su clúster.

Ámbitos y permisos

La seguridad de los operadores de Kubernetes se basa en una cadena de confianza. Esta comienza con quienes crearon el operador y su repositorio, y continúa con la forma en que se distribuye al clúster del usuario, entre otros aspectos. Al igual que con el sistema RBAC, tiene sentido limitar al máximo los permisos del operador. Por desgracia, la mayoría de los operadores de Kubernetes necesitan privilegios bastante amplios para cumplir su función, así que los desarrolladores deben encontrar un equilibrio cuidadoso entre seguridad y utilidad.

Al igual que los roles y sus vínculos, los operadores pueden limitarse al ámbito de un espacio de nombres (con alcance de espacio de nombres) o funcionar en todos los espacios de nombres del clúster (con alcance de clúster). Las aplicaciones de Kubernetes pueden alojarse en espacios de nombres separados para aislarlas entre sí y del sistema de Kubernetes.

Siempre que sea posible, quienes desarrollan operadores deben elegir variantes con alcance de espacio de nombres. Esto evita que los problemas se propaguen a otras implementaciones del mismo clúster. Si un operador debe ofrecer servicios a todas las implementaciones de un clúster, se necesitan operadores con alcance de clúster. Un ejemplo conocido es una implementación que aprovisiona certificados automáticamente para las aplicaciones.

Una forma de evitar que los pods y los contenedores manipulen el resto del sistema de Kubernetes es usar securityContexts, que también se pueden aplicar a los contenedores de operadores. Este es un ejemplo de un fragmento YAML que limita los permisos de un pod:

securityContext:
  privileged: false
  allowPrivilegeEscalation: false
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 1000
  readOnlyRootFilesystem: true

Suponemos que, en este ejemplo, el contenedor se ejecuta en el clúster de Kubernetes sobre el que opera. Con este fragmento, impedimos que los procesos obtengan privilegios de raíz y modifiquen su sistema de archivos raíz. Si el operador se ve comprometido por algún motivo, las acciones que el atacante puede realizar en el sistema host serán limitadas.

El siguiente paso es definir restricciones de seguridad para los pods en todo el clúster. Antes de Kubernetes 1.21, esto se hacía con el objeto PodSecurityPolicy (PSP), que ahora está obsoleto. Su sucesor, PodSecurityAdmission (PSA), es una función beta en Kubernetes 1.23.

Por desgracia, este nuevo PSA no permite el mismo nivel de control personalizado y detallado que el YAML del ejemplo anterior. En cambio, podemos elegir entre tres niveles de políticas nuevos —privileged, baseline y restricted— para aplicarlos a cada espacio de nombres.

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

En este ejemplo, nuestro espacio de nombres está configurado para aplicar el nivel de seguridad intermedio baseline y emitir advertencias en el nivel llamado, muy apropiadamente, restricted. Las definiciones exactas de estas políticas se encuentran en la documentación de Kubernetes sobre Estándares de seguridad de pods.

Beneficios de seguridad de los operadores

Aunque los operadores de Kubernetes plantean algunas consideraciones de seguridad, también pueden hacer que un clúster sea más seguro. Como mencionamos antes, realizan las acciones y configuraciones que normalmente se asignan a operadores humanos, quienes pueden cometer errores y los cometen con regularidad. La configuración y el control ejecutados por un programa son más confiables y reproducibles que las acciones de un operador humano.

Cuando se crean correctamente, los operadores eliminan errores por negligencia e incluso por malicia. La velocidad de respuesta también es un factor esencial para la seguridad. Los operadores de software tienen una ventaja considerable sobre los humanos al responder a problemas, tanto para alertar sobre ellos como para resolverlos.

La mayoría de las implementaciones en un clúster se ocupan de las operaciones diarias de la aplicación, pero también es posible implementar servicios dedicados de administración de seguridad directamente en el clúster. Lo ideal es que un operador los controle mediante su bucle de control.

Soluciones para implementaciones seguras de Kubernetes

El desarrollo de operadores de Kubernetes ha permitido ampliar la API de Kubernetes con lógica personalizada dentro del clúster. Si se gestiona mal, esta capacidad adicional puede ser aprovechada por actores maliciosos. Sin embargo, como desarrolladores, podemos garantizar la seguridad de nuestras implementaciones de Kubernetes mediante reglas de permisos y otras restricciones. La principal función de Kubernetes para administrar permisos es RBAC, que permite especificar privilegios.

Los operadores también pueden aportar beneficios de seguridad al reemplazar procesos manuales que suelen estar expuestos a errores humanos. Permiten ejecutar servicios de seguridad dedicados, como Snyk, dentro de un clúster.

Snyk ofrece soluciones para las posibles vulnerabilidades que describimos en esta publicación y muchas otras, ya que alerta a los desarrolladores sobre los problemas de seguridad en cuanto aparecen durante la programación. En particular, Snyk ofrece un operador de Kubernetes que podemos instalar en nuestro clúster. A partir de ahí, Snyk detecta y reporta las vulnerabilidades que surgen en las cargas de trabajo existentes o nuevas del clúster.

Protege la infraestructura desde el origen

Snyk automatiza la seguridad y el cumplimiento de IaC en los flujos de trabajo, y detecta recursos con desviaciones de configuración y recursos faltantes.