¿Cómo realizar pentesting de Kubernetes y asegurarlo?

Kubernetes es una gran plataforma para la administración de contenedores que ha mostrado un gran avance últimamente, tanto en términos de funcionalidad como en lo referente a seguridad y resistencia. Especialistas afirman que la arquitectura de Kubernetes hace que sea fácil sobrevivir a diferentes tipos de interrupciones y mantenerse activa a pesar de todo, por lo que es una gran opción para el pentesting.

En esta ocasión, los expertos en pentesting del Instituto Internacional de Seguridad Cibernética (IICS) le mostrarán cómo realizar múltiples tareas de hacking en Kubernetes, incluyendo irrupción del clúster, eliminación de certificados y conexión de nodos, todo sin tiempo de inactividad para los servicios que se están ejecutando.

Antes de continuar, como de costumbre le recordamos que este artículo fue elaborado con fines exclusivamente informativos y no debe ser tomado con un llamado a la acción. IICS no es responsable del mal uso de la información aquí contenida.

Para comenzar, recordemos que el panel de control principal de Kubernetes consta de unos pocos componentes:

  • etcd: Utilizado como base de datos
  • kube-apiserver: API y corazón del clúster
  • kube-controller-manager: Para el despliegue de operaciones en los recursos de Kubernetes
  • kube-scheduler: Planificador principal
  • kubelets: Para lanzar contenedores directamente en hosts

Cada uno de estos componentes está protegido por un conjunto de certificados TLS, cliente y servidor, cuyo objetivo es autenticar y autorizar componentes entre ellos, mencionan los expertos en pentesting. Estos recursos no se almacenan en ninguna parte de la base de datos de Kubernetes, excepto en ciertos casos, pero se presentan como archivos normales:

# tree /etc/kubernetes/pki/ /etc/kubernetes/pki/ ├── apiserver.crt ├── apiserver-etcd-client.crt ├── apiserver-etcd-client.key ├── apiserver.key ├── apiserver-kubelet-client.crt ├── apiserver-kubelet-client.key ├── ca.crt ├── ca.key ├── CTNCA.pem ├── etcd │ ├── ca.crt │ ├── ca.key │ ├── healthcheck-client.crt │ ├── healthcheck-client.key │ ├── peer.crt │ ├── peer.key │ ├── server.crt │ └── server.key ├── front-proxy-ca.crt ├── front-proxy-ca.key ├── front-proxy-client.crt ├── front-proxy-client.key ├── sa.key └── sa.pub

Los componentes en sí mismos se describen y ejecutan en los maestros como pods estáticos desde el directorio /etc/kubernetes/manifests /.

Lo más importante es saber cómo hacer un clúster funcional de todo esto; imaginemos que los componentes de Kubernetes mencionados anteriormente de alguna forma interactúan entre sí. El diagrama básico se parece a esto:

Para comunicarse, necesitan certificados TLS, que, en principio, pueden llevarse a un nivel de abstracción separado y confiar completamente en su herramienta de distribución, ya sea kubeadm, kubespray u otra cosa, mencionan los expertos en pentesting. En este ejemplo, veremos kubeadm porque es la herramienta de implementación de Kubernetes más común y, a menudo, se usa como parte de otras soluciones.

Digamos que ya tenemos un clúster implementado. Comencemos con la parte divertida:

rm -rf /etc/kubernetes/ 

En maestros, este directorio contiene:

  • Un conjunto de certificados y CA para etcd (c/etc/kubernetes/pki/etcd)
  • Un conjunto de certificados y CA para Kubernetes (c/etc/kubernetes/pki)
  • Kubeconfig para cluster-admin, kube-controller-manager, kube-Scheduler y kubelet (cada uno también tiene un certificado CA codificado en base64 para nuestro clúster /etc/kubernetes/*.conf)
  • Un conjunto de manifiestos estáticos para etcd, kube-apiserver, kube-Scheduler y kube-controller-manager (c/etc/kubernetes/manifests)

Supongamos que perdimos todo a la vez.                      

Arreglar el plano de control

Para evitar confusiones, los investigadores recomiendan asegurarse de que todos nuestros pods del plano de control también estén detenidos:

crictl rm `crictl ps -aq`

Cabe recordar que kubeadm, de forma predeterminada, no sobrescribe los certificados y kubeconfigs existentes, por lo que para volver a emitirlos, primero debe eliminarlos manualmente.

Comencemos por restaurar etcd, ya que si tuviéramos un quórum (3 o más nodos maestros), el clúster etcd no se iniciará sin la mayoría de ellos presentes.

kubeadm init phase certs etcd-ca

El comando anterior generará una nueva CA para nuestro clúster etcd. Dado que todos los demás certificados deben estar firmados por él, lo copiaremos junto con la clave privada al resto de los nodos maestros:

/etc/kubernetes/pki/etcd/ca.{key,crt}

Ahora, regeneremos el resto de los certificados etcd y los manifiestos estáticos en todos los nodos del plano de control:

kubeadm init phase certs etcd-healthcheck-client
kubeadm init phase certs etcd-peer
kubeadm init phase certs etcd-server
kubeadm init phase etcd local 

En esta etapa, ya deberíamos tener un clúster etcd funcionando, mencionan los expertos en pentesting:

# crictl ps
CONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID
ac82b4ed5d83a       0369cf4303ffd       2 seconds ago       Running             etcd                0                   bc8b4d568751b

Ahora hagamos lo mismo, pero para Kubernetes, en uno de los nodos maestros, ejecute:

kubeadm init phase certs all
kubeadm init phase kubeconfig all
kubeadm init phase control-plane all
cp -f /etc/kubernetes/admin.conf ~/.kube/config 

Los comandos anteriores generarán todos los certificados SSL para nuestro clúster de Kubernetes, así como estadísticas para los manifiestos y kubeconfigs para los servicios de Kubernetes. Si está utilizando kubeadm para unirse a kubeletes, también deberá actualizar la configuración de información de clúster en el espacio de nombres kube-public todavía contiene el hash de su antigua CA.

kubeadm init phase bootstrap-token  

Dado que todos los certificados de otras instancias también deben estar firmados por una CA, cópielos en los otros nodos del plano de control y repita los comandos anteriores en cada uno de ellos, recomiendan los expertos en pentesting.

/etc/kubernetes/pki/{ca,front-proxy-ca}.{key,crt}
/etc/kubernetes/pki/sa.{key,pub}  

Como alternativa a la copia manual de certificados, ahora puede usar la interfaz de Kubernetes, por ejemplo, el siguiente comando:

kubeadm init phase upload-certs --upload-certs

A continuación se encriptarán y cargarán certificados en Kubernetes durante 2 horas, por lo que puede volver a unirse a los maestros de la siguiente manera:

kubeadm join phase control-plane-prepare all kubernetes-apiserver:6443 --control-plane --token cs0etm.ua7fbmwuf1jz946l     --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8 --certificate-key 385655ee0ab98d2441ba8038b4e8d03184df1806733eac131511891d1096be73
kubeadm join phase control-plane-join all

Vale la pena señalar que la API de Kubernetes tiene otra configuración que almacena el certificado de CA para el cliente de proxy frontal, se usa para autenticar solicitudes del apiserver en webhooks y otros servicios de capa de agregación. Afortunadamente, kube-apiserver lo actualiza automáticamente. Sin embargo, es posible que desee limpiarlo manualmente de los certificados antiguos:

kubectl get cm -n kube-system extension-apiserver-authentication -o yaml

En cualquier caso, en esta etapa ya tenemos un plano de control en pleno funcionamiento.

Arreglando los workers

Este comando enumerará todos los nodos en el clúster, aunque ahora todos estarán en el estado NotReady:

kubectl get node

Esto pasa debido a que aún están usando los certificados antiguos y esperan solicitudes de servidor firmadas por la antigua CA. Para solucionar este problema, usaremos kubeadm y crearemos un nodo de registro en el clúster.

Cuando ambos maestros tienen acceso a la CA y pueden conectarse localmente:

systemctl stop kubelet
rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/kubelet.conf
kubeadm init phase kubeconfig kubelet
kubeadm init phase kubelet-start 

Luego, para los workers de unión, generaremos un nuevo token:

kubeadm token create --print-join-command

Y en cada uno de ellos ejecutamos:

systemctl stop kubelet
rm -rf /var/lib/kubelet/pki/ /etc/kubernetes/pki/ /etc/kubernetes/kubelet.conf 
kubeadm join phase kubelet-start kubernetes-apiserver:6443  --token cs0etm.ua7fbmwuf1jz946l     --discovery-token-ca-cert-hash sha256:555f6ececd4721fed0269d27a5c7f1c6d7ef4614157a18e56ed9a1fd031a3ab8

Atención: /etc/kubernetes/pki/ no es necesario borrar el directorio en los masters, ya que contiene todos los certificados necesarios, señalan los expertos en pentesting.

El procedimiento anterior volverá a conectar todos sus cublets al clúster sin afectar los contenedores que ya se están ejecutando en ellos. Sin embargo, si tiene muchos nodos en el clúster y está haciendo esto al mismo tiempo, es posible que tenga una situación en la que Controller-Manager comience a recrear contenedores con nodos NotReady e intente iniciarlos en los nodos activos del clúster.

Para evitar esto, podemos detener temporalmente el controlador-administrador, en los maestros:

rm /etc/kubernetes/manifests/kube-controller-manager.yaml
crictl rmp `crictl ps --name kube-controller-manager -q` 

El último comando solo es necesario para asegurarse de que el controlador-administrador no se esté ejecutando realmente. Una vez que todos los nodos del clúster están conectados, podemos generar un manifiesto estático para la parte posterior del controlador-administrador.

Para hacer esto, en todos los maestros deberán ejecutar:

kubeadm init phase control-plane controller-manager

Tenga en cuenta que debe hacer esto en la etapa en la que ya haya generado el token de unión; de lo contrario, la operación de conexión se bloqueará al intentar leer el token de cluster-info.

Si el kubelet está configurado para recibir un certificado firmado por su CA (servidor opcional TLSBootstrap: true), también deberá volver a confirmar el csr de sus kubelets:

kubectl get csr
kubectl certificate approve <csr> 

Arreglar ServiceAccounts

Hay una cosa más. Dado que perdimos /etc/kubernetes/pki/sa.key, los expertos en pentesting mencionan que esta es la misma clave que firmó los tokens jwt para todas nuestras ServiceAccounts, por lo que debemos recrear los tokens para cada uno de ellos.

Esto se puede hacer simplemente eliminando el campo del token de todos los secretos como kubernetes.io/service-account-token:

kubectl get secret --all-namespaces | awk '/kubernetes.io\/service-account-token/ { print "kubectl patch secret -n " $1 " " $2 " -p {\\\"data\\\":{\\\"token\\\":null}}"}' | sh –x 

Después de eso, kube-controller-manager generará automáticamente nuevos tokens firmados con una nueva clave, mencionan los expertos en pentesting. Desafortunadamente, no todos los microservicios pueden volver a leer el token sobre la marcha, y lo más probable es que deba reiniciar manualmente los contenedores donde se utilizan:

kubectl get pod --field-selector 'spec.serviceAccountName!=default' --no-headers --all-namespaces | awk '{print "kubectl delete pod -n " $1 " " $2 " --wait=false --grace-period=0"}' 

Por ejemplo, este comando generará una lista de comandos para eliminar todos los módulos utilizando una cuenta de servicio no estándar. Recomiendo comenzar con el espacio de nombres del sistema kube. Tiene instalado kube-proxy y el complemento CNI, que son vitales para configurar sus microservicios para comunicarse.

Para conocer más sobre riesgos de seguridad informática, malware, vulnerabilidades y tecnologías de la información, no dude en ingresar al sitio web del Instituto Internacional de Seguridad Cibernética (IICS).