No se puede discutir que Kubernetes ya está omnipresente en prácticamente todos los proyectos en los que trabajamos a día de hoy, siendo además una pieza clave en muchos proveedores de servicios en la nube como servicio gestionado. A pesar de ello, independientemente del sabor que usemos de Kubernetes, siempre debemos lidiar con el desafío de la gestión de los secretos.

Hoy es habitual utilizar algún gestor de secretos como Hashicorp Vault, o algún KMS (Key Management Service) de cualquier proveedor de nube, pero siempre debemos llevar a cabo un proceso de integración del mismo, usando recursos específicos de la nube, o agentes para proveer los secretos en nuestras cargas de trabajo. ¿Y si existiera una solución que llevara a cabo la integración con prácticamente cualquier gestor de secretos y todo gestionado mediante Custom Resources de Kubernetes? Os presentamos External Secrets Operator.

¿Qué es External Secrets Operator?

External Secrets Operator es un operador de Kubernetes que se integra con múltiples proveedores, obtiene la información y monta un secreto de Kubernetes con el contenido.

Entre las principales ventajas que podemos destacar son:

  1. Seguridad mejorada: se separa la frontera entre la gestión de los secretos del código fuente y los manifiestos de Kubernetes. De esta forma podemos adoptar un modelo gitops más apropiado sin tener que referenciar los secretos en ningún repositorio
  2. Gestión centralizada: los equipos de seguridad gestionan los secretos en un único punto, reduciendo la cantidad de sitios donde actualizar, rotar y auditar los secretos.
  3. Integración con múltiples proveedores de secretos: External Secret Operator, como se ha mostrado, es compatible con múltiples sistemas de gestión de secretos. De hecho, en un mismo clúster pueden integrarse todos los sistemas que se consideren necesarios y utilizarlos de manera homogénea.
  4. Facilidad de automatización: usando External Secrets Operator, se puede gestionar el ciclo de despliegue usando un modelo gitops usando los objetos que External Secret Operator nos ofrece. Además, siempre las cargas de trabajo dispondrán de la versión más actualizada del secreto haciendo uso de una sincronización programada o mediante un webhook.

Si ya te has convencido, vamos a comenzar a usarlo.

Puede instalarse usando su chart de Helm y en breves instantes ya dispondrás de los siguientes CRD’s (entre otros).

Para ponerlo en práctica vamos a exponer un sencillo ejemplo en el que configuraremos un SecretStore en nuestro cluster de Kubernetes integrando el acceso contra un servidor Hashicorp Vault usando como provider un token y el engine kv (key-value) version2. Después crearemos un ExternalSecret para recuperar una clave almacenada en la siguiente ruta /demo/mysecretapp. Toda la configuración quedará generada en el namespace external-secrets-example. Suponemos que el despliegue de External Secret Operator ya se ha llevado a cabo.

Comenzamos creando el namespace:

kubectl create namespace external-secrets-example

Creamos un secreto con el valor del token en el mismo namespace:

kubectl create secret generic vault-token --from-literal=token=<introduce aquí el token de vault> -n external-secrets-example

Creamos el SecretStore en el namespace donde vayamos a usarlo indicando la información de conexión a nuestro servidor de Vault y referenciando el nombre del secreto creado en el anterior paso:

SecretStore.yaml

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: example-secret-store
  namespace: external-secrets-example
spec:
  provider:
    vault:
      auth:
          tokenSecretRef:
            key: token
            name: vault-token
      server: https://vault.example.com/
      version: v2

Para comprobar que nuestro SecretStore está correctamente configurado, podemos ejecutar el comando kubectl get secretstore -n external-secrets-example y veremos una salida parecida a la siguiente:

NAME                   AGE   STATUS   CAPABILITIES   READY
example-secret-store   1m    Valid    ReadWrite      True

Por último, crearemos un objeto de tipo ExternalSecret, que referencie a nuestro SecretStore y que devuelva todas las entradas localizadas en la ruta anteriormente descrita:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: external-secret-example
  namespace: external-secrets-example
spec:
  data:
  - remoteRef:
      conversionStrategy: Default
      decodingStrategy: None
      key: demo/mysecretapp
      property: username
    secretKey: eso-vault-secret
  refreshInterval: 30s
  secretStoreRef:
    kind: SecretStore
    name: example-secret-store
  target:
    creationPolicy: Owner
    deletionPolicy: Retain
    name: eso-secret-example

En este manifest de ExternalSecret, podemos observar como el bloque secretStoreRef apunta al secretStore que hemos creado en el paso anterior. A su vez, el bloque remoteRef hace referencia al path y la propiedad que deseamos recuperar de nuestro secretStore. Por último, en el bloque target referenciamos el nombre del secreto que queremos que se cree y la política tanto de creación del secreto, así como lo que queremos que suceda con el secreto cuando el ExternalSecret sea borrado (en este caso, que se mantenga).

El resultado en este caso será un secreto que contiene una clave llamada eso-vault-secret y con el contenido albergado en el path descrito en la clave "key". Además, solo se obtendrá la propiedad llamada username.

Como puede observarse, si existieran dos o más secretStore (o clusterSecretStore), podríamos recuperar la información de ellos creando distintos ExternalSecrets referenciado a cada uno de ellos, pero accediendo a los secretos de forma homogénea independiente del proveedor de secretos.

Gobernar la gestión

Cómo se puede inferir del ejemplo, existen distintas maneras de gobernar la gestión de los secretos en el clúster de Kubernetes.

Generar un clustersecretstore y dar acceso a los namespaces específicos que deben disponer del acceso a dicho secretstore. Así delimitamos el acceso al proveedor de secretos a los namespaces específicos. Este escenario sería el más apropiado cuando se comparte entre todos los usuarios la información de los secretos a utilizar y sea único el gestor de secretos.

Generar un secretstore por namespace. Esta aproximación tiene sentido cuando existe un administrador de secretos y que dará un acceso específico al gestor de claves a cada tenant (y namespace) que se ejecuta en Kubernetes.

La última opción, cuando existen múltiples tenant, distintos proveedores de secretos o para pruebas, es únicamente desplegar ESO y que cada proyecto gestiones su propio acceso a su gestor de secretos.

Conclusiones

Para concluir, creemos que la implementación de esta herramienta resuelve uno de los retos más comunes en el uso de aplicaciones distribuidas y la gestión de los secretos de una forma unificada y predecible. Su implementación es tan sencilla y el coste beneficio obtenido es muy elevado teniendo en cuenta todos los problemas que resuelve.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.