Ir al contenido
  1. Posts/

Encriptando Secretos en Terraform con Sops y Age

·700 palabras·4 mins· loading · loading ·
DevOps Terraform Infraestructura Seguridad devops terraform infraestructura seguridad mozilla secrets
Autor
Enmanuel Moreira
Ingeniero DevOps de día y aprendiz de Barman en mis tiempos libres, con experiencia en Kubernetes, Cloud, y DevOps. También disfruta de hacer stream de juegos, hablar de CI/CD, desplegar en producción un viernes con Terraform y automatizar tareas aburridas con Ansible.

Guardar datos sensibles en texto plano es una pésima idea, no importa cuanto leas esto. Por suerte, hay herramientas que nos permiten encriptar esas passwords, claves de API y demás y de la que les vengo a hablar en este post es de SOPS. Si bien ya lo había tratado en un post anterior que te dejo por aquí, en Terraform ocurre que podemos guardar esas variables sensibles en el archivo terraform.tfvars. Sin embargo, el tema se complica cuando guardamos nuestro código en Git y en sus servicios derivados (GitLab, GitHub, etc).

Aprenderemos a guardar de manera segura nuestros secretos aprovechando el Provider de Terraform carlpett/terraform-provider-sops y age para generar la llave de encriptación.

PROMO DigitalOcean
#

Antes de comenzar, quería contarles que hay una promoción en DigitalOcean donde te dan un crédito de USD 200.00 durante 60 días para que puedas probar los servicios que este Proveedor Cloud ofrece. Lo único que tienes que hacer es suscribirte a DigitalOcean con el siguiente botón:

DigitalOcean Referral Badge

O a través del siguiente enlace: https://bit.ly/digitalocean-itsm

Prerrequisitos
#

Tener SOPS y Age instalado, el cual pueden seguir este tutorial https://blog.enmanuelmoreira.com/protegiendo-secretos-con-mozilla-sops-y-age/#instalando-sops

Generando Llave de Encriptación
#

Con Age procedemos a generar una nueva llave, es necesario que la misma se encuentre en el directorio .sops en la carpeta de usuario de nuestra máquina:

age-keygen -o $HOME/.sops/key.txt

Y exportaremos una variable de entorno hacia nuestro .zshrc o .bashrc

Zsh:

echo "export SOPS_AGE_KEY_FILE=$HOME/.sops/key.txt" >> ~/.zshrc

Bash:

echo "export SOPS_AGE_KEY_FILE=$HOME/.sops/key.txt" >> ~/.bashrc

Configurando Provider SOPS
#

En nuestras recetas de Terraform, declaramos el Provider SOPS para que pueda desencriptar nuestros secretos cada vez que apliquemos los cambios a nuestra infraestructura:

# Provider information
terraform {
  required_version = ">= 0.15.0"

  required_providers {
    sops = {
      source  = "carlpett/sops"
      version = "0.7.2"
    }
  }
}

# Declaramos nuestro archivo yaml que contendrá nuestros secretos
data "sops_file" "secrets" {
  source_file = "secret.sops.yml"
}

Invocando al recurso sops_file nos va a permitir leer el contenido de dicho archivo y poder asignar los valores a variables, como veremos en un momento.

En el archivo secrets.sops.yml guardamos todos los datos sensibles que SOPS leerá por nosotros, el cual se vería mas o menos de esta forma:

proxmox_api_token_id: usuario@pve!terraform
proxmox_api_token_secret: XXXXXXXX-XXXX-XXXX-XXXX-XXX
proxmox_api_url: https://ip-host-proxmox:8006/api2/json

Y ahora viene la parte interesante: vamos a tomar los valores del archivo secret.sops.yml y con el módulo data invocar las variables que van a ser leídas, como ejemplo, tomaremos los datos de autenticación de un servidor de Proxmox, pero puede ser cualquier secreto:

# Configuration options
provider "proxmox" {
  pm_api_url          = data.sops_file.secrets.data["proxmox_api_url"]
  pm_api_token_id     = data.sops_file.secrets.data["proxmox_api_token_id"]
  pm_api_token_secret = data.sops_file.secrets.data["proxmox_api_token_secret"]
  pm_tls_insecure     = true

Ya con todo esto configurado, encriptamos el archivo secret.sops.yml:

sops --encrypt --age $(cat $SOPS_AGE_KEY_FILE |grep -oP "public key: \K(.*)") --in-place secret.sops.yml

Si repasamos un poco en esta parte del comando --age $(cat $SOPS_AGE_KEY_FILE |grep -oP "public key: \K(.*)") le estamos indicando a sops que encripte con nuestra llave publica de Age (ya que vienen tanto publica como privada en el mismo archivo key.txt)

El aspecto final que tendrá nuestro secret.sops.yml será mas o menos el siguiente:

proxmox_api_token_id: ENC[AES256_GCM,data:ttEAL+8QCYJprV5G9NEO+2fiEFfn,iv:H+0oQirQ3hzdUMo9ZlGjcH5XaYilbyV1Cmfl/fevjpU=,tag:O6g3KCi1BfKM9MyKmlEyMQ==,type:str]
proxmox_api_token_secret: ENC[AES256_GCM,data:OcSfZcXBsIN6kssxvfgohhTBdNupdzwF8VjY,iv:s9EC3RS/e/1qFiJHu74U0xWwiBGD8+IaMD6A6115Cz0=,tag:ddkUXlKCHc6r6TCd/KrAuQ==,type:str]
proxmox_api_url: ENC[AES256_GCM,data:Yg5He+T9dq7XsQY9hCASjmnj3WkcJBrSFfCY4qH8fJ1wbUBY/CU=,iv:aF5+Z6shk5pxAdrKfurPadnLypCCeI7QM2NkrHVIjRI=,tag:yawf+Jav+UTN8SGsRJsikg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    age:
        - recipient: age1vjmd692zfxh5fajl5zztgymp8te6ztpj5zux2hqwmyllg800wngks0q20s3
          enc: |
            -----BEGIN AGE ENCRYPTED FILE-----
            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJSmVPTFNmQVI5dWV2WEto
            ZVNKc3hVR3QxWnZWZFdPNk5Tai82Smtyb21BCjdTcXVwM3FyQStZckoyeWt2NDJL
            QVIwSUxYZ215UE9WTFdTRWdvYS93YVUKLS0tIHFQYzNwVlgzNzZhQjY5MzhycVd0
            c1hMUHlaa2czamV0dndoS1grWS9SbkEKpiIO/OxGITvVFrC9aQOxNlO1rwNFcQNx
            L8IBjMh+lrCODrZ736AA4TFrGA8S1ckELCjqvpfqWH8Dd95y3VgMmA==
            -----END AGE ENCRYPTED FILE-----            
    lastmodified: "2023-05-18T22:53:09Z"
    mac: ENC[AES256_GCM,data:ygwOi6FilkuReXn/3W7EPgINsrmc28D/Tl3Z9FD/E+h7cRWC8xoa3SRH5pYnb6cOjro8MtKfTSkaMTdFfdgDYa+v6ZwqkvCr46EY48W5H7jrvBauwnR1Mjzr0YtZA7NM6G8kpmjwlU1A2aJaFkTzlhWot5Yg2FEM0DMCrp3tnAM=,iv:4A8ANkcf8bFQQ45OueP4tX4ha752sN2DeCvdkHbH2M4=,tag:oqbGHtkfu4Io4USKrfRHww==,type:str]
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.7.3

Cool, ¿cierto?. Bueno, ahora pasemos a configurar el Provider SOPS en Terraform.

Probando con Terraform
#

Inicializamos Terraform y nos descargará el Provider SOPS:

terraform init

Ejecutamos un plan para ver los cambios:

terraform plan

Y luego aplicamos los cambios:

terraform apply --auto-approve
data.sops_file.secrets: Refreshing state...
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
Terraform will perform the following actions:
Plan: 0 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.
Enter a value: yes
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
XXXXXX Obviamente no vamos a dejar un dato sensible como Output xD

Y con esto, podríamos guardar de manera segura nuestros secretos en GitLab o GitHub.

Espero les haya gustado y nos vemos en la próxima!