Varios equipos, un clúster, sin que nadie pise a nadie

Un namespace es una frontera de nombres, no una frontera de seguridad. Cuotas, política de red que deniega por defecto, identidad de carga de trabajo y una respuesta clara sobre el aislamiento de nodos son lo que hace funcionar un clúster compartido.

Las dos formas en que esto sale mal son opuestas. En la primera, todo vive en un clúster con un namespace por equipo, sin cuotas, sin política de red y con permisos de administración repartidos porque alguien necesitó depurar una vez. Un trabajo desbocado de un equipo deja sin recursos al grupo de nodos, y cualquier pod puede alcanzar cualquier otro pod y el extremo de metadatos de la nube. En la segunda, el equipo de plataforma dice que el multiinquilinato no es seguro, y dieciocho meses después hay veintitrés clústeres, cada uno con su cuota de plano de control, su pasarela NAT, su atraso de actualizaciones y su propia deriva.

Los dos son caros. El punto medio es alcanzable, y se apoya en ser preciso sobre lo que un namespace te da de verdad.

Qué es y qué no es un namespace

Un namespace te da un ámbito para los nombres, un destino para el control de acceso basado en roles, un sitio donde enganchar cuotas de recursos y un selector para la política de red. Es un conjunto genuinamente útil.

Un namespace no te da aislamiento del núcleo, del nodo ni de la red por defecto. Dos pods en namespaces distintos sobre el mismo nodo comparten núcleo. Sin política de red, pueden hablar libremente. Sin cuotas, compiten por la misma CPU y la misma memoria. Un escape de contenedor llega al nodo, y desde el nodo llega a todas las cargas que hay en él.

Así que el encuadre honesto es: los namespaces son la unidad de gestión, y el aislamiento es un conjunto de controles que añades encima. Cuántos de esos controles necesitas depende de una pregunta que deberías responder explícitamente antes de diseñar nada.

Multiinquilinato blando o duro

Blando significa que los inquilinos son equipos dentro de tu organización. No son adversarios. Los controles existen para evitar accidentes, contener el radio de impacto y hacer atribuibles los costes. Esto cubre la abrumadora mayoría de los clústeres compartidos, y todo lo de abajo es suficiente para ello.

Duro significa que un inquilino puede ser hostil: código suministrado por un cliente, cargas no confiables, o una frontera regulatoria que trata a otro inquilino como un tercero. Aquí los controles a nivel de namespace no bastan. Necesitas separación a nivel de nodo, un tiempo de ejecución aislado, o clústeres separados, y la respuesta honesta suele ser clústeres separados.

La mayoría de los equipos que piden multiinquilinato tienen el caso blando y les están vendiendo el duro.

Cuotas y rangos límite, o un equipo se queda el clúster

Una cuota de recursos por namespace pone techo al total de CPU, memoria, almacenamiento y número de objetos que puede consumir un equipo. Sin ella, un solo despliegue con muchas réplicas y sin límite puede consumir el grupo de nodos entero, y el autoescalador comprará amablemente los nodos para que pueda.

Pon cuotas sobre las peticiones y sobre los límites, e incluye el número de objetos, porque el agotamiento no siempre es de CPU. Diez mil ConfigMaps o un bucle desbocado creando trabajos harán daño a etcd mucho antes que al grupo de nodos.

Acompaña la cuota con un rango límite que fije peticiones y límites por defecto para los pods que no declaren ninguno. Sin él, un pod sin peticiones se planifica como de mejor esfuerzo, se desaloja el primero bajo presión y no contribuye a la contabilidad de la cuota, lo que en silencio derrota el mecanismo entero.

Las cuotas además hacen posible la atribución de coste, que es el argumento de repartir la factura de un clúster compartido. Un equipo con cuota sabe lo que ha reclamado, y una reclamación es algo por lo que puedes facturar.

La política de red que deniega por defecto es el control de más valor

Por defecto, todos los pods de un clúster de Kubernetes pueden abrir una conexión con cualquier otro pod, en cualquier namespace. A casi todos los ingenieros les sorprende, y es el hallazgo que hace trivial el movimiento lateral después de comprometer una sola carga.

El arreglo es una política de denegación por defecto para el tráfico entrante en todos los namespaces, más reglas explícitas de permiso para el tráfico que debe existir. Hazlo namespace a namespace en modo auditoría primero si tu complemento de red lo admite, porque la primera aplicación siempre rompe algo que nadie documentó, normalmente el DNS.

Tres detalles que conviene decir: permite el DNS hacia el servicio de nombres del clúster explícitamente o se rompe todo; controla la salida y no solo la entrada, porque la salida es lo que usa un atacante; y bloquea el acceso al extremo de metadatos de la instancia desde los pods, porque llegar ahí es la ruta clásica de un contenedor comprometido a las credenciales de la nube.

Tu complemento de red tiene que admitir política de red para que nada de esto sea real. Algunos no, y un clúster donde las políticas las acepta el servidor de API y las ignora en silencio el plano de datos es el peor estado posible.

Identidad: del pod a la nube sin clave estática

Las cargas de cada equipo necesitan sus propios permisos en la nube, y la forma incorrecta de hacerlo es una clave de cuenta de servicio dentro de un Secret.

Todas las ofertas gestionadas de Kubernetes admiten ya federar una cuenta de servicio de Kubernetes con una identidad de la nube, de modo que un pod recibe un token de vida corta con un rol acotado y no existe ninguna credencial estática en ninguna parte. Vincula una cuenta de servicio de Kubernetes a un rol de nube por carga de trabajo, no uno por namespace, para que el radio de impacto de un compromiso sea una carga.

Dentro del clúster, los roles deberían tener alcance de namespace, nunca de clúster, y los recursos de alcance de clúster que un equipo puede ver deberían ser una lista explícita y pequeña. El permiso de crear pods en un namespace es en la práctica el permiso de ejecutar lo que sea como las cuentas de servicio de ese namespace, así que trata los permisos de edición sobre un namespace como algo importante.

Aplica el mismo instinto que en mínimo privilegio en la nube: concede a una carga, no a un equipo, y concede los verbos que se usan de verdad.

Aislamiento de nodos, cuando hace falta de verdad

Si una carga procesa datos que no pueden compartir núcleo con otros inquilinos, separa los nodos. Los taints y las tolerancias más los selectores de nodo te dan grupos de nodos dedicados por inquilino sensible. Te cuesta eficiencia de empaquetado, que es el intercambio.

Los tiempos de ejecución aislados ponen una frontera más fuerte alrededor del contenedor a cambio de rendimiento, y son la respuesta correcta para código genuinamente no confiable. La admisión de seguridad de pods en su nivel restringido debería ser la base en todas partes de todos modos, y los motores de política rellenan los huecos, que es el tema de Kyverno o Gatekeeper.

Las cosas compartidas que no son de nadie

Los fallos de un clúster compartido rara vez vienen de las partes aisladas. Vienen de las compartidas:

  • El DNS del clúster. El bucle mal configurado de un equipo puede saturar el servicio de nombres para todos. Pon límites de consulta por namespace si tu DNS lo admite, y vigílalo como un servicio de cara al inquilino.
  • La entrada o la pasarela. Una reclamación de nombre de host que choca con otra, el cambio de TLS de un equipo que afecta a un escuchador compartido. Es exactamente la separación que Gateway API existe para dar.
  • Las definiciones de recursos personalizados. Tienen alcance de clúster y son compartidas. Un equipo que instala un operador cambia el clúster para todos.
  • Las actualizaciones. Una sola ventana de mantenimiento compartida para todos los inquilinos, lo que significa que el equipo de plataforma es dueño de la coordinación, como se ve en actualizaciones de clúster.
  • Los webhooks de admisión. Un webhook que falla bloquea los despliegues de todos, incluido el despliegue que lo arreglaría.

Cuándo partir el clúster en su lugar

Parte cuando los inquilinos tengan fronteras de cumplimiento distintas, cadencias de actualización que no puedas reconciliar, código genuinamente no confiable o regiones distintas. No partas porque los equipos quieran autonomía sobre cosas que deberían ser estándar de todas formas.

La comparación de coste honesta es una cuota de plano de control más una pasarela NAT más las cargas de sistema base más el tiempo operativo por clúster, contra el tiempo de ingeniería de plataforma de montar los controles de arriba una sola vez. Para un puñado de equipos internos, un clúster bien gobernado gana con comodidad. Lo dimensionamos explícitamente durante la fase de plataforma de un proyecto cloud.

Qué hacer esta semana

Elige dos namespaces que no deberían tener nada que ver entre sí, entra en un pod de uno e intenta abrir una conexión con un servicio del otro. Después intenta llegar al extremo de metadatos de la nube. Si las dos cosas funcionan, has encontrado la tarde de trabajo con más valor disponible en tu clúster, y una política de denegación por defecto con permiso explícito de DNS es por donde empieza.

ConsultorIA

¿Quieres esto en tu nube?

El diagnóstico de diez días en solo lectura es gratuito, y con Skyline puedes ver tu entorno en un mapa antes de escribirnos.

Artículos relacionados