TL;DR — Resumen Rápido
Ansible Vault cifra secretos en playbooks con AES-256. Aprende vault create, encrypt_string, vault IDs, integracion CI/CD y rotacion de contrasenas.
Almacenar contrasenas de bases de datos, claves API y claves privadas SSL en texto plano dentro de tus playbooks de Ansible es uno de los errores de seguridad mas comunes en la automatizacion de infraestructura. Una vez que un secreto llega sin cifrar a un repositorio git, esta efectivamente comprometido — el historial de git persiste para siempre, y cualquier persona con acceso de lectura al repositorio puede recuperarlo. Ansible Vault resuelve esto cifrando secretos directamente dentro de tu repositorio usando AES-256, para que puedas commitear archivos de credenciales cifrados junto con tus playbooks sin exponer nada sensible.
Requisitos Previos
Antes de trabajar con esta guia, deberias tener:
- Ansible 2.8 o posterior instalado en tu nodo de control (
ansible --versionpara verificar) - Familiaridad basica con playbooks de Ansible y la estructura de directorios group_vars
- Un proyecto Ansible existente con inventario y al menos un playbook
- Un editor de texto configurado como tu
$EDITOR(nano, vim o VS Code) - Para las secciones de CI/CD: un pipeline de GitHub Actions, GitLab CI o Jenkins que puedas modificar
Nuevo en Ansible? Lee primero nuestra guia de Ansible para principiantes para configurar la estructura de tu proyecto antes de agregar Vault.
Por Que los Secretos en Texto Plano son Peligrosos
Cada secreto almacenado en texto plano en un repositorio git conlleva el mismo riesgo: exposicion. Considera lo que tipicamente termina en archivos de variables de Ansible sin Vault:
- Contrasenas root de bases de datos en
group_vars/dbservers.yml - Claves de acceso AWS en
host_vars/deploy.yml - Credenciales SMTP en
roles/notifications/defaults/main.yml - Contenido de clave privada SSL en un bloque
vars:dentro de un playbook
Cualquiera de los siguientes eventos expone estos secretos de inmediato: un repositorio accidentalmente hecho publico, la cuenta de GitHub de un colaborador comprometida, un log de CI/CD filtrado, o un ex-empleado descontento. Ansible Vault evita esto asegurando que los archivos almacenados en git contengan solo texto cifrado AES-256, no los valores reales de los secretos.
La contrasena del vault en si nunca entra en tu repositorio. Permanece en tu cabeza, en un gestor de contrasenas, o en el almacen de secretos de tu CI/CD — separada del codigo.
Crear Archivos Cifrados con ansible-vault create
El subcomando create abre tu $EDITOR configurado con un archivo vacio, te permite escribir tus secretos y guarda el archivo cifrado al cerrar el editor:
ansible-vault create group_vars/all/vault.yml
Se te pedira una nueva contrasena de vault (dos veces para confirmacion). El editor se abre. Escribe tus secretos:
vault_db_password: "Tr0ub4dor&3"
vault_db_root_password: "R4nd0mS33d#9"
vault_api_key: "sk-live-abc123def456ghi789"
vault_smtp_password: "M@ilS3rv1c3P@ss"
vault_ssl_private_key: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA3cH7...
-----END RSA PRIVATE KEY-----
Guarda y cierra. El archivo en disco ahora contiene solo el blob cifrado:
$ANSIBLE_VAULT;1.1;AES256
66386439653763616335616230333934356262313032393132376163633335636630
34663437326664303537373561373039636131336531353364386530636431343739
...
Editar y ver archivos cifrados
# Abrir en editor para modificaciones
ansible-vault edit group_vars/all/vault.yml
# Mostrar contenido descifrado sin editar
ansible-vault view group_vars/all/vault.yml
# Cifrar un archivo de texto plano existente
ansible-vault encrypt secrets.yml
# Descifrar un archivo de vuelta a texto plano (usar con cuidado)
ansible-vault decrypt secrets.yml
Cifrar Variables Individuales con encrypt_string
A veces quieres un solo secreto dentro de un archivo no vault — quiza porque deseas que el contexto circundante sea visible sin descifrar todo. El subcomando encrypt_string genera un valor cifrado que puedes pegar directamente en cualquier archivo YAML:
ansible-vault encrypt_string --name 'vault_stripe_key' 'sk_live_abc123xyz'
Salida:
vault_stripe_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
66653465346564383664396631316639
37313739623731633864333736396230
...
Pega este bloque directamente en cualquier archivo YAML de variables. Ansible lo descifra de forma transparente en tiempo de ejecucion, de la misma manera que maneja un archivo vault completamente cifrado.
Cuando usar encrypt_string vs archivos cifrados:
| Enfoque | Usar cuando |
|---|---|
ansible-vault create vault.yml | La mayoria de casos — mantiene secretos organizados, facil de auditar |
encrypt_string | Secreto unico en un archivo principalmente plano; secretos en linea en roles |
| Playbook completo cifrado | Raramente necesario; rompe la legibilidad |
Vault IDs: Multiples Contrasenas para Diferentes Conjuntos de Secretos
Los vault IDs te permiten etiquetar contenido cifrado para que Ansible sepa que contrasena aplica. Esto es esencial cuando staging y produccion usan contrasenas diferentes, o cuando quieres rotar una contrasena sin re-cifrar todo.
Cifrar con un vault ID
# Cifrar con un vault ID nombrado
ansible-vault create --vault-id prod@prompt group_vars/production/vault.yml
ansible-vault create --vault-id staging@prompt group_vars/staging/vault.yml
# O apuntar a un archivo de contrasena
ansible-vault create --vault-id prod@~/.vault_prod group_vars/production/vault.yml
Descifrar con multiples vault IDs
ansible-playbook site.yml \
--vault-id prod@~/.vault_prod \
--vault-id staging@~/.vault_staging
Ansible hace coincidir automaticamente cada bloque cifrado con su etiqueta de vault ID y usa la contrasena correcta. El contenido cifrado sin un vault ID explicito usa por defecto la etiqueta default.
Integracion CI/CD con —vault-password-file
Los prompts interactivos de contrasena bloquean los pipelines automatizados. El flag --vault-password-file acepta una ruta a un archivo que contiene solo la contrasena del vault, permitiendo una ejecucion completamente no interactiva.
Ejemplo con GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Instalar Ansible
run: pip install ansible
- name: Escribir contrasena vault en archivo
run: echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > ~/.vault_pass
- name: Ejecutar playbook
run: |
ansible-playbook -i inventory/production site.yml \
--vault-password-file ~/.vault_pass
- name: Eliminar archivo de contrasena vault
if: always()
run: rm -f ~/.vault_pass
Almacena ANSIBLE_VAULT_PASSWORD en los secretos de GitHub Actions del repositorio (Configuracion → Secretos → Actions). La contrasena del vault nunca aparece en los logs porque GitHub enmascara los valores de los secretos.
Ejemplo con GitLab CI
# .gitlab-ci.yml
deploy:
script:
- echo "$ANSIBLE_VAULT_PASSWORD" > ~/.vault_pass
- ansible-playbook -i inventory/production site.yml --vault-password-file ~/.vault_pass
- rm -f ~/.vault_pass
variables:
ANSIBLE_VAULT_PASSWORD: $ANSIBLE_VAULT_PASSWORD
Usar un script de contrasena vault
Para contrasenas de vault dinamicas (obtenidas de HashiCorp Vault, AWS Secrets Manager o Azure Key Vault en tiempo de ejecucion), crea un script ejecutable que devuelva la contrasena:
#!/bin/bash
# ~/.get_vault_pass.sh
aws secretsmanager get-secret-value \
--secret-id ansible/vault-password \
--query SecretString \
--output text
chmod +x ~/.get_vault_pass.sh
ansible-playbook site.yml --vault-password-file ~/.get_vault_pass.sh
Mejores Practicas: Organizar Variables Vault
El patron mas mantenible separa tus archivos de variables en dos archivos paralelos por grupo:
group_vars/
all/
vars.yml # variables planas + referencias a variables vault_
vault.yml # cifrado, solo variables con prefijo vault_
production/
vars.yml
vault.yml
staging/
vars.yml
vault.yml
group_vars/all/vars.yml — texto plano, seguro de inspeccionar en git:
# group_vars/all/vars.yml
db_host: "db.internal.example.com"
db_port: 5432
db_name: "myapp_prod"
db_password: "{{ vault_db_password }}" # referencia, no el valor
api_endpoint: "https://api.stripe.com/v1"
stripe_key: "{{ vault_stripe_key }}"
smtp_host: "smtp.sendgrid.net"
smtp_user: "apikey"
smtp_password: "{{ vault_smtp_password }}"
group_vars/all/vault.yml — cifrado, todas con prefijo vault_:
# group_vars/all/vault.yml (este archivo esta cifrado en disco)
vault_db_password: "Tr0ub4dor&3"
vault_stripe_key: "sk_live_abc123"
vault_smtp_password: "SG.abc123xyz"
Beneficios de este patron:
- Los playbooks referencian nombres legibles (
db_password), no variables vault directamente git diffmuestra cambios en la estructura devars.ymlsin requerir la contrasena vault- Puedes auditar que variables existen en el archivo vault por nombre (ejecuta
ansible-vault view) - Los nuevos miembros del equipo pueden leer
vars.ymlpara entender la estructura de variables sin acceso al vault
Rotar Contrasenas del Vault con rekey
Rotar la contrasena del vault es una operacion de un solo comando:
ansible-vault rekey group_vars/all/vault.yml
Se te pedira la contrasena actual del vault, luego se te pedira ingresar y confirmar la nueva contrasena. El archivo se re-cifra en su lugar. Ningun secreto se descifra en disco durante este proceso.
Para multiples archivos vault a la vez:
# Re-encriptar todos los archivos vault del proyecto
find . -name "vault.yml" -exec ansible-vault rekey {} \;
# O con --new-vault-password-file para rotacion no interactiva
ansible-vault rekey \
--vault-password-file ~/.old_vault_pass \
--new-vault-password-file ~/.new_vault_pass \
group_vars/all/vault.yml group_vars/production/vault.yml
Despues de rekey, actualiza el almacen de secretos de tu CI/CD con la nueva contrasena antes de la proxima ejecucion del pipeline.
Usar Ansible Vault con AWX y Semaphore
AWX / Ansible Tower
AWX tiene un tipo de credencial nativo para Ansible Vault. Crea una credencial de tipo Vault (Configuracion → Credenciales → Agregar → Vault), ingresa la contrasena del vault, y asignala a tu plantilla de trabajo. AWX maneja el paso de la contrasena de forma segura sin exponerla en la salida del trabajo.
Para multiples vault IDs en AWX, crea una credencial Vault por ID y asigna todas ellas a la plantilla de trabajo.
Semaphore UI
Semaphore almacena contrasenas vault en su Almacen de Claves. Navega a Key Store → Nueva Clave, selecciona tipo Contrasena, ingresa tu contrasena vault, y nombrela (ej. vault-production). Al crear una plantilla de tarea, selecciona la clave vault en el campo Vault Password.
Semaphore pasa la clave a Ansible mediante un archivo temporal que se elimina despues de la ejecucion del playbook, siguiendo el mismo modelo de seguridad que el enfoque de archivo de contrasena en CI/CD.
Comparacion: Ansible Vault vs Alternativas
| Herramienta | Cifrado | Nativo en git | CI/CD | Secretos dinamicos | Complejidad |
|---|---|---|---|---|---|
| Ansible Vault | AES-256 | Si — archivos en repo | Archivo de contrasena | Via script | Baja |
| HashiCorp Vault | AES-256-GCM | No — servicio externo | API + token | Si (leases) | Alta |
| SOPS | AES-256 / PGP / KMS | Si — valores cifrados | Clave KMS | No | Media |
| git-crypt | AES-256 | Si — transparente | Clave GPG | No | Media |
Elige Ansible Vault cuando tus secretos solo necesitan ser accesibles por playbooks de Ansible y quieres cero infraestructura adicional. Elige HashiCorp Vault cuando necesitas secretos dinamicos, expiracion de contratos granular, o acceso de secretos para multiples aplicaciones. SOPS es util cuando necesitas archivos cifrados legibles por herramientas distintas de Ansible (Terraform, Helm). git-crypt funciona bien para archivos cifrados generales en un repo pero carece de integraciones especificas para Ansible.
Escenario Real: Credenciales de BD y Claves API en Multiples Entornos
Gestionas tres entornos (desarrollo, staging, produccion) para una aplicacion web. Cada entorno tiene una base de datos PostgreSQL, una clave API de Stripe y una contrasena SMTP de SendGrid. Desarrollo usa contrasenas locales debiles; produccion usa credenciales fuertes gestionadas por el equipo de seguridad.
Estructura de directorios:
proyecto/
ansible.cfg
inventory/
development/hosts
staging/hosts
production/hosts
group_vars/
all/
vars.yml # estructura de variables compartida
development/
vars.yml # configuracion de desarrollo no sensible
vault.yml # secretos de dev cifrados (contrasena separada)
staging/
vars.yml
vault.yml # secretos de staging cifrados
production/
vars.yml
vault.yml # secretos de produccion cifrados (mas restringidos)
playbooks/
deploy.yml
db_setup.yml
El vault.yml de cada entorno usa un vault ID y contrasena separados, gestionados por el equipo apropiado:
# Crear vault de desarrollo (contrasena del equipo de desarrollo)
ansible-vault create --vault-id dev@prompt group_vars/development/vault.yml
# Crear vault de staging (contrasena del equipo QA)
ansible-vault create --vault-id staging@prompt group_vars/staging/vault.yml
# Crear vault de produccion (contrasena del equipo de operaciones)
ansible-vault create --vault-id prod@prompt group_vars/production/vault.yml
Pipeline de despliegue (solo produccion):
ansible-playbook -i inventory/production playbooks/deploy.yml \
--vault-id prod@~/.vault_prod
La contrasena del vault de produccion nunca se comparte con desarrolladores o QA. Si el vault de staging se ve comprometido, los secretos de produccion no se ven afectados porque usan una contrasena completamente separada.
Errores Comunes y Casos Extremos
Olvidar la contrasena del vault es permanente. No hay mecanismo de recuperacion. Los datos cifrados se pierden. Almacena las contrasenas vault en un gestor de contrasenas y en un secreto CI/CD inmediatamente despues de crearlas.
Evita --ask-vault-pass en automatizacion. Bloquea los pipelines y requiere entrada interactiva. Siempre usa --vault-password-file en cualquier contexto no interactivo.
ansible.cfg puede establecer valores predeterminados de vault. Agrega vault_password_file = ~/.vault_pass a tu ansible.cfg para evitar especificarlo en cada comando.
Los archivos vault.yml nunca deben estar en .gitignore. El objetivo es commitearlos cifrados. Si tienes la tentacion de ignorar un archivo vault en git, probablemente lo dejaste en texto plano por accidente.
Los archivos cifrados siguen apareciendo como cambiados en git al re-cifrarse identicamente. AES-256 en modo CBC produce texto cifrado diferente para el mismo texto plano cada vez. No ejecutes ansible-vault encrypt en archivos sin cambios ya que contamina el historial git.
Los archivos binarios tambien pueden cifrarse. Certificados SSL, archivos keystore y claves SSH en formato binario pueden cifrarse con ansible-vault encrypt. Ansible los descifra en un archivo temporal antes de pasar la ruta al modulo correspondiente.
Solucion de Problemas
Error “Decryption failed” (fallo en descifrado):
# Verificar que la contrasena es correcta viendo el archivo
ansible-vault view group_vars/all/vault.yml
# Verificar que el vault ID coincide si usas vault IDs
ansible-vault view --vault-id prod@prompt group_vars/production/vault.yml
El playbook se ejecuta sin pedir la contrasena vault y usa un valor incorrecto:
Verifica si vault_password_file esta configurado en ansible.cfg. Si el archivo no existe o esta vacio, Ansible omite silenciosamente el descifrado del vault. Agrega --ask-vault-pass temporalmente para confirmar.
Error “File is not encrypted” (archivo no cifrado) al editar:
El archivo esta almacenado como texto plano. Cifra primero: ansible-vault encrypt filename.yml.
La variable muestra la cadena literal {{ vault_db_password }} en la salida de la tarea:
La variable se referencia pero el archivo vault no se cargo. Verifica que el archivo vault este en una ruta que Ansible carga automaticamente (group_vars/, host_vars/) o incluyelo explicitamente con vars_files: en el playbook.
El pipeline CI/CD muestra la contrasena vault en los logs:
Nunca pases la contrasena vault como argumento de linea de comandos. Escribe siempre en un archivo primero. Confirma que tu proveedor CI enmascara la variable secreta en la salida del log.
Resumen
- Almacena todos los secretos en archivos
group_vars/*/vault.ymlcifrados conansible-vault create - Usa la convencion de prefijo
vault_para distinguir variables cifradas de las planas - Referencia variables vault desde archivos
vars.ymlplanos usando{{ vault_varname }}para que los playbooks sean legibles - Usa
encrypt_stringsolo para secretos individuales en linea, no como patron principal de secretos - Usa vault IDs para mantener contrasenas separadas para diferentes entornos o equipos
- Integra con CI/CD usando
--vault-password-filey almacena la contrasena en tu gestor de secretos - Rota contrasenas vault con
ansible-vault rekeyy actualiza los secretos CI/CD inmediatamente - Nunca almacenes contrasenas vault en archivos que se committean a git
- AWX y Semaphore tienen soporte nativo de credenciales vault — prefielo sobre soluciones alternativas