Cómo crear un Vault Server de Hashicorp usando Packer y Terraform en DigitalOcean

El autor seleccionó la Free and Open Source Fund para recibir una donación como parte del programa Write for DOnations.

Introducción

Vault, de Hashicorp, es una herramienta de código abierto que permite almacenar de forma segura datos secretos y sensibles en entornos dinámicos en la nube. Ofrece un cifrado de datos seguro, acceso basado en identidad usando políticas personalizadas, y arrendamiento y revocación de secretos, además de un registro de auditoría detallado que se asienta en todo momento. Vault también cuenta con una API HTTP, lo que lo convierte en la opción ideal para almacenar credenciales en implementaciones diseminadas orientadas al servicio, como Kubernetes.

Packer y Terraform, también desarrollados por Hashicorp, pueden usarse juntos para crear e implementar imágenes de Vault. En este flujo de trabajo, los desarrolladores pueden usar Packer para escribir imágenes inmutables para diferentes plataformas desde un único archivo de configuración, que especifica lo que la imagen debería contener. Posteriormente, Terraform implementa tantas instancias personalizadas de las imágenes creadas como sea necesario.

En este tutorial, usará Packer para crear una instantánea inmutable del sistema con Vault instalado y orquestará su implementación usando Terraform. Al final, tendrá un sistema automatizado para implementar Vault, lo que le permitirá centrarse en trabajar con Vault y no en el proceso de instalación y aprovisionamiento subyacente.

Requisitos previos

  • Packer instalado en su máquina local. Para acceder a instrucciones, consulte la documentación oficial.
  • Terraform instalado en su computadora local. Consulte la documentación oficial para obtener ayuda.
  • Un token de acceso personal (clave de API) con permisos de lectura y escritura para su cuenta de DigitalOcean. Para aprender a crear uno, consulte Cómo crear un token de acceso personal en los documentos.
  • Una clave SSH que usará para la autenticación con los Droplets de Vault implementados, disponible en su equipo local y añadida a su cuenta de DigitalOcean. También necesitará su huella; podrá copiarla de la página Seguridad de su cuenta una vez que la haya añadido. Consulte la documentación de DigitalOcean para hallar instrucciones detalladas o el tutorial Cómo configurar claves SSH.

Paso 1: Crear una plantilla de Packer

En este paso, escribirá un archivo de configuración de Packer, llamado template, que indicará a Packer cómo crear una imagen que tenga Vault previamente instalado. Escribirá la configuración en formato JSON, un formato de archivo de configuración legible para humanos que se usa comúnmente.

A los efectos de este tutorial, almacenará todos los archivos en ~/vault-orchestration. Cree el directorio ejecutando el siguiente comando:

  • mkdir ~/vault-orchestration

Diríjase a este:

  • cd ~/vault-orchestration

Almacenará los archivos de configuración para Packer y Terraform por separado, en subdirectorios diferentes. Créelos usando el siguiente comando:

  • mkdir packer terraform

Debido a que primero trabajará con Packer, diríjase al directorio de este:

  • cd packer

Usar variables de plantilla

Almacenar datos privados y secretos de la aplicación en un archivo de variables independiente es una excelente alternativa para mantenerlos fuera de su plantilla. Cuando se cree la imagen, Packer sustituirá las variables referenciadas por sus valores. Codificar de forma rígida valores secretos en su plantilla es un riesgo de seguridad, en particular si se compartirá con miembros del equipo o se dispondrá en sitios públicos, como GitHub.

Los almacenará en el subdirectorio packer, en un archivo llamado variables.json. Créelo usando su editor de texto favorito:

  • nano variables.json

Añada las siguientes líneas:

~/vault-orchestration/packer/variables.json

{     "do_token": "your_do_api_key",     "base_system_image": "ubuntu-18-04-x64",     "region": "nyc3",     "size": "s-1vcpu-1gb" } 

El archivo de variables consiste en un diccionario JSON, que asigna los nombres de variables a sus valores. Usará estas variables en la plantilla que está a punto de crear. Si lo desea, puede editar los valores básicos de imagen, región y tamaño del Droplet conforme a los documentos para desarrolladores.

Recuerde sustituir your_do_api_key por la clave de API que creó como parte de los requisitos previos, y luego guarde y cierre el archivo.

Crear compiladores y aprovisionadores

Una vez que esté listo el archivo de variables, creará la plantilla de Packer.

Guardará la plantilla de Packer para Vault en un archivo llamado template.json. Créelo usando su editor de texto:

  • nano template.json

Añada las siguientes líneas:

~/vault-orchestration/packer/template.json

{      "builders": [{          "type": "digitalocean",          "api_token": "{{user `do_token`}}",          "image": "{{user `base_system_image`}}",          "region": "{{user `region`}}",          "size": "{{user `size`}}",          "ssh_username": "root"      }],      "provisioners": [{          "type": "shell",          "inline": [              "sleep 30",              "sudo apt-get update",              "sudo apt-get install unzip -y",              "curl -L https://releases.hashicorp.com/vault/1.3.2/vault_1.3.2_linux_amd64.zip -o vault.zip",              "unzip vault.zip",              "sudo chown root:root vault",              "mv vault /usr/local/bin/",              "rm -f vault.zip"          ]     }] } 

En la plantilla, se definen conjuntos de compiladores y aprovisionadores. Los compiladores indican a Packer la manera de compilar la imagen del sistema (según su tipo) y el espacio en el que se almacenará, mientras que los aprovisionadores contienen conjuntos de acciones que Packer debería realizar en el sistema antes de convertirlo en una imagen inmutable; por ejemplo, instalar o configurar software. Sin aprovisionadores, obtendría una imagen de sistema básica intacta. Tanto los compiladores como los aprovisionadores exponen parámetros para una personalización del flujo de trabajo más completa.

Primero se define un compilador individual del tipo digitalocean; esto significa que, cuando se le ordene crear una imagen, Packer usará los parámetros proporcionados para crear un Droplet temporal del tamaño definido usando la clave de API proporcionada, con la imagen del sistema básica y en la región especificada. El formato para obtener una variable es {{user 'variable_name'}}, donde la parte resaltada es su nombre.

Cuando se proporcione el Droplet temporal, el proveedor establecerá conexión con él usando SSH con el nombre de usuario especificado y de forma secuencial ejecutará todos los proveedores definidos antes de crear una instantánea de DigitalOcean desde el Droplet y eliminarlo.

Es del tipo shell, que ejecutará los comando determinados en el destino. Los comandos pueden especificarse inline, como un conjunto de cadenas, o definirse en archivos de secuencias de comandos independientes si su inserción en la plantilla dificulta la manipulación debido al tamaño. Los comandos de la plantilla esperarán 30 segundos para que el sistema arranque, y luego descargarán y desempaquetarán Vault 1.3.2. Consulte la página oficial de descarga de Vault y sustituya el enlace de los comandos por una versión más reciente de Linux, si está disponible.

Cuando termine, guarde y cierre el archivo.

Para verificar la validez de su plantilla, ejecute el siguiente comando:

  • packer validate -var-file=variables.json template.json

Packer acepta una ruta al archivo de variables a través del argumento -var-file.

Verá el siguiente resultado:

OutputTemplate validated successfully. 

Si ve un error, Packer especificará con exactitud dónde se produjo para que pueda corregirlo.

Ahora tiene una plantilla en funcionamiento que produce una imagen con Vault instalado, con su clave de API y otros parámetros definidos en un archivo independiente. Con esto, estará listo para invocar Packer y crear la instantánea.

Paso 2: Crear la instantánea

En este paso, creará una instantánea de DigitalOcean desde su plantilla usando el comando build de Packer.

Para crear su instantánea, ejecute el siguiente comando:

  • packer build -var-file=variables.json template.json

Este comando tardará un tiempo en ejecutarse. Verá muchos resultados que tendrán un aspecto similar a este:

Outputdigitalocean: output will be in this color.  ==> digitalocean: Creating temporary ssh key for droplet... ==> digitalocean: Creating droplet... ==> digitalocean: Waiting for droplet to become active... ==> digitalocean: Using ssh communicator to connect: ... ==> digitalocean: Waiting for SSH to become available... ==> digitalocean: Connected to SSH! ==> digitalocean: Provisioning with shell script: /tmp/packer-shell035430322 ... ==> digitalocean:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current ==> digitalocean:                                  Dload  Upload   Total   Spent    Left  Speed     digitalocean: Archive:  vault.zip ==> digitalocean: 100 45.5M  100 45.5M    0     0   154M      0 --:--:-- --:--:-- --:--:--  153M     digitalocean:   inflating: vault ==> digitalocean: Gracefully shutting down droplet... ==> digitalocean: Creating snapshot: packer-1581537927 ==> digitalocean: Waiting for snapshot to complete... ==> digitalocean: Destroying droplet... ==> digitalocean: Deleting temporary ssh key... Build 'digitalocean' finished.  ==> Builds finished. The artifacts of successful builds are: --> digitalocean: A snapshot was created: 'packer-1581537927' (ID: 58230938) in regions '...' 

Packer registra todos los pasos que realizó al crear su plantilla. La última línea contiene el nombre de la instantánea (como packer-1581537927) y su ID entre paréntesis marcado en rojo. Anote el ID de la instantánea, lo necesitará en el paso siguiente.

Si el proceso de creación falla debido a errores de API, espere unos minutos y realice un nuevo intento.

Creó una instantánea de DigitalOcean conforme a su plantilla. La instantánea tiene Vault previamente instalado, y ahora puede implementar Droplets con él como su imagen de sistema. En el siguiente paso, escribirá la configuración de Terraform para automatizar dichas implementaciones.

Paso 3: Escribir la configuración de Terraform

En este paso, escribirá la configuración de Terraform para automatizar implementaciones de Droplets de la instantánea que contiene el Vault que acaba de crear usando Packer.

Antes de escribir la configuración real de Terraform para implementar Vault desde la instantánea creada previamente, primero deberá configurar el proveedor de DigitalOcean. Diríjase al subdirectorio terraform ejecutando lo siguiente:

  • cd ~/vault-orchestration/terraform

A continuación, cree un archivo llamado do-provider.tf en el que almacenará el proveedor:

  • nano do-provider.tf

Añada las siguientes líneas:

~/vault-orchestration/terraform/do-provider.tf

variable "do_token" { }  variable "ssh_fingerprint" { }  variable "instance_count" {   default = "1" }  variable "do_snapshot_id" { }  variable "do_name" {   default = "vault" }  variable "do_region" { }  variable "do_size" { }  variable "do_private_networking" {   default = true }  provider "digitalocean" {   token = var.do_token } 

Este archivo declara las variables del parámetro y proporciona al proveedor digitalocean una clave de API. Posteriormente, usará estas variables en su plantilla de Terraform, pero primero deberá especificar sus valores. Para ello, Terraform admite la especificación de valores de variables en un archivo de definiciones de variables de forma similar a Packer. El nombre del archivo debe terminar en .tfvars o .tfvars.jason. Luego, pasará ese archivo a Terraform usando el argumento –var-file.

Guarde y cierre el archivo.

Cree un archivo de definiciones de variables llamado definitions.tfvars usando su editor de texto:

  • nano definitions.tfvars

Añada las siguientes líneas:

~/vault-orchestration/terraform/definitions.tf

do_token         = "your_do_api_key" ssh_fingerprint  = "your_ssh_key_fingerprint" do_snapshot_id   = your_do_snapshot_id do_name          = "vault" do_region        = "nyc3" do_size          = "s-1vcpu-1gb" instance_count   = 1 

Recuerde sustituir your_do_api_key, your_ssh_key_fingerprint y your_do_snapshot_id por la clave de API de su cuenta, la huella de su clave SSH y el ID de la instantánea que anotó en el paso anterior, respectivamente. Los parámetros do_region y do_size deben tener los mismos valores que en el archivo de variables de Packer. Si desea implementar varias instancias de una vez, ajuste instance_count según su valor deseado.

Cuando termine, guarde y cierre el archivo.

Para obtener más información sobre el proveedor de Terraform de DigitalOcean, consulte los documentos oficiales.

Guardará la configuración de la implementación de la instantánea de Vault en un archivo llamado deployment.tf, en el directorio terraform. Créelo usando su editor de texto:

  • nano deployment.tf

Añada las siguientes líneas:

~/vault-orchestration/terraform/deployment.tf

resource "digitalocean_droplet" "vault" {   count              = var.instance_count   image              = var.do_snapshot_id   name               = var.do_name   region             = var.do_region   size               = var.do_size   private_networking = var.do_private_networking   ssh_keys = [     var.ssh_fingerprint   ] }  output "instance_ip_addr" {   value = {     for instance in digitalocean_droplet.vault:     instance.id => instance.ipv4_address   }   description = "The IP addresses of the deployed instances, paired with their IDs." } 

Aquí, define un único recurso del tipo digitalocean_droplet llamado vault. A continuación, establecerá sus parámetros según los valores de las variables y añadirá una clave SSH (usando la huella de esta) de su cuenta de DigitalOcean al recurso del Droplet. Finalmente, usará output para mostrar las direcciones IP de todas las instancias recién implementadas en la consola.

Guarde y cierre el archivo.

Antes de aplicar cualquier otra acción a la configuración de su implementación, deberá inicializar el directorio como proyecto de Terraform:

  • terraform init

Verá el siguiente resultado:

Output Initializing the backend...  Initializing provider plugins...  The following providers do not have any version constraints in configuration, so the latest version was installed.  To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below.  * provider.digitalocean: version = "~> 1.14"  Terraform has been successfully initialized!  You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work.  If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. 

Cuando se inicia un directorio como un proyecto, Terraform lee los archivos de configuración disponibles y los complementos de descargas considerados necesarios, según lo que se registre en el resultado.

Ahora cuenta con la configuración de Terraform para implementar su instantánea de Vault. Podrá proceder a validarla e implementarla en un Droplet.

Paso 4: Implementar Vault usando Terraform

En esta sección, verificará su configuración de Terraform usando el comando validate. Una vez que se verifique correctamente, utilizará apply en ella e implementará un Droplet como resultado.

Ejecute el siguiente comando para probar la validez de su configuración:

  • terraform validate

Verá el siguiente resultado:

OutputSuccess! The configuration is valid. 

A continuación, ejecute el comando plan para ver qué intentará hacer Terraform cuando se aprovisione la infraestructura según su configuración:

  • terraform plan -var-file="definitions.tfvars"

Terraform acepta un archivo de definiciones de variables a través del parámetro -var-file.

El resultado será similar a este:

OutputRefreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.   ------------------------------------------------------------------------  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:    # digitalocean_droplet.vault[0] will be created   + resource "digitalocean_droplet" "vault" {         ...     }  Plan: 1 to add, 0 to change, 0 to destroy.  ------------------------------------------------------------------------  Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run. 

El signo + verde al principio de la línea resource "digitalocean_droplet" "vault" implica que Terraform creará un nuevo Droplet llamado vault y usará los parámetros que siguen. Esto es correcto; por ello, ahora podrá implementar el plan ejecutando terraform apply:

  • terraform apply -var-file="definitions.tfvars"

Introduzca yes cuando se solicite. Después de unos minutos, el Droplet finalizará el aprovisionamiento y verá un resultado similar a este:

OutputAn execution plan has been generated and is shown below. Resource actions are indicated with the following symbols:   + create  Terraform will perform the following actions:    + digitalocean_droplet.vault-droplet  ...  Plan: 1 to add, 0 to change, 0 to destroy.  ...  digitalocean_droplet.vault-droplet: Creating...  ...  Apply complete! Resources: 1 added, 0 changed, 0 destroyed.  Outputs:  instance_ip_addr = {   "181254240" = "your_new_server_ip" } 

En el resultado, Terraform registra las acciones que realizó (en este caso, para crear un Droplet) y muestra su dirección IP pública al final. La usará para establecer conexión con su nuevo Droplet en el siguiente paso.

Creó un nuevo Droplet a partir de la instantánea que contiene Vault y ahora está listo para verificarlo.

Paso 5: Verificar su Droplet implementado

En este paso, accederá a su nuevo Droplet usando SSH y verificará que Vault se haya instalado correctamente.

En equipos con Windows, puede usar un software como Kitty o Putty para establecer conexión con el Droplet con una clave SSH.

En equipos con Linux o macOS, puede usar el comando ssh disponible para la conexión.

Responda yes cuando se solicite. Una vez que acceda, inicie Vault ejecutanto lo siguiente:

  • vault

Verá el resultado de “ayuda”, que tiene este aspecto:

OutputUsage: vault <command> [args]  Common commands:     read        Read data and retrieves secrets     write       Write data, configuration, and secrets     delete      Delete secrets and configuration     list        List data or secrets     login       Authenticate locally     agent       Start a Vault agent     server      Start a Vault server     status      Print seal and HA status     unwrap      Unwrap a wrapped secret  Other commands:     audit          Interact with audit devices     auth           Interact with auth methods     debug          Runs the debug command     kv             Interact with Vault's Key-Value storage     lease          Interact with leases     namespace      Interact with namespaces     operator       Perform operator-specific tasks     path-help      Retrieve API help for paths     plugin         Interact with Vault plugins and catalog     policy         Interact with policies     print          Prints runtime configurations     secrets        Interact with secrets engines     ssh            Initiate an SSH session     token          Interact with tokens 

Puede cerrar la conexión escribiendo exit.

De esta manera, habrá verificado verificó que su Droplet recién implementado se creó a partir de la instantánea que realizó y que Vault se instaló correctamente.

Conclusión

Ahora dispone de un sistema automatizado para implementar Hashicorp Vault en Droplets de DigitalOcean usando Terraform y Packer. Ahora puede implementar tantos servidores Vault como necesite. Para comenzar a usar Vault, deberá iniciarlo y realizar algunas configuraciones adicionales. Para hallar instrucciones sobre cómo hacerlo, consulte la documentación oficial.

Para hallar más tutoriales sobre Terraform, consulte nuestra página de contenido de Terraform.