Simplifica la validación de recursos en Kubernetes con Validating Admission Policies y CEL
La validación dentro del cluster tiene muchos usos prácticos: evitar el borrado accidental o malintencionado de recursos, limitar la cantidad de réplicas que puede tener un deployment para gestionar mejor los recursos o exigir que ciertas anotaciones, labels o variables de entorno estén presentes (o ausentes), entre otros. Entender cómo funciona la validación de recursos dentro de un cluster de Kubernetes es bastante simple: cada vez que llega una solicitud que coincide con una regla de la API, se ejecuta un conjunto de políticas para determinar si se permite o se rechaza.
Esto resulta muy útil para los operadores del cluster, porque permite aplicar estándares y reglas específicas a quienes lo usan, como los desarrolladores. No sorprende que se haya vuelto un tema candente entre muchos de nuestros clientes y que hayan aparecido numerosos productos y empresas que buscan simplificar la creación y ejecución de validaciones para recursos de Kubernetes.
Al profundizar en el rol del Dynamic Admission Control (DAC) dentro del proceso de validación y en los desafíos que enfrentan los operadores del cluster, este artículo te ayudará a apreciar mejor la simplicidad y los beneficios de las Validating Admission Policies (VAP), el Common Expression Language (CEL) y su sintaxis tipo RBAC. Con ese contexto podrás tomar decisiones más informadas sobre la adopción de estas nuevas funciones en tus entornos de Kubernetes.
Si vienes por el código y los ejemplos, salta directo a la sección Validating Admission Policies. Para casos de uso más avanzados, revisa el artículo complementario: Validating Admission Policies en Kubernetes: casos de uso avanzados.
Desafíos al implementar Dynamic Admission Control

Fases del Admission Controller ( fuente)
Implementar la validación de solicitudes dentro del cluster no siempre es pan comido. Implica usar DAC, una herramienta poderosa pero no siempre amigable para quienes operan el cluster.
El DAC es un componente clave en el proceso de validación de Kubernetes. Permite a los administradores del cluster controlar la admisión de recursos interceptando las solicitudes a la API y aceptándolas o rechazándolas según un conjunto de políticas predefinidas. Así se obtiene un control granular sobre los recursos que se crean, actualizan o eliminan, garantizando que cumplan con estándares y reglas específicas.
Sin embargo, hoy por hoy usar DAC puede ser complicado para los operadores del cluster por su complejidad. En resumen, el proceso implica escribir un servidor de admission webhook, crear el recurso webhook, configurar la autenticación al API server si hace falta y administrar el certificado que se usa para TLS. Además, los operadores deben dominar conceptos de Kubernetes y buenas prácticas de seguridad, y manejar lenguajes de programación para crear y mantener la lógica de validación.
Aunque herramientas como OPA Gatekeeper pueden agilizar el proceso, los operadores aún tienen que desplegar y administrar el workload que corre en su cluster. ¡Pero tranquilo! Kubernetes incorporó un nuevo tipo de recurso llamado Validating Admission Policies (VAPs), disponible en alpha desde la v1.26, que ofrece una solución mucho más sencilla. Su simplicidad, que demostraremos en las próximas secciones, probablemente convierta a las VAPs en el estándar de facto para hacer validaciones dentro del cluster.
Te presentamos las VAPs: el mejor aliado del operador del cluster para validar recursos
Las VAPs aprovechan el CEL de Google para definir expresiones de validación que se ejecutan contra las solicitudes de la API que coincidan. Como las expresiones CEL se evalúan directamente en el API server, no hace falta escribir ni desplegar workloads personalizados para evaluar tus políticas. Esto vuelve la creación de políticas increíblemente directa y accesible frente a otros métodos, como los Validating Admission Webhooks mencionados antes.
Si te preocupa sobrecargar la API y afectar otros procesos, tranquilo: CEL incluye restricciones de recursos para mantener todo bajo control. Vale la pena conocer estas protecciones leyendo sobre las restricciones de recursos de CEL en la documentación oficial de Kubernetes.
El soporte para CEL apareció por primera vez en Kubernetes v1.23 para la validación inline de Custom Resource Definitions (CRD), actualmente en beta desde la v1.25. Esto abrió numerosas posibilidades de uso de CEL dentro de Kubernetes. Para profundizar en los orígenes de CEL en Kubernetes y su potencial, te recomendamos ver la charla de Cici Huang, " The Path to Self Contained CRDs", que inspiró nuestra exploración de las Validating Admission Policies.
Saca el máximo provecho de las VAPs y CEL para una validación eficiente
Una de las principales ventajas de usar CEL y VAPs es su simplicidad. Como la lógica de validación se evalúa directamente dentro del API server, no se suma overhead por workloads personalizados, lo que facilita escalar la validación a medida que crece el cluster. Además, CEL ofrece una forma ligera y eficiente de expresar reglas de validación, lo que mejora el rendimiento y reduce el riesgo de saturar el API server. Las restricciones de recursos integradas refuerzan aún más la estabilidad y la eficiencia del proceso.
Apoyándose en las Validating Admission Policies y CEL, los operadores del cluster cuentan con un proceso de validación más simple y escalable. Esto les permite aplicar estándares y reglas dentro del cluster de forma más eficiente y efectiva que con otros métodos. La facilidad de uso, la escalabilidad y el rendimiento de las VAPs y CEL las vuelven una alternativa atractiva para quienes buscan simplificar la validación dentro del cluster en Kubernetes.
Validating Admission Policies
Para usar esta función necesitas dos componentes principales:
ValidatingAdmissionPolicy: define la política de fallo, las coincidencias de solicitudes y las expresiones de validación CEL. Es decir, la política en sí.ValidatingAdmissionPolicyBinding: define el alcance de la política; la vincula a un conjunto de recursos coincidentes.
Aunque esta función está en Alpha desde la v1.26, algunas características solo aparecen a partir de la v1.27 ( audit annotation, validation actions), así que todos los manifiestos siguientes se probaron en un cluster de Kubernetes con la v1.27.1 (con las funciones alpha y el feature gate ValidatingAdmissionPolicy habilitados).
Todos los ejemplos de este artículo también están disponibles en un repositorio de GitHub creado para este propósito.
El primer ejemplo viene directo de la documentación. Crearemos y usaremos el namespace demo para correr todos los ejemplos:
$ echo 'apiVersion: v1
kind: Namespace
metadata:
labels:
environment: demo
name: demo' | k apply -f-
namespace/demo created
$ k config set-context --current --namespace demo
A continuación, crearemos una política que impide que los Deployments tengan más de cinco réplicas:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= 5"
El campo failurePolicy puede tomar los siguientes valores:
Fail: indica que un error al invocar laValidatingAdmissionPolicyhace que la admisión falle y la solicitud a la API se rechace.Ignore: indica que un error al invocar laValidatingAdmissionPolicyse ignora y se permite que la solicitud continúe.
El campo matchConstraints sirve para hacer coincidir las solicitudes entrantes y está configurado para que la política solo se aplique a solicitudes para Deployments en la API apps/v1, y únicamente a operaciones CREATE o UPDATE de un Deployment.
Por último, el campo validations contiene las expresiones CEL que se ejecutarán contra las solicitudes coincidentes. Todas las expresiones deben evaluarse como true para que la solicitud sea admitida.
Crearemos el siguiente binding para completar la configuración:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "demo-binding-test.example.com"
spec:
policyName: "demo-policy.example.com"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: demo
Estamos vinculando la política creada antes a cualquier namespace que tenga el label environment=demo; esto resulta útil para definir dónde queremos aplicar la validación.
Hay más opciones para hacer match de namespaces, como matchExpressions para coincidencias más granulares, y otras opciones de configuración: excludeResourceRules para excluir ciertos recursos u objectSelector para hacer match con ciertos objetos (no se recomienda, ya que los desarrolladores podrían omitir un label para evitar la auditoría). Cubriré las opciones más interesantes en ejemplos posteriores.
Una vez aplicados estos manifiestos al cluster, intentar crear un nuevo Deployment que viole la política arrojará un error, como era de esperarse:
$ k create deployment nginx — image=nginx — replicas=10
error: failed to create deployment: deployments.apps "nginx" is forbidden: ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5
También funciona al intentar hacer diff del recurso:
$ k create deployment nginx --image=nginx --replicas=10 --dry-run=client -oyaml | k diff -f -
The deployments "nginx" is invalid: : ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5
Múltiples expresiones de validación
A veces vas a querer aplicar varias validaciones a los recursos. Considera las siguientes expresiones de validación para Deployments:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
# Los Deployments no pueden tener más de 3 réplicas
- expression: "object.spec.replicas <= 3"
# Los contenedores del Deployment deben usar imágenes alojadas en el Artifact Registry de europe-west1 en el proyecto test-eyal
- expression: "object.spec.template.spec.containers.all(c, c.image.startsWith('europe-west1-docker.pkg.dev/test-eyal/'))"
# El Deployment no puede usar volúmenes emptyDir
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(v, !has(v.emptyDir))"
Un Deployment que coincida con esta política deberá tener todas las expresiones evaluadas como true para ser admitido en el cluster.
Algo que conviene tener en cuenta es que las validaciones se ejecutan secuencialmente. Si una expresión falla, devolverá ese fallo al cliente de inmediato; esto significa que, si tu recurso viola más de una expresión de validación, será un proceso iterativo: ser rechazado, corregir la violación y volver a intentar.
Personaliza el mensaje de validación
También se puede entregar un message personalizado que se devuelve al cliente cuando una validación falla. Incluso puedes hacer interpolación si lo necesitas usando un messageExpression. Una expresión de mensaje tiene acceso a object, oldObject, request y params.
Actualicemos nuestra última política con mensajes personalizados en lugar de comentarios:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= 3"
messageExpression: "'Deployments cannot have more than 3 replicas, this one has ' + string(object.spec.replicas)"
- expression: "object.spec.template.spec.containers.all(c, c.image.startsWith('europe-west1-docker.pkg.dev/test-eyal/'))"
message: "Deployment containers must be using images hosted in europe-west1 Artifact Registry in project test-eyal"
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(v, !has(v.emptyDir))"
messageExpression: "'Deployment cannot use emptyDir volumes, change the following volume: ' + object.spec.template.spec.volumes.filter(v, has(v.emptyDir)).map(v, v.name)[0]"
Es importante tener presente que cualquier valor interpolado en el campo messageExpression debe ser de tipo string; de lo contrario, el mensaje devolverá un error por la evaluación fallida.
Sin un mensaje personalizado, un deployment con un volumen emptyDir habría fallado con lo siguiente:
The deployments "nginx" is invalid: : ValidatingAdmissionPolicy ‘demo-policy.example.com’ with binding ‘demo-binding-test.example.com’ denied request: failed expression: !has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(v, !has(v.emptyDir))
Esto puede resultar difícil de entender desde la perspectiva del cliente. Con la expresión de mensaje personalizada obtendríamos lo siguiente:
The deployments "nginx" is invalid: : ValidatingAdmissionPolicy ‘demo-policy.example.com’ with binding ‘demo-binding-test.example.com’ denied request: Deployment cannot use emptyDir volumes, change the following volumes: test
Ten en cuenta que estoy usando un messageExpression en lugar de un message normal en esta validación para mostrar el poder de CEL. ¡Que puedas hacer algo no siempre significa que debas hacerlo!
Recursos adicionales
- Validating Admission Policies en Kubernetes: casos de uso avanzados
- validating-admission-policy-playground: repositorio de GitHub con ejemplos completos y funcionales de Validating Admission Policies
- Validating Admission Policy en la documentación oficial de Kubernetes
- Common Expression Language en Kubernetes
- Kubernetes Enhancement Proposal (KEP)-3488: CEL para Admission Control
- The Path to Self Contained CRDs, charla de Cici Huang
A medida que Kubernetes sigue evolucionando, el soporte para CEL traerá capacidades aún más interesantes en próximas versiones. Te invitamos a explorar las Validating Admission Policies y familiarizarte con esta poderosa función, ya que es muy probable que se convierta en una herramienta indispensable para los operadores del cluster.
Recuerda que el feature gate de Validating Admission Policy está actualmente en Alpha, lo que significa que se esperan cambios y mejoras a medida que avanza hacia General Availability. Mantente al día siguiendo las novedades en el changelog y la documentación de Kubernetes para no perderte los últimos avances.
Dado el carácter evolutivo de esta función, el contenido de este artículo puede requerir actualizaciones con el tiempo. Si te encuentras con un ejemplo que ya no funciona o una afirmación que ya no es precisa, escríbenos y revisaremos y actualizaremos la información.