Encriptar secretos de Helm con Mozilla SOPS y AWS KMS

Una de las partes importantes que tenemos que considerar cuando utilizamos Helm es gestionar los secrets. Hay muchas maneras de hacerlo. Podemos usar Hashicorp vault, el Parameter Store de AWS…

Para mí, la combinación de Mozilla SOPS y AWS KMS es una buena manera de guardar los secretos en nuestros repositorios de forma fácil y segura.

AWS KMS

AWS KMS nos permite crear y gestionar claves de cifrado. Hay diferentes tipos de claves simétricas y asimétricas. En este ejemplo utilizaremos una clave KMS simétrica.

Crear un rol AIM

Utilizo Jenkinkins para integración y despliegue continues, con diferentes credenciales IAM para acceder a distintas cuentas en AWS. También hay usuario que editan los secretos y los guardan en repositorios.

Cuando empecé a experimentar con SOPS y KMS me encontré con un problema al encriptar o desencriptar los secretos, dependiendo de quién, Jenkins o un usuario, lo hubiese encriptado primero. La solución fue añadir un rol que tiene permisos para usar la clave KMS que se usa para cifrar. Tanto los usuarios como Jenkins usan ese rol para interactuar con KMS.

Para hacer esto creé un rol sin ningún tipo de permisos y añadí las diferentes cuentas de AWS que uso a la lista de Entidades de confianza. De esta forma, todos mis usuarios, desde las diferentes cuentas, pueden adoptarlo.

Toma nota del ARN, lo usaremos más tarde.

Controlar quién puede adoptar el rol

Para controlar quién puede adoptar el rol usamos la siguiente política en AIM

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "123",
      "Effect": "Allow",
      "Action": [
        "sts:AssumeRole"
      ],
      "Resource": [
        "arn:aws:iam::yourAWSAccount:role/yourRoleName"
      ]
    }
  ]
}

Puedes añadirla directamente a los usuarios o utilizar grupos. Lo que mejor te venga en tú situación.

Crear una clave simétrica

Aquí uso algunos valores como ejemplo. Reemplazalos con lo que vayas a usar.

  1. Crear clave
  2. Configurar clave
    • Tipo de clave: Simétrico
    • Opciones avanzadas:
      • Origen del material de claves: KMS
  3. Crear alias y descripción
    • Alias: helm-secrets
    • Descripción: This is used to encrypt Helm secrets
  4. Administradores de clavez:
    • Añade lo que más te convenga aquí.
  5. Definir permisos de uso de claves:
    • Añade el rol que creaste anteriormente.
Toma nota del ARN, lo usaremos más tarde.

SOPS

SOPS es un editor de ficheros encriptados desarollado por Mozilla. Soporta YAML (ideal para nuestros values fies), JSON, ENV… y también soporta la encriptación con AWS, GCP KMS, Azure Key Vault y PGP.

Demo sacada del proyecto SOPS en GitHub

Instalar SOPS

Puedes encontrar la última versión estable en el GitHub del proyecto.

Fichero de configuración de SOPS

Ahora debemos crear un fichero de configuración .sops.yaml en el directorio raíz de nuestro proyecto. Esto le dirá a SOPS que KMS debe utilizar, qué parte del fichero YAML queremos encryptar y a qué ficheros afectaran estas reglas.

Para mí, la posibilidad de encriptar sólo una parte del fichero es algo muy interesante. Nos permite encriptar sólo contraseñas y otros secretos, dejando que el resto del values file sea muy fácil de leer, por ejemplo, para revisarlo antes de hacer un merge.

La configuración es de este estilo:

---
creation_rules:
  - path_regex: values.*.yaml$
    kms: 'arn_of_your_kms+arn_of_the_role'
    encrypted_suffix: secrets

Línea por línea:

  • path_regex: values.*.yaml$
    • Las reglas sólo afectan a cuyos nombres cuadren con esta expresión regular.
  • kms: 'arn_of_your_kms+arn_of_the_role'
    • ARN de la clave KMS que creaste + ARN del rol IAM que usas para acceder a la misma.
    • Necesitas incluir el símbolo +.
    • No dejes espacios entre los ARNs y el símbolo.
    • Si no utilizas un rol no necesitas la segunda parte. Sólo el ARN de tu clave KMS.
  • encrypted_suffix: secrets
    • SOPS sólo encriptará las entradas en la sección secrets del values file.

Encriptando nuestro values file

Usemos este ejemplo:

minReplicas: 5
maxReplicas: 20
targetCPUUtilizationPercentage: 50
image:
    name: someImageName
    tag: v1.2.3
config:
    open:
        SOME_API_ENDPOINT: http://api.example.com/svc/some-service/
        DB_NAME: mydatabase
        DB_HOST: somedbhost
    secrets:
        DB_USER: mydbuser
        DB_PASSWORD: mysupersecretpassword

Ya que una de nuestras reglas es que sólo se encriptará la sección secrets, todo lo demás se mantendrá en texto plano. Podemos ejecutar los siguientes comandos:

  • sops -e values.example.yaml
    • Esto encriptará el fichero e imprimirá el resultado por pantalla (sin guardarlo).
    • Piedes redirigir la salida a un fichero.
  • sops -e -i values.example.yaml
    • Esto encriptará el fichero y lo guardará con su extensión original.

Una vez encriptado quedará de la siguiente forma:

minReplicas: 5
maxReplicas: 20
targetCPUUtilizationPercentage: 50
image:
    name: someImageName
    tag: v1.2.3
config:
    open:
        SOME_API_ENDPOINT: http://api.example.com/svc/some-service/
        DB_NAME: mydatabase
        DB_HOST: somedbhost
    secrets:
        DB_USER: ENC[AES256_GCM,data:+LkJ1nV95lzCkrVbk4JNGH5HhH2R3WN9hmpdLFd1K6Q2Yxz5ZkDEPC1jogO2uXNPQONvN5LrS5VSytx14==,type:str]
        DB_PASSWORD: ENC[AES256_GCM,data:67oWHhA2l3kvzL8PCCbsDzYVeuSNTVo9p9CeFDIRlFkx39JIzOByWXhTWIxzawIXqO1WkwRPsreUXTRH==,type:str]
sops:
    kms:
    -   arn: arnOfMyKMS
        role: arnOfMyRole
        created_at: '2020-10-17T21:34:08Z'
        enc: wq4htZxKTIN0pu4r8DxYuyEfkmpRUzmy4uqSuv9GSIyWzARMXqyUhEm2qYAgFk2k6qzVuY2bdxLXoTbw7mIT1VvmtbKVCweJp6RlfGqb9AbKwl6HKNKwQs3Dnd2cPFNkB3G0KpIXC4RiIwDtzzZI6D9hqvxFphljGh1eDCkxrd3NG5mcKHDiivUBe7te64L8zQLL0kLW3Bm7rtSGVz6eVlSWs6kQnLviaXLWrdghlP94Apzk==
        aws_profile: ""
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-10-17T21:34:09Z'
    mac: ENC[AES256_GCM,data:/Qmx0X86psXm8eCnfoZLGWGP8bGlRBF8QvCYz9tPE7XkiQ15HAY9SM4ZdbY79RBfTvAuyazcUfQcC9WaC/KlwsTtUqqXpZUbBiA9F8RRdU0vhrAg6ChmWuoUR98yVbUOgUYXm049J5BDbe5RrgiKg0NuRmrRtO8Y0L/5DmwBc2AqcZWnztf30OkbU089aklnVMZhS8JaHAcRqLUUM61SUn9EzG1QqsDF0QaIhf2cCssyDITJFEr==,type:str]
    pgp: []
    encrypted_suffix: secrets
    version: 3.5.0

Como puedes ver, sólo las entradas de la sección secrets han sido encriptadas y además, SOPS ha añadido algunos metadatos.

Desencriptar nuestro values file

Para desencriptar un fichero simplemente ejecuta:

  • sops -d values.example.yaml (salida por pantalla)
  • sops -d -i values.example.yaml (desencriptar y guardar con la misma extensión)
Una cosa más. Ejecutar sops nombreDelFichero lo abrirá en el editor que tengas seleccionado por defecto (vim, nano…) y lo encriptará automáticamente cuando lo guardes y cierres.

Creando secretos con Helm

Ahora que nuestros secretos están encriptados con seguridad es hora de usarlos con Helm. Una plantilla para hacerlo podría ser:

apiVersion: v1
kind: Secret
metadata:
  name: {{ template "my-chart.fullname" . }}
  labels:
    {{- include "my-chart.someLabels" . | nindent 4 }}
type: Opaque
data:
{{- range $key, $value := .Values.config.secrets }}
  {{ $key }}: {{ $value | b64enc | quote }}
{{- end -}}

La forma en la que desencriptes el values file para utilizarlo depende de cómo sean tus procesos. Zendesk ha desarrollado helm-secrets, pero yo nunca lo he usado así que no puedo comentar. Yo desencripto los ficheros y los limpio despues como parte del código de Jenkins que se encarga de los despliegues.


¡Gracias de nuevo por visitar mi blog! ¿Qué usas para gestionar tus secretos? Por favor, déjame algún comentario abajo y si tienes alguna pregunta no dudes en hacerla.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *