tutoriales.com

Asegurando tus Pipelines CI/CD con Secretos Dinámicos en HashiCorp Vault

Este tutorial te guiará a través de la implementación de secretos dinámicos en HashiCorp Vault para proteger tus pipelines CI/CD. Descubre cómo eliminar secretos estáticos y reducir la superficie de ataque, mejorando significativamente la seguridad de tus procesos de entrega continua. Ideal para equipos DevOps que buscan fortalecer su postura de seguridad.

Intermedio20 min de lectura14 views
Reportar error

La seguridad en los entornos CI/CD es una preocupación creciente para las organizaciones. Los secretos, como claves API, credenciales de bases de datos y tokens, a menudo se gestionan de forma insegura, almacenados en variables de entorno, archivos de configuración o, peor aún, directamente en el código fuente. Esta práctica no solo aumenta el riesgo de brechas de seguridad, sino que también dificulta la rotación de credenciales y el cumplimiento de políticas de seguridad.

HashiCorp Vault es una herramienta de gestión de secretos que permite almacenar, acceder y distribuir de forma segura los secretos. Una de sus características más poderosas son los secretos dinámicos, que generan credenciales bajo demanda con un tiempo de vida limitado (TTL). Esto elimina la necesidad de secretos estáticos y reduce drásticamente la superficie de ataque, ya que las credenciales son efímeras y se revocan automáticamente.

En este tutorial, exploraremos cómo integrar HashiCorp Vault con tus pipelines CI/CD para utilizar secretos dinámicos. Cubriremos la configuración de Vault, la creación de políticas, la configuración de motores de secretos dinámicos y, finalmente, cómo un pipeline puede solicitar y utilizar estas credenciales temporales.


🎯 ¿Por qué usar Secretos Dinámicos en CI/CD?

La gestión tradicional de secretos a menudo implica:

  • Secretos estáticos: Credenciales de larga duración que son un objetivo atractivo para los atacantes.
  • Almacenamiento inseguro: Secretos guardados en repositorios de código, sistemas de archivos o variables de entorno expuestas.
  • Rotación manual: Proceso propenso a errores y rara vez realizado con la frecuencia necesaria.
  • Falta de auditoría: Dificultad para saber quién accedió a qué secreto y cuándo.

Los secretos dinámicos de Vault abordan estos problemas de frente:

  • Credenciales efímeras: Se generan solo cuando se necesitan y expiran automáticamente.
  • Auditoría completa: Vault registra cada acceso a un secreto, proporcionando un rastro de auditoría claro.
  • Rotación automática: Las credenciales son de un solo uso o tienen una vida útil corta, lo que equivale a una rotación constante.
  • Principios de mínimo privilegio: Cada aplicación o etapa del pipeline solo obtiene las credenciales que necesita, con los permisos mínimos necesarios.
💡 Consejo: Adoptar secretos dinámicos es un pilar fundamental en la implementación de una estrategia de seguridad de "confianza cero" (Zero Trust) en tus operaciones DevOps.

🛠️ Prerrequisitos

Para seguir este tutorial, necesitarás lo siguiente:

  • HashiCorp Vault: Una instancia de Vault en funcionamiento. Puedes instalarla localmente para pruebas o usar una instancia en la nube. Necesitarás permisos de administrador para configurarlo inicialmente.
  • Docker (opcional): Para ejecutar Vault en un contenedor localmente.
  • Un sistema CI/CD: Como Jenkins, GitLab CI, GitHub Actions, CircleCI, etc. Aunque los ejemplos se centrarán en la lógica de Vault, el concepto es aplicable a cualquier plataforma.
  • Conocimientos básicos de Vault: Familiaridad con conceptos como motores de secretos, políticas y tokens.
  • Cliente Vault CLI: Para interactuar con Vault desde la línea de comandos.

🚀 Instalación y Configuración Básica de Vault (Local)

Si no tienes Vault instalado, puedes usar Docker para una configuración rápida:

docker run --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=myroottoken' -p 8200:8200 --name dev-vault vault:latest

Esto iniciará Vault en modo de desarrollo, con un token raíz myroottoken y escuchando en http://127.0.0.1:8200.

Exporta la variable de entorno para la CLI:

export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='myroottoken'

Para verificar que Vault está funcionando:

vault status

Deberías ver una salida similar a esta:

Key                      Value
---                      -----
Seal Type                shamir
Initialized              true
Sealed                   false
Total Shares             1
Threshold                1
Version                  1.15.4
Build Date               2024-02-14T11:47:04Z
Storage Type             inmem
Cluster Name             vault-cluster-34b92d6e
Cluster ID               93504d6e-981f-f131-01ff-1ddff024471f
HA Enabled               false

🔐 Configurando un Motor de Secretos Dinámicos: AWS

Como ejemplo, configuraremos el motor de secretos de AWS para generar credenciales de IAM temporales. Este es un caso de uso común en pipelines CI/CD que necesitan interactuar con servicios de AWS.

Paso 1: Habilitar el Motor de Secretos AWS

Primero, habilita el motor de secretos AWS en una ruta específica (por ejemplo, aws-ci-cd/):

vault secrets enable aws

Paso 2: Configurar las Credenciales Raíz de AWS

Vault necesita credenciales de AWS con permisos para generar otras credenciales. Estas credenciales son estáticas y deben ser gestionadas con el máximo cuidado (idealmente, usando un rol de IAM para Vault mismo si se ejecuta en AWS, o mediante un método de entrada seguro).

En este ejemplo, usaremos un access_key_id y secret_access_key que tienen permisos para crear y gestionar roles y políticas de IAM. En un entorno de producción, nunca usarías las credenciales de un usuario IAM con permisos administrativos. En su lugar, asignarías un rol de IAM a la instancia o contenedor de Vault que le otorgue los permisos mínimos necesarios para crear y rotar credenciales para otros servicios.

vault write aws/config/root \
    access_key="YOUR_AWS_ACCESS_KEY_ID" \
    secret_key="YOUR_AWS_SECRET_ACCESS_KEY" \
    region="us-east-1"

Reemplaza YOUR_AWS_ACCESS_KEY_ID y YOUR_AWS_SECRET_ACCESS_KEY con tus propias credenciales. Asegúrate de que estas credenciales tengan los permisos necesarios en AWS (por ejemplo, iam:CreateUser, iam:PutUserPolicy, iam:DeleteUser, iam:DeleteUserPolicy).

Paso 3: Definir un Rol en Vault para AWS

Un rol en Vault define el tipo de credenciales que se pueden generar y los permisos asociados. Aquí crearemos un rol llamado ci-cd-user que generará usuarios de IAM con una política específica.

Creamos una política JSON de IAM que permite acceso de solo lectura a buckets S3. Guarda esto en un archivo, por ejemplo, s3-readonly-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:Get*",
        "s3:List*"
      ],
      "Resource": "*"
    }
  ]
}

Luego, configura el rol en Vault, especificando la política de IAM y un TTL (Time To Live) para las credenciales generadas. El default_ttl es el tiempo de vida predeterminado, y el max_ttl es el tiempo de vida máximo que una credencial puede tener, incluso si se solicita un TTL más largo.

vault write aws/roles/ci-cd-user \
    credential_type="iam_user" \
    policy_document=@s3-readonly-policy.json \
    default_ttl="1h" \
    max_ttl="2h"

En este ejemplo, las credenciales de ci-cd-user serán un usuario IAM nuevo, con la política s3-readonly-policy, y durarán 1 hora por defecto (máximo 2 horas).

⚠️ Advertencia: El `policy_document` debe ser tan restrictivo como sea posible. Sigue el principio del mínimo privilegio.

📄 Creando Políticas de Acceso en Vault para tu Pipeline

Para que tu pipeline CI/CD pueda solicitar secretos dinámicos, necesita tener un token de Vault con los permisos adecuados. Estos permisos se definen mediante políticas en Vault.

Crea un archivo de política llamado ci-cd-policy.hcl:

path "aws/creds/ci-cd-user" {
  capabilities = ["read"]
}

# Opcional: Permitir al pipeline leer secretos estáticos si es necesario
# path "secret/data/my-static-secret" {
#   capabilities = ["read"]
# }

Esta política permite leer credenciales generadas por el rol ci-cd-user del motor de secretos aws/. Guarda esta política en Vault:

vault policy write ci-cd-policy ci-cd-policy.hcl

🔑 Autenticación del Pipeline en Vault

Para que tu pipeline pueda obtener un token de Vault con la política ci-cd-policy, necesitas configurar un método de autenticación. Los métodos de autenticación comunes para CI/CD incluyen:

  • AppRole: Ideal para máquinas y aplicaciones. Vault genera un Role ID y Secret ID que el pipeline usa para autenticarse.
  • Kubernetes: Si tu pipeline se ejecuta en Kubernetes, puedes usar la autenticación de Kubernetes de Vault, que mapea Service Accounts a roles de Vault.
  • JWT/OIDC: Para integraciones con proveedores de identidad que emiten tokens JWT.

Usaremos AppRole como ejemplo, ya que es genérico y aplicable a la mayoría de sistemas CI/CD.

Paso 1: Habilitar el Método de Autenticación AppRole

vault auth enable approle

Paso 2: Crear un Rol de AppRole para CI/CD

Este rol de AppRole asociará la política ci-cd-policy al pipeline cuando se autentique.

vault write auth/approle/role/my-ci-cd-app \
    token_policies="ci-cd-policy" \
    token_ttl="5m" \
    token_max_ttl="15m"

Aquí, token_ttl y token_max_ttl definen la vida útil del token de Vault que el AppRole generará para tu pipeline. Mantén estos valores bajos para mayor seguridad.

Paso 3: Obtener el Role ID y un Secret ID

El Role ID es público y se asocia al rol AppRole. El Secret ID es una credencial secreta que se genera una vez y se usa junto con el Role ID para autenticarse.

Obtener el Role ID:

vault read auth/approle/role/my-ci-cd-app/role-id

Salida (ejemplo):

Key        Value
---        -----
role_id    c1a1b2c2-d3e4-f5a6-7b8c-9d0e1f2a3b4c

Generar un Secret ID (este es el secreto que tu pipeline usará para autenticarse en Vault):

vault write -f auth/approle/role/my-ci-cd-app/secret-id

Salida (ejemplo):

Key                     Value
---                     -----
secret_id               abcde12345fghij67890klmnoPQRSTUVxyz
secret_id_accessor      87654321-fedc-ba98-7654-3210fedcba98
secret_id_num_uses      0
secret_id_ttl           0s

Guarda estos dos valores (Role ID y Secret ID) de forma segura en tu sistema CI/CD. El Secret ID debe tratarse como un secreto de alta sensibilidad, almacenado como una variable de entorno secreta en tu sistema CI/CD, no directamente en el repositorio de código.


🚀 Integración en tu Pipeline CI/CD

Ahora que Vault está configurado, integremos la obtención de secretos dinámicos en un pipeline. La lógica general es:

  1. Configurar VAULT_ADDR: La dirección de tu servidor Vault.
  2. Autenticarse en Vault: Usar el Role ID y Secret ID para obtener un token de Vault.
  3. Solicitar credenciales dinámicas: Usar el token de Vault para leer las credenciales dinámicas de AWS.
  4. Usar las credenciales: Exportarlas como variables de entorno o pasarlas directamente al comando/script que las necesite.
  5. Revocar el token (opcional pero recomendado): Asegurarse de que el token de Vault y los secretos dinámicos sean revocados al final del trabajo, aunque Vault los revocará automáticamente después de su TTL.

Aquí hay un ejemplo conceptual usando un script bash que podría ejecutarse en cualquier sistema CI/CD. Adaptar esto a GitLab CI, GitHub Actions o Jenkins sería cuestión de envolver estos comandos en su sintaxis específica.

Script del Pipeline (Ejemplo genérico)

#!/bin/bash

# 1. Configurar la dirección de Vault (debe ser accesible desde el runner del pipeline)
export VAULT_ADDR="http://127.0.0.1:8200"

# 2. Variables de entorno para AppRole (SECRETO)
# Estas variables deben ser inyectadas de forma segura por tu sistema CI/CD
# Por ejemplo: en GitHub Actions como secretos, en Jenkins como credenciales, etc.
# export VAULT_ROLE_ID="c1a1b2c2-d3e4-f5a6-7b8c-9d0e1f2a3b4c" # Reemplaza con tu Role ID
# export VAULT_SECRET_ID="abcde12345fghij67890klmnoPQRSTUVxyz" # Reemplaza con tu Secret ID

# Verificar que las variables están disponibles
if [ -z "$VAULT_ROLE_ID" ] || [ -z "$VAULT_SECRET_ID" ]; then
  echo "Error: VAULT_ROLE_ID o VAULT_SECRET_ID no están configurados."
  exit 1
fi

# 3. Autenticarse en Vault usando AppRole para obtener un token de cliente
echo "Autenticando en Vault con AppRole..."
VAULT_RESPONSE=$(vault write -field=client_token auth/approle/login \
    role_id="$VAULT_ROLE_ID" \
    secret_id="$VAULT_SECRET_ID")

if [ -z "$VAULT_RESPONSE" ]; then
  echo "Error al obtener el token de Vault."
  exit 1
fi

export VAULT_TOKEN="$VAULT_RESPONSE"

echo "Token de Vault obtenido exitosamente."

# 4. Solicitar credenciales dinámicas de AWS
echo "Solicitando credenciales dinámicas de AWS..."
AWS_CREDS=$(vault read -json aws/creds/ci-cd-user)

if [ $? -ne 0 ]; then
  echo "Error al leer credenciales de AWS de Vault."
  exit 1
fi

AWS_ACCESS_KEY_ID=$(echo "$AWS_CREDS" | jq -r '.data.access_key')
AWS_SECRET_ACCESS_KEY=$(echo "$AWS_CREDS" | jq -r '.data.secret_key')
AWS_SESSION_TOKEN=$(echo "$AWS_CREDS" | jq -r '.data.security_token')

# Exportar las credenciales como variables de entorno
# ESTO ES CRÍTICO: Asegúrate de que tu sistema CI/CD enmascare estos valores en los logs.
export AWS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY
export AWS_SESSION_TOKEN

echo "Credenciales de AWS obtenidas y exportadas como variables de entorno."

# 5. Ejecutar la tarea CI/CD que requiere acceso a AWS
# Ejemplo: Listar buckets S3 (la política 's3-readonly-policy' lo permite)
echo "Ejecutando tarea que usa credenciales de AWS..."
aws s3 ls

# 6. (Opcional pero recomendado) Revocar el token de Vault al final del trabajo
# Esto asegura que el token se invalida inmediatamente si no se necesita más.
# La revocación automática por TTL de Vault también se encargará de esto.
# echo "Revocando token de Vault..."
# vault token revoke "$VAULT_TOKEN"
# echo "Token de Vault revocado."

# Limpiar variables de entorno sensibles
unset VAULT_TOKEN
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN

echo "Pipeline terminado. Credenciales efímeras usadas y limpiadas."
🔥 Importante: Asegúrate de que el comando `jq` esté instalado en el entorno de tu pipeline si usas el ejemplo. Para la gestión de secretos, es fundamental que las credenciales no queden expuestas en los logs del pipeline. La mayoría de los sistemas CI/CD tienen mecanismos para enmascarar variables de entorno secretas.

Adaptación a Plataformas Específicas

  • GitHub Actions: Almacena VAULT_ROLE_ID y VAULT_SECRET_ID como secretos de GitHub. Usa los env de los pasos para pasar estas variables. El comando vault login y vault read se ejecutarían en un run script.
  • GitLab CI: Usa las variables protegidas/enmascaradas para VAULT_ROLE_ID y VAULT_SECRET_ID. Puedes usar before_script para obtener el token de Vault y las credenciales de AWS.
  • Jenkins: Utiliza el plugin de Jenkins para HashiCorp Vault o inyecta las credenciales como secretos usando el plugin Credentials Binding para VAULT_ROLE_ID y VAULT_SECRET_ID y luego ejecuta los comandos vault en un paso de shell.

🔍 Verificando la Rotación de Secretos

Una vez que el pipeline haya ejecutado y generado credenciales, puedes verificar en Vault que el arrendamiento (lease) de esas credenciales se creó y, eventualmente, expiró o fue revocado. Si iniciaste Vault en modo dev, puedes ver los leases activos:

vault lease list aws/creds/ci-cd-user

Verás una entrada similar a:

Key                                                     ExpireTime
---                                                     ----------
aws/creds/ci-cd-user/b8c0a1d2-e3f4-5a6b-7c8d-9e0f1a2b3c4d  2024-03-08T15:30:00Z

Si esperas el TTL configurado, verás que la entrada desaparece. Cada vez que el pipeline solicite credenciales, se generará una nueva entrada única, asegurando la rotación constante.

INICIO: Pipeline Job 1. Autenticación AppRole (Role ID + Secret ID) 2. Obtiene Client Token Token temporal de Vault 3. Solicita credenciales AWS Petición a Vault AWS Engine 4. Vault genera IAM efímero Access Key / Secret Key dinámica 5. Pipeline usa credenciales Operaciones en Infraestructura AWS FIN: Expiración/Revocación TTL cumplido, credenciales inactivas

💡 Buenas Prácticas y Consideraciones Avanzadas

  • Principio del Mínimo Privilegio: Siempre otorga a Vault (y a las credenciales generadas por Vault) solo los permisos mínimos necesarios.
  • TTL Cortos: Configura default_ttl y max_ttl lo más cortos posible para los secretos dinámicos y los tokens de Vault. Esto minimiza el impacto de una credencial comprometida.
  • Seguridad del Secret ID de AppRole: El Secret ID es tan sensible como cualquier otra credencial. Protéjelo con los mecanismos de secretos de tu sistema CI/CD y rótalo regularmente (Vault puede generarlo bajo demanda o tiene opciones para secret_id_num_uses).
  • Auditoría de Vault: Configura los audit devices de Vault para registrar todas las solicitudes y respuestas. Esto es crucial para el cumplimiento y la detección de incidentes.
  • Rotación de Credenciales Raíz de Vault: Las credenciales que Vault usa para interactuar con AWS (en el ejemplo, aws/config/root) deben ser rotadas periódicamente. Idealmente, Vault se autenticaría con AWS utilizando roles de instancia/servicio, eliminando la necesidad de secretos estáticos para la configuración raíz.
  • Considera los métodos de autenticación de Vault: AppRole es un buen punto de partida, pero para entornos en Kubernetes, la autenticación de Kubernetes de Vault es a menudo más idiomática y segura.
  • Vault Agent: Para aplicaciones que se ejecutan durante un tiempo prolongado, el Vault Agent puede simplificar la gestión de tokens y la rotación de secretos dinámicos, proporcionando un agente local que renueva tokens y actualiza secretos automáticamente.
  • Error Handling y Retry: Implementa mecanismos robustos de manejo de errores y reintentos en tus scripts de pipeline para la interacción con Vault, ya que la red o Vault mismo pueden experimentar interrupciones temporales.
Preguntas Frecuentes (FAQ)

P: ¿Qué pasa si el pipeline falla antes de usar las credenciales?

R: Las credenciales dinámicas aún tendrán su TTL. Si el token de Vault que las solicitó no se revoca manualmente (lo cual es opcional), también expirará por su TTL. En cualquier caso, las credenciales se auto-revocarán eventualmente, limitando el riesgo.

P: ¿Puedo usar esto con otras plataformas además de AWS?

R: ¡Absolutamente! Vault tiene motores de secretos dinámicos para una gran variedad de plataformas, incluyendo bases de datos (MySQL, PostgreSQL, MSSQL, Oracle), Azure, GCP, Kubernetes, SSH, etc. La configuración es similar, adaptando los comandos al motor específico.

P: ¿Es seguro almacenar el Secret ID en mi sistema CI/CD?

R: Sí, siempre y cuando se almacene como una variable secreta cifrada, no en texto plano en el repositorio. Los sistemas CI/CD están diseñados para manejar este tipo de secretos de forma segura. Sin embargo, dado que el Secret ID es una credencial de larga duración, algunos prefieren rotarlo periódicamente o usar métodos de autenticación sin secretos como la autenticación de Kubernetes o JWT/OIDC si están disponibles.


✅ Conclusión

La implementación de secretos dinámicos con HashiCorp Vault en tus pipelines CI/CD es un paso crucial para fortalecer la postura de seguridad de tu organización. Al eliminar los secretos estáticos, reducir la superficie de ataque y auditar cada acceso, no solo proteges tus sistemas, sino que también mejoras la agilidad y el cumplimiento.

Este tutorial te ha proporcionado una guía completa para configurar Vault, definir roles y políticas, y integrar la obtención de credenciales efímeras en un ejemplo de pipeline. ¡Ahora estás listo para aplicar estos principios y construir pipelines más seguros y robustos!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!