APIs, TLS y seguridad avanzada en Kubernetes
Todo en Kubernetes pasa por el API server: kubectl, los kubelets, los controladores y cualquier aplicación que hable con el cluster. Eso lo convierte en la pieza más crítica a proteger y en un tema recurrente de los exámenes CKA y CKS. En este capítulo veremos cómo se estructura la API, cómo se protege con TLS y qué configuraciones debes conocer (y auditar).
Anatomía de la API de Kubernetes
La API es una API REST: cada recurso tiene su ruta. Cuando ejecutas kubectl get pods, en realidad se hace una petición GET https://<api-server>:6443/api/v1/namespaces/default/pods.
Los recursos se organizan en grupos de API (los apiGroups que ya vimos en RBAC):
/api/v1: el grupo core (pods, services, configmaps, secrets...)./apis/<grupo>/<versión>: el resto, por ejemplo/apis/apps/v1(deployments) o/apis/batch/v1(jobs).
Puedes explorar la API directamente:
# Listar todos los recursos, sus grupos y abreviaturas
kubectl api-resources
# Listar las versiones de API disponibles
kubectl api-versions
# Ver la estructura de cualquier recurso (tu mejor amigo en el examen)
kubectl explain pod.spec.containers
Versionado y deprecaciones
Las versiones de API siguen una progresión: v1alpha1 → v1beta1 → v1 (estable). Las versiones alpha están desactivadas por defecto y las beta pueden cambiar; cuando un recurso se gradúa, las versiones antiguas se marcan como deprecated y acaban eliminándose.
Esto importa porque las actualizaciones de cluster rompen manifiestos antiguos: si tus YAML usan una API eliminada (como extensions/v1beta1 para deployments, o las PodSecurityPolicies que vimos en seguridad), dejarán de aplicar. Antes de actualizar, revisa la guía de APIs deprecadas y comprueba qué usa tu cluster:
# ¿Qué versión estoy usando para cada recurso?
kubectl get deployments.v1.apps -o yaml | head
# Herramientas como kubent o pluto detectan APIs deprecadas en uso
Acceder a la API directamente
Para entender la seguridad de la API, nada como llamarla sin kubectl.
La forma fácil es kubectl proxy, que abre un túnel local ya autenticado con tu kubeconfig:
kubectl proxy --port=8001 &
curl http://localhost:8001/api/v1/namespaces/default/pods
La forma manual, con certificados, te enseña qué hay realmente en tu kubeconfig:
# Extraer las credenciales del kubeconfig
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}' | base64 -d > client.crt
kubectl config view --raw -o jsonpath='{.users[0].user.client-key-data}' | base64 -d > client.key
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
curl https://<ip-api-server>:6443/api/v1/pods \
--cert client.crt --key client.key --cacert ca.crt
Desde dentro de un pod, las aplicaciones usan el token de su service account:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl https://kubernetes.default.svc/api/v1/namespaces/default/pods \
--header "Authorization: Bearer $TOKEN" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
TLS en el cluster
Toda la comunicación interna de Kubernetes va cifrada con TLS: kubectl ↔ API server, API server ↔ kubelets, API server ↔ etcd... Cada componente tiene su par de certificado y clave, firmados por la CA del cluster que vive en /etc/kubernetes/pki/.
ls /etc/kubernetes/pki/
# apiserver.crt, apiserver.key -> el certificado del API server
# apiserver-kubelet-client.crt/key -> para hablar con los kubelets
# ca.crt, ca.key -> la CA del cluster
# etcd/ -> PKI propia de etcd
# front-proxy-* -> para el agregador de APIs
# sa.key, sa.pub -> firma de tokens de service accounts
En el próximo capítulo gestionaremos estos certificados en detalle (caducidades, renovaciones, kubeadm certs). De momento, quédate con la foto: una CA raíz firma la identidad de cada componente, y cualquier petición se valida contra ella.
Flags de seguridad del API server
La configuración del API server vive en /etc/kubernetes/manifests/kube-apiserver.yaml (es un static pod, como vimos en troubleshooting). Estos son los flags que debes conocer y auditar:
spec:
containers:
- command:
- kube-apiserver
- --anonymous-auth=false # Rechazar peticiones anónimas (cuidado: los health checks las usan)
- --authorization-mode=Node,RBAC # Nunca AlwaysAllow
- --enable-admission-plugins=NodeRestriction
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --kubelet-client-certificate=... # Autenticación mutua con los kubelets
- --audit-log-path=/var/log/kubernetes/audit.log # Auditoría (si está configurada)
Puntos calientes que pregunta el examen (y que explotan los atacantes):
--anonymous-auth: si está entrue(valor por defecto) las peticiones sin credenciales se tratan como el usuariosystem:anonymous. Combinado con un RBAC permisivo, es una puerta abierta.--authorization-mode=AlwaysAllow: desactiva la autorización por completo. Nunca en producción.--insecure-port: el antiguo puerto 8080 sin TLS ni autenticación. Eliminado en versiones modernas, pero pregunta clásica de hardening.- NodeRestriction: admission plugin que limita lo que cada kubelet puede modificar (solo su propio nodo y sus pods). Esencial para que un nodo comprometido no afecte al resto.
Tras cualquier cambio en este fichero, recuerda que el kubelet recrea el pod automáticamente; vigílalo con crictl ps si el API deja de responder.
Puertos del cluster
Conocer los puertos es necesario tanto para configurar firewalls como para el examen:
| Puerto | Componente | Uso |
|---|---|---|
| 6443 | kube-apiserver | API del cluster (TLS) |
| 2379-2380 | etcd | Cliente y comunicación entre pares |
| 10250 | kubelet | API del kubelet (¡protégela! permite ejecutar comandos en pods) |
| 10257 | kube-controller-manager | Métricas/health (localhost) |
| 10259 | kube-scheduler | Métricas/health (localhost) |
| 30000-32767 | nodos | Rango NodePort |
El puerto 10250 del kubelet merece mención especial: es un objetivo clásico de ataques, porque si queda accesible sin autenticación permite listar pods y ejecutar comandos en ellos. Verifica que el kubelet tenga authentication.anonymous.enabled: false y authorization.mode: Webhook en /var/lib/kubelet/config.yaml.
Resumen
- La API es REST y se organiza en grupos versionados;
kubectl explainykubectl api-resourcesson tus herramientas de exploración. - Las APIs se deprecan y eliminan: revisa tus manifiestos antes de cada actualización de cluster.
- Todo el tráfico interno va con TLS firmado por la CA de
/etc/kubernetes/pki/. - Audita los flags del API server (
anonymous-auth,authorization-mode, admission plugins) y protege el puerto 10250 del kubelet.
- Lista de vídeos en Youtube: Curso Kubernetes