Motor de Políticas Kyverno para Kubernetes
Kyverno es un motor de políticas nativo de Kubernetes que permite definir, validar, mutar y generar recursos del clúster usando YAML estándar sin necesidad de aprender un lenguaje de programación adicional como Rego. Su integración nativa como controlador de admisión lo convierte en la solución más accesible para equipos que necesitan gobernanza de Kubernetes sin la curva de aprendizaje de OPA Gatekeeper. Esta guía cubre la instalación de Kyverno, tipos de políticas y casos de uso comunes.
Requisitos Previos
- Clúster Kubernetes 1.26+
- kubectl configurado con acceso de administrador
- Helm 3 instalado
- Mínimo 2 GB de RAM disponible para Kyverno
Instalación de Kyverno
# Instalar Kyverno con Helm (recomendado)
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
# Instalación en modo de alta disponibilidad (3 réplicas)
helm install kyverno kyverno/kyverno \
--namespace kyverno \
--create-namespace \
--set replicaCount=3 \
--set admissionController.replicas=3 \
--set backgroundController.replicas=2 \
--set cleanupController.replicas=1 \
--set reportsController.replicas=1
# Instalación simple para desarrollo/pruebas (1 réplica)
helm install kyverno kyverno/kyverno \
--namespace kyverno \
--create-namespace \
--set replicaCount=1
# Verificar que los pods están en ejecución
kubectl get pods -n kyverno
kubectl rollout status deployment kyverno-admission-controller -n kyverno
# Instalar también las políticas predefinidas de la comunidad
helm install kyverno-policies kyverno/kyverno-policies \
--namespace kyverno \
--set podSecurityStandard=restricted # baseline, restricted o privileged
# Verificar las políticas instaladas
kubectl get clusterpolicies
Políticas de Validación
Las políticas de validación rechazan recursos que no cumplen las reglas:
# Política: requerir límites de recursos en todos los contenedores
cat > /tmp/require-limits.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
annotations:
policies.kyverno.io/title: Límites de recursos obligatorios
policies.kyverno.io/category: Best Practices
policies.kyverno.io/description: >-
Requiere que todos los contenedores definan limits de CPU y memoria
para prevenir el consumo descontrolado de recursos del nodo.
spec:
# Deny: bloquear recursos que no cumplen / Audit: solo reportar
validationFailureAction: Enforce
# Evaluar también los recursos existentes (no solo nuevos)
background: true
rules:
- name: validar-limites-cpu-memoria
match:
any:
- resources:
kinds: [Pod]
namespaceSelector:
matchLabels:
enforce-limits: "true"
# Excluir namespaces del sistema
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
- monitoring
validate:
message: >-
El contenedor '{{ request.object.spec.containers[0].name }}'
debe definir resources.limits.cpu y resources.limits.memory
# El patrón define la estructura que DEBE tener el recurso
foreach:
- list: "request.object.spec.containers"
deny:
conditions:
any:
- key: "{{ element.resources.limits | length(@) }}"
operator: Equals
value: 0
- key: "{{ element.resources.limits.memory }}"
operator: Equals
value: ""
- key: "{{ element.resources.limits.cpu }}"
operator: Equals
value: ""
EOF
kubectl apply -f /tmp/require-limits.yaml
# Política: prohibir el uso de la imagen 'latest'
cat > /tmp/no-latest-tag.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: no-latest-image-tag
annotations:
policies.kyverno.io/title: Prohibir tag 'latest'
policies.kyverno.io/description: >-
Prohibe el uso del tag 'latest' en imágenes de contenedor para
garantizar versiones reproducibles y trazabilidad en el historial.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: no-imagen-latest
match:
any:
- resources:
kinds: [Pod]
exclude:
any:
- resources:
namespaces: [kube-system, kyverno]
validate:
message: "El tag ':latest' está prohibido. Usar un tag de versión específico como 'v1.2.3'"
foreach:
- list: "request.object.spec.containers"
pattern:
image: "!*:latest"
- list: "request.object.spec.initContainers"
pattern:
image: "!*:latest"
EOF
kubectl apply -f /tmp/no-latest-tag.yaml
Políticas de Mutación
Las políticas de mutación modifican los recursos automáticamente antes de aceptarlos:
# Política: añadir automáticamente etiquetas de equipo a todos los pods
cat > /tmp/add-default-labels.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-labels
annotations:
policies.kyverno.io/title: Añadir etiquetas por defecto
policies.kyverno.io/description: >-
Añade automáticamente etiquetas de gestión a todos los pods
para facilitar el inventario, monitorización y facturación.
spec:
rules:
- name: add-labels
match:
any:
- resources:
kinds: [Pod]
exclude:
any:
- resources:
namespaces: [kube-system, kyverno]
mutate:
patchStrategicMerge:
metadata:
labels:
# Mantener etiquetas existentes y añadir las que falten
+(managed-by): kyverno
+(environment): "{{ request.object.metadata.namespace }}"
EOF
kubectl apply -f /tmp/add-default-labels.yaml
# Política: establecer securityContext por defecto en todos los pods
cat > /tmp/default-security-context.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-security-context
annotations:
policies.kyverno.io/title: Security context por defecto
spec:
rules:
- name: add-security-context
match:
any:
- resources:
kinds: [Pod]
exclude:
any:
- resources:
namespaces: [kube-system, kyverno]
mutate:
patchStrategicMerge:
spec:
securityContext:
# Solo añadir si no está definido (operador +)
+(runAsNonRoot): true
+(runAsUser): 1000
+(fsGroup): 1000
+(seccompProfile):
type: RuntimeDefault
containers:
- (name): "*"
securityContext:
+(allowPrivilegeEscalation): false
+(readOnlyRootFilesystem): true
+(capabilities):
drop: ["ALL"]
EOF
kubectl apply -f /tmp/default-security-context.yaml
Políticas de Generación
Las políticas de generación crean recursos automáticamente cuando se crean otros recursos:
# Política: crear automáticamente un NetworkPolicy por defecto
# en cada namespace nuevo que deniegue todo el tráfico
cat > /tmp/generate-networkpolicy.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-default-network-policy
annotations:
policies.kyverno.io/title: Generar NetworkPolicy por defecto
policies.kyverno.io/description: >-
Crea automáticamente una NetworkPolicy de denegación por defecto
en cada nuevo namespace de aplicación, requiriendo políticas explícitas
para permitir el tráfico.
spec:
rules:
- name: crear-networkpolicy-default-deny
match:
any:
- resources:
kinds: [Namespace]
exclude:
any:
- resources:
selector:
matchLabels:
skip-network-policy: "true"
generate:
# Tipo de recurso a generar
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny-all
# Namespace donde crear el recurso (el recién creado)
namespace: "{{ request.object.metadata.name }}"
# Si el namespace se elimina, eliminar también la política
synchronize: true
data:
spec:
# Seleccionar todos los pods del namespace
podSelector: {}
policyTypes:
- Ingress
- Egress
# Sin reglas = denegar todo el tráfico entrante y saliente
EOF
kubectl apply -f /tmp/generate-networkpolicy.yaml
# Crear un namespace de prueba y verificar que se generó la NetworkPolicy
kubectl create namespace prueba-kyverno
kubectl get networkpolicies -n prueba-kyverno
# Debe mostrar: default-deny-all
Verificación de Imágenes
# Política: verificar que las imágenes están firmadas con Cosign
cat > /tmp/verify-signatures.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
annotations:
policies.kyverno.io/title: Imágenes firmadas obligatorias
policies.kyverno.io/description: >-
Verifica que las imágenes de los registros internos están firmadas
con la clave Cosign de la empresa antes de permitir su ejecución.
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verificar-firma-cosign
match:
any:
- resources:
kinds: [Pod]
namespaces: ["produccion", "staging"]
verifyImages:
- imageReferences:
- "registry.empresa.com/*"
required: true
attestors:
- count: 1
entries:
- keys:
publicKeys: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXXXXXXXXXXXXXXXXXXXXXXXXXXXX
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==
-----END PUBLIC KEY-----
EOF
kubectl apply -f /tmp/verify-signatures.yaml
Auditoría e Informes
# Ver el estado de cumplimiento de todas las políticas
kubectl get policyreport -A
# Ver el informe detallado de un namespace
kubectl get policyreport -n produccion -o yaml | \
python3 -c "
import sys, yaml
for doc in yaml.safe_load_all(sys.stdin.read()):
if doc:
results = doc.get('results', [])
fails = [r for r in results if r['result'] == 'fail']
for f in fails[:10]:
print(f'FAIL: {f[\"policy\"]}/{f[\"rule\"]} - {f[\"resources\"][0][\"kind\"]}/{f[\"resources\"][0][\"name\"]}: {f[\"message\"]}')
"
# Ver el informe a nivel de clúster
kubectl get clusterpolicyreport
# Número de violaciones por política
kubectl get policyreport -A -o json | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
counts = {}
for item in data['items']:
for result in item.get('results', []):
if result['result'] == 'fail':
key = result['policy']
counts[key] = counts.get(key, 0) + 1
for policy, count in sorted(counts.items(), key=lambda x: -x[1]):
print(f'{count:4d} violaciones: {policy}')
"
Mejores Prácticas de Seguridad
# Aplicar las políticas del Pod Security Standard 'restricted'
# (equivalente al antiguo PodSecurityPolicy más estricto)
cat > /tmp/pss-restricted.yaml << 'EOF'
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: pod-security-standards-restricted
spec:
validationFailureAction: Enforce
background: true
rules:
- name: no-privileged
match:
any: [{resources: {kinds: [Pod]}}]
exclude:
any: [{resources: {namespaces: [kube-system, kyverno]}}]
validate:
message: "Los contenedores privilegiados no están permitidos"
pattern:
spec:
=(initContainers):
- =(securityContext):
=(privileged): "false"
containers:
- =(securityContext):
=(privileged): "false"
- name: no-host-namespaces
match:
any: [{resources: {kinds: [Pod]}}]
exclude:
any: [{resources: {namespaces: [kube-system, kyverno]}}]
validate:
message: "El uso de host namespaces no está permitido"
pattern:
spec:
=(hostPID): "false"
=(hostIPC): "false"
=(hostNetwork): "false"
EOF
kubectl apply -f /tmp/pss-restricted.yaml
Solución de Problemas
# Ver los logs de Kyverno
kubectl logs -n kyverno -l app.kubernetes.io/component=admission-controller -f
# Estado de las políticas
kubectl get clusterpolicies
kubectl describe clusterpolicy require-resource-limits
# Probar una política sin aplicarla (modo seco)
# Cambiar validationFailureAction a "Audit" temporalmente
kubectl patch clusterpolicy require-resource-limits \
--type merge \
-p '{"spec":{"validationFailureAction":"Audit"}}'
# Ver por qué un recurso fue rechazado
kubectl get events --field-selector reason=PolicyViolation
# Ver todas las decisiones de admisión de Kyverno
kubectl logs -n kyverno -l app.kubernetes.io/component=admission-controller \
--since=10m | grep "admission.review"
# Kyverno no está interceptando recursos
# Verificar que el webhook está configurado
kubectl get validatingwebhookconfigurations | grep kyverno
kubectl get mutatingwebhookconfigurations | grep kyverno
# Regenerar los webhooks si están corruptos
kubectl delete validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg
# Kyverno los regenera automáticamente
Conclusión
Kyverno simplifica radicalmente la gobernanza de Kubernetes al usar YAML nativo para definir políticas de validación, mutación y generación, eliminando la necesidad de aprender Rego u otros lenguajes de dominio específico. Su capacidad para generar recursos automáticamente, combinar con verificación de imágenes Cosign y producir informes de auditoría detallados lo convierte en una solución integral de policy-as-code que los equipos de DevOps pueden adoptar progresivamente sin interrumpir los flujos de trabajo existentes.


