Terraform Essentials IV: Cómo gestionar "secretos" en Terraform.

Terraform Essentials IV: Cómo gestionar "secretos" en Terraform.

En este artículo, veremos cómo gestionar passwords y tokens cuando trabajamos con Terraform.

Llegamos al cuarto episodio de esta serie llamada #TerraformEssentials. Por si te perdiste los tres artículos anteriores, te dejo el enlace a cada uno o de ellos. Si queres saber un poco más sobre Terrafom, podés verlo en la categoría de este blog.

Terraform Essentials I: Cómo conectarlo con Docker y desplegar un Hello World.
Este es el primer artículo de la serie Terraform Essentials, donde aprenderemos a trabajar con esta herramienta de Infrastructure as Code.
Terraform Essentials II: Cómo desplegar Traefik y Wordpress en Docker con soporte para Lets Encrypt [Entendiendo Argumentos]
En este artículo te mostraré una receta de Terraform que nos servirá para desplegar Traefik, NGINX, Wordpress y MariaDB con soporte para Lets Encrypt.
Terraform Essentials III: Cómo hacer nuestra Infraestructura escalable y reproducible gracias a las variables.
En este caso, les voy a hablar de las variables y de como estás pueden ayudarnos a que nuestro código e infraestructura sea escalable y reproducible.

Arrancamos

Los secretos en Terraform son los passwords o los tokens de autenticación, que eventualmente usaremos para los recursos que estamos creando con esta herramienta. Por ejemplo, el usuario y password de una base de datos, de un login o un token para acceder a un s3 de AWS, por ejemplo.

Como todos sabemos, gestionar nuestros secretos es clave para que nuestros sistemas no terminen comprometidos y así como sabemos que no debemos anotar los passwords en un Post IT en un monitor, no deberíamos dejar nuestros Token o contraseñas en los archivos .tf, sobre todo si estos los compartimos con muchas personas en un equipo de trabajo.

¿Entonces? bueno, una de las maneras de gestionar secretos en Terraform es agregandolo como variable de entorno en nuestra terminal y mientras esta no se cierre, va a poder ser usada con Terraform (O cualquier script que sea capaz de tomar esa variable), otra manera puede ser usando las variables alojadas en archivos .tfvars, como vimos en el capítulo anterior, también puede ser como argumento en la línea de comando cuando ejecutamos "terraform apply" y por último, podemos usar Hashicorp Vault.

En este post, vamos a hablar sobre las primeras tres y dejaré para un post aparte la herramienta Vault.

Secretos como variables de entorno

En este pique, hay un ejemplo, claro, de cómo exportando un token de autenticación de DigitalOcean como variable de entorno, podemos ejecutar nuestro plan de Terraform sobre DO sin ingresar ningún valor adicional.

Pero veamos un ejemplo más acá. Supongamos que vamos a desplegar un MariaDB en un contenedor y le pasamos variables a ese contenedor como la creación de un usuario, contraseña del mismo y la contraseña del root. El tf sería más o menos así:

# Contenedor
resource "docker_container" "mariadb" {
  name  = "cduser_mariadb"
  image = "mariadb:10.5.1"

# Especificamos el volumen a montar de mariadb. Siempre es recomendable que quede persistente en un volumen la carpeta /var/lib/mysql.

  volumes {
    container_path  = "/var/lib/mysql"
    read_only = false
    host_path = "/Users/nacho/terraform/files/mariadb/lib"
  }


# Especificamos variables de entorno para crear una base, generar un usuario root, una contraseña para ese usuario, el usuario de base de datos para wordpress y su contraseña.

  env = ["MYSQL_DATABASE=wordpress", "MYSQL_USER=user", "MYSQL_PASSWORD=pass", "MYSQL_ROOT_PASSWORD=password_root"]
  command = ["--default-authentication-plugin=mysql_native_password"]

}

Vamos a especificar unas variables en la terminal, estas mismas se usarán como variables de entorno del contenedor:

"MYSQL_PASSWORD=pass"
"MYSQL_ROOT_PASSWORD=password_root"

Los valores, los vamos a cambiar con el nombre de nuestra variable, para este caso va a quedar así:

"MYSQL_PASSWORD=$password_user"
"MYSQL_ROOT_PASSWORD=$password_root"

Y ahora lo que vamos a hacer es correr en nuestra terminal, lo siguiente:

$ export password_user=esta_es_mi_contraseña
$ export password_root=este-es-mi-password-root

Cuando lo corramos y como "confirmación" de que la acepto, no aparecerá ningún mensaje.

Ahora, hacemos un "terraform apply".

terraform apply

Nos debería devolver esto:

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_container.mariadb will be created
  + resource "docker_container" "mariadb" {
      + attach           = false
      + bridge           = (known after apply)
      + command          = [
          + "--default-authentication-plugin=mysql_native_password",
        ]
      + container_logs   = (known after apply)
      + entrypoint       = (known after apply)
      + env              = [
          + "MYSQL_DATABASE=wordpress",
          + "MYSQL_PASSWORD=$password_user",
          + "MYSQL_ROOT_PASSWORD=$password_root",
          + "MYSQL_USER=user",
        ]
      + exit_code        = (known after apply)
      + gateway          = (known after apply)
      + hostname         = (known after apply)
      + id               = (known after apply)
      + image            = "mariadb:10.5.1"
      + ip_address       = (known after apply)
      + ip_prefix_length = (known after apply)
      + ipc_mode         = (known after apply)
      + log_driver       = "json-file"
      + logs             = false
      + must_run         = true
      + name             = "cduser_mariadb"
      + network_data     = (known after apply)
      + read_only        = false
      + restart          = "no"
      + rm               = false
      + shm_size         = (known after apply)
      + start            = true

      + labels {
          + label = (known after apply)
          + value = (known after apply)
        }

      + volumes {
          + container_path = "/var/lib/mysql"
          + host_path      = "/Users/nacho/terraform/files/mariadb/lib"
          + read_only      = false
        }
    }

Plan: 1 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
docker_container.mariadb: Creating...
docker_container.mariadb: Creation complete after 1s [id=209fe96c48070bcf17def696b27b81a08b8a62c025abb343e3308e7c1ccd570f]

Cómo ven, con este ejemplo, el contenedor esta corriendo, si buscamos en el "tfstate", no veremos nuestra contraseña, también, solo veremos las variables.

Esta es una forma de gestionar los secretos, imaginate que, si hay varias cosas para especificar, lo que podemos hacer es un script que haga la "exportación" hacia el entorno de la terminal y luego, ejecutamos el "terraform apply".

Secretos en el tf.vars

La otra opción es gestionarlos a través de los archivos tf.vars, en donde, en vez de especificarlo como variable en la terminal, lo ponemos en un archivo y luego especificamos la variable en el TF.

Esto, requiere de un paso adicional que es la declaración de la variable en el "root" del archivo .tf

Pueden ver ejemplos concretos de uso en el capítulo anterior:

Terraform Essentials III: Cómo hacer nuestra Infraestructura escalable y reproducible gracias a las variables.
En este caso, les voy a hablar de las variables y de como estás pueden ayudarnos a que nuestro código e infraestructura sea escalable y reproducible.

Variable como argumento en la línea de comando

La tercer opción es pasar la variable mientras ejecutamos el "terraform apply". Esta solución es ideal cuando tenemos un par de variables pero no sirve para cuando tenemos muchas, ya que deberíamos armar una cadena de comandos larguísima.

El otro problema que tiene esta "solución" es que nuestro "secreto" quedará en el historial de bash.

Por último, funcionaria de la misma manera que cuando usamos variables, en el root del archivo ".tf", quedando de esta manera:

variable "password_root" {}
variable "password_user" {}

# Contenedor
resource "docker_container" "mariadb" {
  name  = "cduser_mariadb"
  image = "mariadb:10.5.1"

# Especificamos el volumen a montar de mariadb. Siempre es recomendable que quede persistente en un volumen la carpeta /var/lib/mysql.

  volumes {
    container_path  = "/var/lib/mysql"
    read_only = false
    host_path = "/Users/nacho/terraform/files/mariadb/lib"
  }


# Especificamos variables de entorno para crear una base, generar un usuario root, una contraseña para ese usuario, el usuario de base de datos para wordpress y su contraseña.

  env = ["MYSQL_DATABASE=wordpress", "MYSQL_USER=user", "MYSQL_PASSWORD=$password_user", "MYSQL_ROOT_PASSWORD=$password_root"]
  command = ["--default-authentication-plugin=mysql_native_password"]

}

El terraform apply, debería quedar así:

terraform apply -var 'password_user=esta_es_mi_contraseña' -var 'password_root=este-es-mi-password-root'

Si todo sale bien, tipeamos "yes" y nuestro contenedor debería desplegarse sin ningún problema.

Para ir cerrando

Como viste, hay diferentes maneras de usar secretos en Terraform, si me preguntas, de la manera que lo gestiono yo, es hacer la exportación como variable de entorno y que el Terraform las lea, si son muchos los hosts, las agrupo en un script. Pero creo que la mejor manera es usando Hashicorp Vault, cosa, que hablaremos en otro artículo.

¿Te sirvió? ¿Conoces otra manera? Compartela en los comentarios. Buen fin de semana.