Kubernetes facilita el despliegue y la operación de aplicaciones a escala, pero su flexibilidad viene acompanada de una superficie de ataque considerable. Un cluster mal configurado puede exponer secrets, permitir escalación de privilegios y abrir puertas a movimientos laterales entre servicios. La seguridad en Kubernetes no es un componente que se agrega al final: debe integrarse en cada capa, desde la construcción de imagenes hasta el runtime de los contenedores. Esta guía cubre las prácticas esenciales para proteger clusters productivos de forma sistemática.

Pod Security Standards

Los Pod Security Standards (PSS) son el mecanismo nativo de Kubernetes para controlar la postura de seguridad de los pods. Reemplazan a las antiguas PodSecurityPolicies (removidas en v1.25) y definen tres niveles:

  • Privileged: Sin restricciones. Solo para workloads de sistema como CNI plugins o logging agents.
  • Baseline: Previene escalaciones de privilegio conocidas. Bloquea hostNetwork, hostPID, contenedores privilegiados y capabilities peligrosas.
  • Restricted: Maximo nivel de seguridad. Requiere que los contenedores corran como non-root, con un seccomp profile y sin capabilities adicionales.

Aplicar PSS con Pod Security Admission

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

Al etiquetar el namespace, Kubernetes rechaza automáticamente cualquier pod que no cumpla con el nivel restricted. Los modos audit y warn permiten detectar violaciones sin bloquear, útil durante la migración.

RBAC: control de acceso granular

Role-Based Access Control es la primera linea de defensa para controlar quien puede hacer que en el cluster. Errores comunes incluyen otorgar cluster-admin a cuentas de servicio o dejar bindings demasiado permisivos.

Principio de mínimo privilegio

Crea roles específicos por función:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: readonly-pods
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "services", "endpoints"]
  verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-readonly
  namespace: production
subjects:
- kind: Group
  name: developers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: readonly-pods
  apiGroup: rbac.authorization.k8s.io

Auditar permisos existentes

Revisa regularmente los permisos asignados. Usa kubectl auth can-i --list --as=system:serviceaccount:default:mi-sa para verificar que permisos tiene una cuenta de servicio específica. Elimina ClusterRoleBindings innecesarios y evita usar wildcards (*) en las reglas.

Network Policies

Sin network policies, cualquier pod puede comunicarse con cualquier otro pod en el cluster. Esto es inaceptable en producción.

Estrategia default-deny

Comienza bloqueando todo el tráfico y luego abre solo lo necesario:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-egress-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Egress
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
    ports:
    - protocol: TCP
      port: 5432
  - to:  # Permitir DNS
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

No olvides permitir tráfico DNS en las políticas de egress; de lo contrario, la resolución de nombres dentro del cluster dejara de funcionar.

Gestión de secrets

Los secrets de Kubernetes se almacenan en etcd codificados en base64, pero no cifrados por defecto. Esto no es seguro.

Cifrado en reposo

Habilita encryption at rest para etcd en clusters autogestionados:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: <clave-base64-de-32-bytes>
    - identity: {}

Soluciones externas recomendadas

Para producción, integra un gestor de secrets externo:

  • HashiCorp Vault: Inyecta secrets via sidecar (Vault Agent) o CSI driver. Soporta rotación automática y audit logging.
  • AWS Secrets Manager / Parameter Store: Usa el CSI Secrets Store Driver con el provider de AWS para montar secrets como volumenes.
  • Sealed Secrets (Bitnami): Permite cifrar secrets en el repositorio Git. Solo el controlador en el cluster puede descifrarlos.

La rotación periodica de credenciales es obligatoria. Automatizala para que no dependa de intervención manual.

Escaneo de imagenes y seguridad en el supply chain

Escaneo de vulnerabilidades

Integra escaneo de imagenes en el pipeline de CI/CD y como admission control en el cluster:

# Escanear imagen con Trivy antes del push al registry
trivy image --severity HIGH,CRITICAL --exit-code 1 mi-app:v2.1.0

# Escanear el Dockerfile en busca de misconfigurations
trivy config --severity HIGH,CRITICAL ./Dockerfile

Políticas de admision para imagenes

Usa un admission controller para bloquear imagenes no escaneadas o con vulnerabilidades criticas:

# Ejemplo con Kyverno
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-signature
spec:
  validationFailureAction: Enforce
  rules:
  - name: verify-signature
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - imageReferences:
      - "mi-registry.com/*"
      attestors:
      - entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              ...
              -----END PUBLIC KEY-----

Prácticas para imagenes seguras

  • Usa imagenes base minimas: distroless, alpine o scratch.
  • Nunca uses la tag latest. Fija versiones especificas con digest SHA.
  • Ejecuta contenedores como usuario non-root.
  • No incluyas herramientas de debug (curl, wget, shells) en imagenes de producción.

Seguridad en runtime con Falco

Falco es una herramienta open source de la CNCF que monitorea el comportamiento de los contenedores en tiempo real usando syscalls del kernel.

Que detecta Falco

  • Ejecución de shells dentro de contenedores.
  • Lectura de archivos sensibles (/etc/shadow, /etc/passwd).
  • Conexiones de red inesperadas.
  • Modificación de binarios del sistema.
  • Escalación de privilegios.

Regla personalizada de ejemplo

- rule: Shell in Container
  desc: Detecta ejecucion de shell interactivo en contenedores
  condition: >
    spawned_process and
    container and
    proc.name in (bash, sh, zsh) and
    not proc.pname in (cron, supervisord)
  output: >
    Shell ejecutado en contenedor
    (user=%user.name container=%container.name
    shell=%proc.name parent=%proc.pname
    cmdline=%proc.cmdline)
  priority: WARNING
  tags: [container, shell]

Integra Falco con Alertmanager o un SIEM para recibir notificaciones inmediatas ante comportamiento anomalo.

SecurityContext en pods

Configura el security context para endurecer cada pod:

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: mi-app:v2.1.0
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}

Puntos clave: ejecutar como non-root, filesystem de solo lectura (montando /tmp como emptyDir si la aplicación necesita escribir), eliminar todas las capabilities de Linux y habilitar seccomp.

CIS Kubernetes Benchmark

El Center for Internet Security pública un benchmark específico para Kubernetes que cubre la configuración segura de todos los componentes del cluster. Usa herramientas automatizadas para evaluar el cumplimiento:

# Ejecutar kube-bench para evaluar conformidad CIS
kube-bench run --targets master,node,policies

# Verificar un nodo especifico
kube-bench run --targets node

Los hallazgos se clasifican en PASS, FAIL y WARN. Prioriza los FAIL críticos: permisos de archivos de configuración, cifrado de etcd, configuración de audit logging y políticas de autenticación del API server.

Conclusion

La seguridad en Kubernetes es un proceso continuo que abarca multiples capas: desde los Pod Security Standards y RBAC hasta network policies, gestión de secrets, escaneo de imagenes y monitoreo de runtime. No existe una sola herramienta que resuelva todo; se necesita un enfoque de defensa en profundidad donde cada capa complementa a las demas. La recomendación práctica es implementar estas medidas de forma incremental, comenzando por RBAC y PSS, luego network policies y secrets management, y finalmente runtime security con Falco y audit logging.

Recursos adicionales