Guía de seguridad en Kubernetes: autenticación, autorización, auditoría

Guía de seguridad en Kubernetes: autenticación, autorización, auditoría

Tarde o temprano, en la operación de cualquier sistema surge la cuestión de la seguridad: garantizar la autenticación, la separación de privilegios, la auditoría y otras tareas. Para Kubernetes ya se ha creado una gran cantidad de soluciones, que permiten cumplir con los estándares incluso en entornos muy exigentes... Este material se centra en los aspectos fundamentales de la seguridad implementados en los mecanismos integrados de K8s. En primer lugar, será útil para aquellos que están comenzando a familiarizarse con Kubernetes, como un punto de partida para estudiar cuestiones relacionadas con la seguridad.

Autenticación

En Kubernetes, hay dos tipos de usuarios:

  • Cuentas de Servicio — cuentas gestionadas por la API de Kubernetes;
  • Usuarios — usuarios "normales", gestionados por servicios externos e independientes.

La principal diferencia entre estos tipos es que para las Cuentas de Servicio existen objetos especiales en la API de Kubernetes (que se llaman así — ServiceAccounts), que están vinculados a un espacio de nombres y a un conjunto de credenciales de autorización almacenadas en el clúster en objetos del tipo Secrets. Estos usuarios (Cuentas de Servicio) están destinados principalmente para gestionar los derechos de acceso a la API de Kubernetes de procesos que se ejecutan en el clúster de Kubernetes.

Los usuarios normales, en cambio, no tienen registros en la API de Kubernetes: su gestión debe llevarse a cabo mediante mecanismos externos. Están destinados a personas o procesos que operan fuera del clúster.

Cada solicitud a la API está vinculada ya sea a una Cuenta de Servicio, a un Usuario, o se considera anónima.

Los datos de autenticación del usuario incluyen:

  • Nombre de usuario — nombre de usuario (¡sensible a mayúsculas!);
  • UID — una cadena de identificación legible por máquina que "es más consistente y única que el nombre de usuario";
  • Groups — una lista de grupos a los que pertenece el usuario;
  • Extra — campos adicionales que pueden ser utilizados por el mecanismo de autorización.

Kubernetes puede utilizar una gran cantidad de mecanismos de autenticación: certificados X509, tokens Bearer, proxies de autenticación, HTTP Basic Auth. A través de estos mecanismos puedes implementar una gran variedad de esquemas de autorización: desde un archivo estático con contraseñas hasta OpenID OAuth2.

Moreover, the use of multiple authorization schemes is allowed simultaneously. By default, the cluster uses:

  • tokens de cuenta de servicio — para Cuentas de Servicio;
  • X509 — para Usuarios.

La pregunta sobre la gestión de ServiceAccounts queda fuera del alcance de este artículo, y a quienes deseen profundizar en este tema les recomiendo comenzar con la página de documentación oficial. Sin embargo, aquí examinaremos en detalle el tema de los certificados X509.

Certificados para usuarios (X.509)

El método clásico para trabajar con certificados implica:

  • la generación de una clave:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/.certs/mynewuser.key 2048
  • la generación de una solicitud de certificado:
    openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • el procesamiento de la solicitud de certificado mediante las claves del CA del clúster de Kubernetes, obteniendo el certificado del usuario (para obtener el certificado, se debe usar una cuenta que tenga acceso a la clave del centro de certificación del clúster de Kubernetes, que por defecto se encuentra en /etc/kubernetes/pki/ca.key):
    openssl x509 -req -in ~/.certs/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/.certs/mynewuser.crt -days 500
  • la creación de un archivo de configuración:
    • una descripción del clúster (especifique la dirección y la ubicación del archivo del certificado CA de la instalación específica del clúster):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • o — como noopción recomendada — se puede omitir el certificado raíz (entonces kubectl no verificará la validez del api-server del clúster):
      kubectl config set-cluster kubernetes  --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
    • añadiendo el usuario al archivo de configuración:
      kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt  --client-key=.certs/mynewuser.key
    • añadiendo el contexto:
      kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
    • estableciendo el contexto por defecto:
      kubectl config use-context mynewuser-context

Después de las manipulaciones anteriores, en el archivo .kube/config se creará un config con el siguiente aspecto:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: /etc/kubernetes/pki/ca.crt
    server: https://192.168.100.200:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    namespace: target-namespace
    user: mynewuser
  name: mynewuser-context
current-context: mynewuser-context
kind: Config
preferences: {}
users:
- name: mynewuser
  user:
    client-certificate: /home/mynewuser/.certs/mynewuser.crt
    client-key: /home/mynewuser/.certs/mynewuser.key

Para facilitar la transferencia de la configuración entre cuentas y servidores, es útil editar los valores de las siguientes claves:

  • certificate-authority
  • client-certificate
  • client-key

Para ello, se pueden codificar los archivos mencionados en base64 y escribirlos en la configuración, añadiendo el sufijo -data, es decir, resultando en certificate-authority-data etc.

Certificados con kubeadm

Con el lanzamiento de Kubernetes 1.15 El trabajo con certificados se ha vuelto mucho más fácil gracias a la versión alfa de su soporte en la utilidad kubeadm. Por ejemplo, así es como puede verse ahora la generación del archivo de configuración con las claves del usuario:

kubeadm alpha kubeconfig user --client-name=mynewuser --apiserver-advertise-address 192.168.100.200

NB: Requerido dirección de publicidad se puede consultar en la configuración del api-server, que por defecto se ubica en /etc/kubernetes/manifests/kube-apiserver.yaml.

La configuración resultante se mostrará en stdout. Debe guardarse en ~/.kube/config la cuenta del usuario o en un archivo especificado en la variable de entorno KUBECONFIG.

Profundizando

Para quienes deseen profundizar más en los temas descritos:

Autorización

La cuenta autorizada por defecto no tiene derechos de acción en el clúster. Para otorgar permisos en Kubernetes se implementó un mecanismo de autorización.

Hasta la versión 1.6 de Kubernetes, se utilizaba un tipo de autorización llamado ABAC (control de acceso basado en atributos). Los detalles sobre esto se pueden encontrar en documentación oficial. Actualmente, este enfoque se considera obsoleto (legacy), sin embargo, todavía puedes usarlo junto con otros tipos de autorización.

El modo actual (y más flexible) de segmentar los derechos de acceso al clúster se llama RBAC (control de acceso basado en roles). Se declaró estable a partir de la versión Kubernetes 1.8. RBAC implementa un modelo de derechos en el que se prohíbe todo lo que no esté explícitamente permitido.
Para habilitar RBAC, debes iniciar el api-server de Kubernetes con el parámetro --authorization-mode=RBAC. Los parámetros se establecen en el manifiesto con la configuración del api-server, que por defecto se encuentra en la ruta /etc/kubernetes/manifests/kube-apiserver.yaml, en la sección command. Sin embargo, por defecto, RBAC ya está habilitado, así que probablemente no debas preocuparte por esto: puedes verificarlo por el valor authorization-mode (en el ya mencionado kube-apiserver.yaml). A propósito, entre sus valores también pueden estar otros tipos de autorización (node, webhook, always allow), pero dejaremos su discusión fuera de este material.

A propósito, ya hemos publicado artículo una explicación bastante detallada sobre los principios y características del trabajo con RBAC, así que a continuación me limitaré a enumerar brevemente los fundamentos y ejemplos.

Para gestionar el acceso en Kubernetes a través de RBAC se utilizan las siguientes entidades API:

  • Role y ClusterRole — los roles que se utilizan para describir los derechos de acceso:
  • Role permite describir derechos dentro del espacio de nombres;
  • ClusterRole — dentro del clúster, incluidos objetos específicos del clúster como nodos, URLs no de recursos (es decir, no relacionados con los recursos de Kubernetes — por ejemplo, /version, /logs, /api*);
  • RoleBinding y ClusterRoleBinding — sirve para vincular Role y ClusterRole a un usuario, grupo de usuarios o ServiceAccount.

Las entidades Role y RoleBinding están limitadas al namespace, es decir, deben estar dentro de un solo espacio de nombres. Sin embargo, RoleBinding puede hacer referencia a ClusterRole, lo que permite crear un conjunto de permisos típicos y gestionar el acceso con ellos.

Los roles describen los derechos mediante conjuntos de reglas que contienen:

  • grupos de API — ver la documentación oficial por apiGroups y salida kubectl api-resources;
  • recursos (resources: pod, namespace, deployment etc.);
  • verbos (verbs: set, update etc.).
  • nombres de recursos (resourceNames) — en el caso de que sea necesario proporcionar acceso a un recurso específico, y no a todos los recursos de ese tipo.

Una descripción más detallada de la autorización en Kubernetes se puede encontrar en la página documentación oficial. En lugar de esto (más precisamente — además de esto) proporcionaré ejemplos que ilustran su funcionamiento.

Ejemplos de entidades RBAC

Sencillo Role, que permite obtener la lista y el estado de los pod y monitorearlos en el espacio de nombres target-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: target-namespace
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Ejemplo ClusterRole, lo que permite obtener la lista y el estado de los pod y monitorearlos en todo el clúster:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # no hay sección "namespace", ya que ClusterRole afecta a todo el clúster
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Ejemplo RoleBinding, lo que permite al usuario mynewuser «leer» pod en el espacio de nombres my-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: target-namespace
subjects:
- kind: User
  name: mynewuser # nombre de usuario sensible a mayúsculas y minúsculas!
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # aquí debe ser “Role” o “ClusterRole”
  name: pod-reader # nombre de Role que está en el mismo namespace,
                   # o nombre de ClusterRole, cuyo uso
                   # queremos permitir al usuario
  apiGroup: rbac.authorization.k8s.io

Auditar eventos

La arquitectura de Kubernetes se puede representar esquemáticamente de la siguiente manera:

Guía de seguridad en Kubernetes: autenticación, autorización, auditoría

El componente clave de Kubernetes, responsable de procesar las solicitudes, es api-server. Todas las operaciones en el clúster pasan a través de él. Puedes leer más sobre estos mecanismos internos en el artículo «¿Qué sucede en Kubernetes al ejecutar kubectl run?».

La auditoría del sistema es una característica interesante en Kubernetes que está desactivada por defecto. Permite registrar todas las solicitudes al API de Kubernetes. Como se puede adivinar fácilmente, a través de esta API se realizan todas las acciones relacionadas con el control y la modificación del estado del clúster. Una buena descripción de sus capacidades se puede (como suele ser habitual) encontrar en documentación oficial K8s. A continuación, intentaré exponer el tema de manera más sencilla.

Así que, Para habilitar la auditoría, debemos pasar al contenedor en api-server tres parámetros obligatorios, de los cuales se habla con más detalle a continuación:

  • --audit-policy-file=/etc/kubernetes/policies/audit-policy.yaml
  • --audit-log-path=/var/log/kube-audit/audit.log
  • --audit-log-format=json

Además de estos tres parámetros necesarios, existen muchas configuraciones adicionales relacionadas con la auditoría: desde la rotación de registros hasta descripciones de webhook. Ejemplo de parámetros de rotación de registros:

  • --audit-log-maxbackup=10
  • --audit-log-maxsize=100
  • --audit-log-maxage=7

Pero no nos detendremos más en ellos: se pueden encontrar todos los detalles en la documentación de kube-apiserver..

Como se mencionó anteriormente, todos los parámetros se configuran en el manifiesto con la configuración del api-server (por defecto /etc/kubernetes/manifests/kube-apiserver.yaml), en la sección command. Volvamos a los 3 parámetros obligatorios y analicémoslos:

  1. audit-policy-file — la ruta al archivo YAML que describe la política de auditoría. Volveremos a su contenido más adelante, pero por ahora mencionaré que el archivo debe ser accesible para ser leído por el proceso del api-server. Por lo tanto, es necesario montarlo dentro del contenedor, para lo cual se puede agregar el siguiente código en las secciones correspondientes de la configuración:
      volumeMounts:
        - mountPath: /etc/kubernetes/policies
          name: policies
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/policies
          type: DirectoryOrCreate
        name: policies
  2. audit-log-path — la ruta al archivo de registro. Esta ruta también debe ser accesible para el proceso del api-server, por lo que describimos su montaje de manera similar:
      volumeMounts:
        - mountPath: /var/log/kube-audit
          name: logs
          readOnly: false
      volumes:
      - hostPath:
          path: /var/log/kube-audit
          type: DirectoryOrCreate
        name: logs
  3. audit-log-format — el formato del registro de auditoría. Por defecto, es json., pero también está disponible un formato de texto obsoleto (legacy).

Política de auditoría

Ahora sobre el mencionado archivo que describe la política de registro. El primer concepto de la política de auditoría es level, nivel de registro. Existen los siguientes tipos:

  • None — no registrar;
  • Metadata — registrar metadatos de la solicitud: usuario, hora de la solicitud, recurso objetivo (pod, namespace, etc.), tipo de acción (verb) y demás;
  • Request — registrar metadatos y el cuerpo de la solicitud;
  • RequestResponse — registrar metadatos, el cuerpo de la solicitud y el cuerpo de la respuesta.

Los últimos dos niveles (Request y RequestResponse) no registran solicitudes que no accedieron a los recursos (accesos a URLs no recursos).

Además, todas las solicitudes pasan por varias etapas:

  • RequestReceived — etapa en la que la solicitud es recibida por el manejador y aún no es pasada a la cadena de manejadores;
  • ResponseStarted — se han enviado los encabezados de respuesta, pero antes de enviar el cuerpo de la respuesta. Se genera para solicitudes prolongadas (por ejemplo, ver);
  • ResponseComplete — el cuerpo de la respuesta ha sido enviado, no se enviará más información;
  • Panic — se generan eventos cuando se detecta una situación anómala.

Para omitir alguna etapa se puede usar omitStages.

En el archivo de políticas podemos describir varias secciones con diferentes niveles de registro. Se aplicará la primera regla apropiada encontrada en la descripción de la política.

El demonio kubelet rastrea los cambios en el manifiesto con la configuración del api-server y al detectarlos reinicia el contenedor con el api-server. Pero hay un detalle importante: los cambios en el archivo de política serán ignorados. Después de realizar cambios en el archivo de política, será necesario reiniciar manualmente el api-server. Dado que el api-server se ejecuta como static pod, equipo kubectl delete no llevará a su reinicio. Será necesario ejecutar manualmente docker stop en los kube-masters donde se ha cambiado la política de auditoría:

docker stop $(docker ps | grep k8s_kube-apiserver | awk '{print $1}')

Al habilitar la auditoría, es importante recordar que aumenta la carga en el kube-apiserver. En particular, el consumo de memoria para almacenar el contexto de las solicitudes aumenta. El registro en los logs comienza solo después de enviar el encabezado de respuesta. Además, la carga depende de la configuración de la política de auditoría.

Ejemplos de políticas

Analicemos la estructura de los archivos de políticas con ejemplos.

Aquí hay un archivo simple policy, para registrar todo a nivel Metadata:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

En la política se puede especificar una lista de usuarios (Usuarios y ServiceAccounts) y grupos de usuarios. Por ejemplo, así ignoraremos a los usuarios del sistema, pero registraremos todo lo demás a nivel Request:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: None
    userGroups:
      - "system:serviceaccounts"
      - "system:nodes"
    users:
      - "system:anonymous"
      - "system:apiserver"
      - "system:kube-controller-manager"
      - "system:kube-scheduler"
  - level: Request

También hay la posibilidad de describir los objetivos:

  • espacios de nombres (namespaces);
  • verbos (verbs: get, update, delete y otros);
  • recursos (resources, a saber: pod, configmaps etc.) y grupos de recursos (apiGroups).

¡Atención! Los recursos y grupos de recursos (grupos API, es decir, apiGroups), así como sus versiones disponibles en el clúster, se pueden obtener mediante los comandos:

kubectl api-resources
kubectl api-versions

La siguiente política de auditoría se presenta como una demostración de las mejores prácticas en la documentación de Alibaba Cloud:

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# No registrar la etapa RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # No registrar eventos que se consideren poco significativos y no peligrosos:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # este es el grupo API con un nombre vacío, al que pertenecen
                  # los recursos básicos de Kubernetes, llamados “core”
        resources: ["endpoints", "services"]
  - level: None
    users: ["system:unsecured"]
    namespaces: ["kube-system"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["configmaps"]
  - level: None
    users: ["kubelet"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["nodes"]
  - level: None
    userGroups: ["system:nodes"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["nodes"]
  - level: None
    users:
      - system:kube-controller-manager
      - system:kube-scheduler
      - system:serviceaccount:kube-system:endpoint-controller
    verbs: ["get", "update"]
    namespaces: ["kube-system"]
    resources:
      - group: "" # core
        resources: ["endpoints"]
  - level: None
    users: ["system:apiserver"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["namespaces"]
  # No registrar accesos a URLs de solo lectura:
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /swagger*
  # No registrar mensajes relacionados con el tipo de recurso “eventos”: 
  - level: None
    resources:
      - group: "" # core
        resources: ["events"]
  # Los recursos de tipo Secret, ConfigMap y TokenReview pueden contener datos secretos,
  # por lo tanto, solo registramos los metadatos de las solicitudes relacionadas con ellos
  - level: Metadata
    resources:
      - group: "" # core
        resources: ["secrets", "configmaps"]
      - group: authentication.k8s.io
        resources: ["tokenreviews"]
  # Las acciones de tipo get, list y watch pueden ser intensivas en recursos; no las registramos
  - level: Request
    verbs: ["get", "list", "watch"]
    resources:
      - group: "" # core
      - group: "admissionregistration.k8s.io"
      - group: "apps"
      - group: "authentication.k8s.io"
      - group: "authorization.k8s.io"
      - group: "autoscaling"
      - group: "batch"
      - group: "certificates.k8s.io"
      - group: "extensions"
      - group: "networking.k8s.io"
      - group: "policy"
      - group: "rbac.authorization.k8s.io"
      - group: "settings.k8s.io"
      - group: "storage.k8s.io"
  # Nivel de registro predeterminado para los recursos estándar de la API
  - level: RequestResponse
    resources:
      - group: "" # core
      - group: "admissionregistration.k8s.io"
      - group: "apps"
      - group: "authentication.k8s.io"
      - group: "authorization.k8s.io"
      - group: "autoscaling"
      - group: "batch"
      - group: "certificates.k8s.io"
      - group: "extensions"
      - group: "networking.k8s.io"
      - group: "policy"
      - group: "rbac.authorization.k8s.io"
      - group: "settings.k8s.io"
      - group: "storage.k8s.io"
  # Nivel de registro predeterminado para todas las demás solicitudes
  - level: Metadata

Otro buen ejemplo de política de auditoría — perfil utilizado en GCE.

Para la respuesta rápida a los eventos de auditoría hay posibilidad de describir un webhook. Este tema se detalla en documentación oficial, lo dejaré fuera del alcance de este artículo.

Resultados

El artículo ofrece una visión general de los mecanismos básicos de seguridad en clústeres de Kubernetes, que permiten crear cuentas personalizadas para los usuarios, dividir sus derechos y registrar sus acciones. Espero que sea útil para quienes se han enfrentado a tales cuestiones en teoría o en la práctica. También recomiendo consultar la lista de otros materiales sobre el tema de la seguridad en Kubernetes, que se presenta en el «P.S.», ya que es posible que encuentren detalles necesarios para los problemas que les preocupan.

P.D.

También puedes leer en nuestro blog:

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster