
Curso Kubernetes 25 — Seguridad: RBAC, PSA, NetworkPolicy y secrets
Seguridad en Kubernetes no es una herramienta. Es un conjunto de patrones que impiden el próximo incidente — casi siempre causado por un cluster-admin olvidado o un namespace sin red aislada.
Este capítulo monta lo mínimo que todo clúster de producción debería tener.
Sello del curso: probado en Kubernetes 1.35 con PSA y Calico/Cilium.
Modelo de amenaza práctico
Preguntas que valen más que un checklist genérico:
- Si un pod es comprometido, ¿qué alcanza? (red, API, secrets)
- ¿Quién puede crear pod? Pod crea acceso al nodo.
- ¿Dónde están los secretos y quién los lee?
- ¿Qué corre con
privileged: truey por qué?
RBAC sin cluster-admin
Nunca dé cluster-admin a aplicación. Cree una Role en el namespace:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: portal-backend
namespace: portal
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: portal-backend
namespace: portal
subjects:
- kind: ServiceAccount
name: portal-backend
namespace: portal
roleRef:
kind: Role
name: portal-backend
apiGroup: rbac.authorization.k8s.io
En el Deployment:
spec:
template:
spec:
serviceAccountName: portal-backend
automountServiceAccountToken: false
automountServiceAccountToken: false cuando la app no habla con la API es la ganancia más barata del capítulo: sin token montado, credencial filtrada en el contenedor no sirve para nada.
Audite:
kubectl auth can-i --list --as=system:serviceaccount:portal:portal-backend -n portal
kubectl get clusterrolebindings -o json | jq -r '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name'
Personas: cada uno con su propio usuario (OIDC), no un kubeconfig compartido.
Pod Security Admission
PSA sustituyó a PSP. Tres niveles:
| Nivel | Qué exige |
|---|---|
| privileged | nada |
| baseline | bloquea lo peor (privileged, hostNetwork, hostPath) |
| restricted | corre como non-root, sin privilege escalation, seccomp, capabilities descartadas |
En el namespace:
apiVersion: v1
kind: Namespace
metadata:
name: portal
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
Comience con warn + audit, corrija las cargas, luego enforce.
Pod compatible con restricted:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem rompe app que escribe en /tmp. Monte emptyDir en /tmp — y descubra cuántas apps escriben donde no debían.
NetworkPolicy: default-deny
Sin política, todo pod habla con todo pod. Comience negando:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: portal
spec:
podSelector: {}
policyTypes: ["Ingress", "Egress"]
Luego libere lo necesario:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow
namespace: portal
spec:
podSelector:
matchLabels:
app: portal-backend
policyTypes: ["Ingress", "Egress"]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: portal-postgres
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Dos errores clásicos:
- Olvidar DNS en el egress: todo se vuelve timeout.
- Confiar en IP: pod cambia de IP; use selectors.
Prueba:
kubectl -n portal run tmp --rm -it --image=busybox -- sh
# dentro: nc -zv portal-postgres 5432 (deve falhar se não liberado)
Secretos
Lo que no hacer:
- Secret en
values.yamlo en Git. - Secret en variable de entorno impresa en log.
- Mismo secreto en todos los ambientes.
Opciones:
- External Secrets Operator + Vault/AWS SM.
- Sealed Secrets (cifrado en Git).
- SOPS + age.
Independientemente de la elección:
- rote credenciales;
- limite lectura de Secret por Role (Secret es namespaced, pero lectura es poder);
- habilite
encryption at resten etcd; - evite
kubectl get secret -o yamlen pipeline.
Y la alerta que ya apareció en este curso: minioadmin, changeme, admin/admin en manifest es incidente esperando fecha.
Runtime e imágenes
- Imágenes de registry confiable, por digest.
imagePullPolicy: IfNotPresentcon tag inmutable.- Admission policy (Kyverno) bloqueando
:latest,privilegedy hostPath. - Scanner de vulnerabilidades en el pipeline (capítulo 24).
- Runtime security (Falco) para detectar shell en contenedor.
Laboratorio
- Aplique
default-denyen el namespace y observe qué se rompe. - Libere DNS y las dependencias reales; valide con
nc/curl. - Active PSA en
warny liste los pods no conformes. - Corrija
securityContextde un Deployment y pase aenforce. - Cree ServiceAccount + Role mínima y pruebe con
kubectl auth can-i. - Intente
kubectl execcomo non-root y escribir en/(debe fallar).
Errores comunes
| Síntoma | Causa |
|---|---|
| Timeout tras NetworkPolicy | DNS no liberado en el egress |
| Pod rechazado por el PSA | falta runAsNonRoot/seccomp |
| App se rompe con readOnlyRoot | escritura en /tmp sin emptyDir |
| Cluster-admin en todas partes | RBAC copiado de tutorial |
| Secret legible por cualquier pod | falta RBAC + namespace aislado |
| Token de la API montado sin uso | automountServiceAccountToken por defecto |
| Policy no aplica | CNI sin soporte para NetworkPolicy |
Checklist
- [ ] ServiceAccount por aplicación, token desmontado cuando sea posible.
- [ ] Roles mínimas y auditoría de
cluster-admin. - [ ] PSA
restricted(obaselinejustificado) por namespace. - [ ]
securityContextcon non-root, seccomp y capabilities descartadas. - [ ] NetworkPolicy default-deny + liberaciones explícitas.
- [ ] DNS liberado en el egress.
- [ ] Secretos fuera de Git y rotación definida.
- [ ] etcd con encryption at rest.
- [ ] Admission policy para imágenes y privilegios.
- [ ] Runtime detection (Falco) evaluada.
Glosario
- RBAC: control de acceso por rol.
- ServiceAccount: identidad del pod.
- PSA: Pod Security Admission.
- NetworkPolicy: firewall entre pods.
- default-deny: niega todo y libera lo necesario.
- seccomp: filtro de syscalls.
- External Secrets: operador que sincroniza secretos externos.
Próximo
Clúster endurecido. Ahora la pregunta que derrumba ambiente: ¿cuánta carga aguanta? Capacidad, k6, rate limit y qué sucede cuando la base de datos se convierte en el cuello de botella.
Próximo: Curso Kubernetes 26 — Capacidad y carga: HPA, k6 y rate limit
Seguir leyendo
Lea también
ArmazenamentoManual LSI / Broadcom MegaRAID — MegaCli y StorCLI
Con esta guía, su equipo gana autonomía para diagnosticar rebuilds, resolver foreign configurations, gestionar BBU y CacheCade — con escenarios reales de producción documentados.
20 min de leitura
Nginx Cheatsheet
Guía de bolsillo con ejemplos listos para configurar Nginx en producción: desde instalación y virtual hosts hasta ajuste de rendimiento y resolución de problemas.
12 min de leitura
KubernetesCurso Kubernetes 27 — SLO/SLI, on-call y postmortem sin culpa
Qué prometer, cómo medir y qué hacer cuando el error se dispara: SLI, SLO, presupuesto de error, alertas que despiertan a alguien y postmortem sin caza de brujas.
5 min de leitura
Comentarios
Aún sin comentarios
Sea el primero en compartir su opinión sobre este artículo.
Deje un comentario