--- title: "Cómo instalar OPA Gatekeeper en Kubernetes con Helm" description: "Instala OPA Gatekeeper con Helm, crea ConstraintTemplates en Rego, prueba políticas en modo dryrun con la auditoría y bloquea recursos que no las cumplen." category: "Seguridad de Contenedores" language: es source: https://cubepath.com/es/docs/seguridad-de-contenedores/instalacion-de-open-policy-agent-gatekeeper updated: 2026-09-25 --- # Cómo instalar OPA Gatekeeper en Kubernetes con Helm OPA Gatekeeper es un controlador de admisión para Kubernetes basado en Open Policy Agent. Intercepta cada petición a la API del clúster y rechaza los recursos que incumplen las políticas que definas en Rego, además de auditar periódicamente lo que ya está desplegado. En este tutorial instalarás Gatekeeper con Helm, crearás dos políticas (etiquetas obligatorias en namespaces y prohibición de contenedores privilegiados), las probarás primero en modo `dryrun` y después las pondrás a bloquear. ## Requisitos previos Para seguir esta guía necesitas: - Un clúster Kubernetes en una versión mantenida (1.30 o superior), por ejemplo un clúster Kubernetes gestionado de CubePath o uno propio con kubeadm o k3s. - `kubectl` configurado con permisos de `cluster-admin` sobre ese clúster. - Helm 3 instalado en tu equipo. - Conocimientos básicos de Pods, Deployments y namespaces. Comprueba el acceso antes de empezar: ```bash kubectl version helm version --short ``` ## Paso 1: Instalar Gatekeeper con Helm Añade el repositorio oficial del chart y actualiza el índice: ```bash helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts helm repo update ``` Instala Gatekeeper en su propio namespace. Los valores por defecto (3 réplicas del webhook y una auditoría cada 60 segundos) sirven para la mayoría de clústeres: ```bash helm install gatekeeper gatekeeper/gatekeeper \ --namespace gatekeeper-system \ --create-namespace ``` Espera a que los Deployments estén listos: ```bash kubectl -n gatekeeper-system rollout status deployment/gatekeeper-controller-manager kubectl -n gatekeeper-system rollout status deployment/gatekeeper-audit ``` Comprueba los pods: ```bash kubectl get pods -n gatekeeper-system ``` ```output NAME READY STATUS RESTARTS AGE gatekeeper-audit-6c9d7b8f5d-8kq2x 1/1 Running 0 60s gatekeeper-controller-manager-7f8b6d9c4b-2xk7p 1/1 Running 0 60s gatekeeper-controller-manager-7f8b6d9c4b-9hz4m 1/1 Running 0 60s gatekeeper-controller-manager-7f8b6d9c4b-rt5vn 1/1 Running 0 60s ``` El `controller-manager` atiende el webhook de admisión y `gatekeeper-audit` revisa periódicamente los recursos existentes. ## Paso 2: Entender ConstraintTemplate y Constraint Gatekeeper separa la lógica de la política de su aplicación: | Objeto | Qué contiene | Ejemplo | |---|---|---| | `ConstraintTemplate` | El código Rego y el esquema de parámetros. Crea un nuevo tipo de recurso (CRD). | `K8sRequiredLabels` | | Constraint | Una instancia de ese tipo: a qué recursos se aplica, con qué parámetros y qué acción tomar. | "Todo namespace debe llevar la etiqueta `owner`" | La acción de cada Constraint se define en `enforcementAction`: - `deny`: rechaza la petición (valor por defecto). - `dryrun`: no bloquea nada; las infracciones solo aparecen en la auditoría. - `warn`: acepta la petición pero devuelve un aviso a `kubectl`. ## Paso 3: Crear la plantilla de etiquetas obligatorias Crea un directorio de trabajo para los manifiestos: ```bash mkdir -p ~/gatekeeper && cd ~/gatekeeper ``` Crea la plantilla: ```bash nano k8srequiredlabels-template.yaml ``` ```yaml apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8srequiredlabels spec: crd: spec: names: kind: K8sRequiredLabels validation: openAPIV3Schema: type: object properties: labels: type: array items: type: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8srequiredlabels violation[{"msg": msg, "details": {"missing_labels": missing}}] { provided := {label | input.review.object.metadata.labels[label]} required := {label | label := input.parameters.labels[_]} missing := required - provided count(missing) > 0 msg := sprintf("faltan las etiquetas obligatorias: %v", [missing]) } ``` La regla `violation` compara el conjunto de etiquetas presentes en el objeto (`input.review.object`) con las que exige la Constraint (`input.parameters.labels`) y genera un mensaje si falta alguna. Aplica la plantilla: ```bash kubectl apply -f k8srequiredlabels-template.yaml ``` Comprueba que Gatekeeper ha compilado el Rego y ha creado el nuevo tipo: ```bash kubectl get constrainttemplate k8srequiredlabels -o jsonpath='{.status.created}{"\n"}' kubectl get crd k8srequiredlabels.constraints.gatekeeper.sh ``` ```output true NAME CREATED AT k8srequiredlabels.constraints.gatekeeper.sh 2026-09-25T10:12:31Z ``` Si `status.created` no es `true`, revisa `kubectl describe constrainttemplate k8srequiredlabels`: los errores de sintaxis de Rego aparecen en la sección `Status`. ## Paso 4: Aplicar la política en modo dryrun Antes de bloquear nada, conviene ver cuántos recursos existentes incumplirían la política. Crea una Constraint que exija la etiqueta `owner` en todos los namespaces, excepto los del sistema: ```bash nano ns-must-have-owner.yaml ``` ```yaml apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: ns-must-have-owner spec: enforcementAction: dryrun match: kinds: - apiGroups: [""] kinds: ["Namespace"] excludedNamespaces: - kube-system - kube-public - kube-node-lease - default - gatekeeper-system parameters: labels: ["owner"] ``` ```bash kubectl apply -f ns-must-have-owner.yaml ``` Espera un minuto a que se ejecute la auditoría y consulta el resultado: ```bash kubectl get k8srequiredlabels ``` ```output NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS ns-must-have-owner dryrun 3 ``` Para ver qué recursos concretos incumplen la política: ```bash kubectl get k8srequiredlabels ns-must-have-owner \ -o jsonpath='{range .status.violations[*]}{.kind}/{.name}: {.message}{"\n"}{end}' ``` ```output Namespace/monitoring: faltan las etiquetas obligatorias: {"owner"} Namespace/web: faltan las etiquetas obligatorias: {"owner"} Namespace/staging: faltan las etiquetas obligatorias: {"owner"} ``` Corrige los recursos existentes antes de activar el bloqueo, por ejemplo: ```bash kubectl label namespace web owner=equipo-web ``` > Nota: la auditoría guarda como máximo 20 infracciones por Constraint en `status.violations` (valor `constraintViolationsLimit` del chart). `TOTAL-VIOLATIONS` sí muestra el número real. ## Paso 5: Pasar la política a modo deny Cuando la auditoría esté limpia o las excepciones estén justificadas, cambia la acción a `deny`: ```bash kubectl patch k8srequiredlabels ns-must-have-owner \ --type merge -p '{"spec":{"enforcementAction":"deny"}}' ``` Prueba a crear un namespace sin la etiqueta: ```bash kubectl create namespace prueba ``` ```output Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-owner] faltan las etiquetas obligatorias: {"owner"} ``` Ahora crea el mismo namespace con la etiqueta en un manifiesto: ```bash nano ns-prueba.yaml ``` ```yaml apiVersion: v1 kind: Namespace metadata: name: prueba labels: owner: equipo-devops ``` ```bash kubectl apply -f ns-prueba.yaml ``` ```output namespace/prueba created ``` Guarda los manifiestos en Git y cambia `enforcementAction` en el YAML para que el repositorio refleje el estado real del clúster. ## Paso 6: Prohibir contenedores privilegiados Un contenedor con `privileged: true` tiene acceso prácticamente total al nodo. Esta segunda plantilla revisa contenedores normales e `initContainers`: ```bash nano k8spspprivileged.yaml ``` ```yaml apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8spspprivileged spec: crd: spec: names: kind: K8sPSPPrivileged targets: - target: admission.k8s.gatekeeper.sh rego: | package k8spspprivileged violation[{"msg": msg}] { c := input_containers[_] c.securityContext.privileged msg := sprintf("contenedor privilegiado no permitido: %v", [c.name]) } input_containers[c] { c := input.review.object.spec.containers[_] } input_containers[c] { c := input.review.object.spec.initContainers[_] } ``` Crea la Constraint en otro archivo, porque la plantilla debe existir antes de que Kubernetes acepte el nuevo tipo: ```bash nano no-privileged.yaml ``` ```yaml apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sPSPPrivileged metadata: name: no-privileged-containers spec: enforcementAction: deny match: kinds: - apiGroups: [""] kinds: ["Pod"] excludedNamespaces: - kube-system - gatekeeper-system ``` ```bash kubectl apply -f k8spspprivileged.yaml kubectl get constrainttemplate k8spspprivileged -o jsonpath='{.status.created}{"\n"}' kubectl apply -f no-privileged.yaml ``` Prueba la política con un pod privilegiado en el namespace que creaste antes: ```bash nano pod-privilegiado.yaml ``` ```yaml apiVersion: v1 kind: Pod metadata: name: privilegiado namespace: prueba spec: containers: - name: app image: nginx:1.27 securityContext: privileged: true ``` ```bash kubectl apply -f pod-privilegiado.yaml ``` ```output Error from server (Forbidden): error when creating "pod-privilegiado.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [no-privileged-containers] contenedor privilegiado no permitido: app ``` > Importante: la Constraint se aplica a objetos `Pod`. Si creas un Deployment con un contenedor privilegiado, el Deployment se acepta pero su ReplicaSet no podrá crear los pods. Lo verás con `kubectl describe replicaset -n `. Para rechazarlo antes, añade `apps/Deployment` a `match.kinds` y adapta el Rego para leer `spec.template.spec`. Elimina los recursos de prueba: ```bash kubectl delete namespace prueba ``` ## Paso 7: Reutilizar la biblioteca oficial de políticas No hace falta escribir cada política desde cero. El proyecto mantiene [Gatekeeper Library](https://open-policy-agent.github.io/gatekeeper-library/website/), con plantillas probadas para los casos más comunes: registros de imágenes permitidos (`K8sAllowedRepos`), límites de recursos obligatorios (`K8sContainerLimits`), prohibición de la etiqueta `latest`, equivalentes de Pod Security Standards, etc. Cada política incluye su `template.yaml`, ejemplos de Constraint y casos de prueba. El flujo recomendado es el mismo que has seguido: aplica la plantilla, crea la Constraint en `dryrun`, revisa la auditoría y solo entonces cambia a `deny`. ## Solución de problemas **La Constraint no aparece o da `no matches for kind`.** La plantilla aún no se ha procesado o su Rego tiene un error. Revisa: ```bash kubectl describe constrainttemplate k8srequiredlabels ``` **Las peticiones no se bloquean.** Comprueba que el webhook está registrado y que el namespace no está excluido: ```bash kubectl get validatingwebhookconfigurations gatekeeper-validating-webhook-configuration kubectl get namespace --show-labels ``` Los namespaces con la etiqueta `admission.gatekeeper.sh/ignore` quedan fuera del webhook. **Errores en los logs.** ```bash kubectl logs -n gatekeeper-system deployment/gatekeeper-controller-manager kubectl logs -n gatekeeper-system deployment/gatekeeper-audit ``` **El clúster se queda sin poder crear recursos si Gatekeeper falla.** Por defecto el webhook de validación usa `failurePolicy: Ignore`, así que una caída de Gatekeeper no bloquea el clúster, pero durante ese tiempo tampoco se aplican las políticas. La auditoría detectará después lo que haya entrado. ## Conclusión Has instalado OPA Gatekeeper con Helm, has escrito dos ConstraintTemplates en Rego y has seguido el ciclo seguro para activarlas: `dryrun`, revisión de la auditoría y paso a `deny`. Como siguientes pasos, guarda plantillas y Constraints en Git y despliégalas con Argo CD o Flux, incorpora políticas de Gatekeeper Library como `K8sAllowedRepos`, y compara este enfoque con Kyverno si tu equipo prefiere escribir políticas en YAML en lugar de Rego.