Vault en producción: configuración HA con Raft


Desde hace tiempo me han estado preguntando sobre este tema, así que vamos a crear un clúster de Hashicorp Vault, algo fundamental para una empresa moderna. Una bóveda que nos ayudará a gestionar mejor los secretos y también a reducir los costos de nuestro uso en la nube.
La idea es tener un Hashicorp Vault multinodo con alta disponibilidad, usando raft como almacenamiento backend. De esta forma, podremos ofrecer resiliencia, escalabilidad y seguridad. Además, mostraremos cómo conectarnos a través de la interfaz de usuario y cómo las aplicaciones pueden utilizar esos secretos.
HashiCorp Vault es una herramienta de código abierto diseñada para almacenar y gestionar de forma segura información sensible como secretos (claves API, contraseñas, certificados, etc.), tokens y claves de cifrado. Vault proporciona una interfaz unificada para administrar estos secretos de manera segura en distintos entornos, incluyendo infraestructuras en la nube, aplicaciones y servicios. Es capaz de manejar secretos dinámicos, ofrecer cifrado como servicio y admite diversos métodos de autenticación para controlar el acceso.
Preparación de Entorno
Tengo un cluster con un master y un worker, en Kubernetes. Para esa prueba de concepto, estoy usando microK8s.
Esta todo listo para comenzar a trabajar. Vamos a necesitar tener helm, instalado. Corremos los siguientes comandos para agregar y actualizar nuestras fuentes.
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
Para tener una idea de que vamos a estar haciendo, les dejo esta grafica.
Instalación Vault con Raft
Antes que nada vamos a crear custom-values.yaml
para habilitar la alta disponibilidad (HA) y configurar el backend Raft.
server:
affinity: ""
ha:
enabled: true
raft:
enabled: true
HashiCorp Vault utiliza Raft como backend de almacenamiento para garantizar alta disponibilidad, replicación de datos y una fuerte consistencia entre los nodos del clúster. Raft permite que Vault funcione sin dependencias externas, lo que facilita su gestión y escalabilidad, asegurando al mismo tiempo tolerancia a fallos y una recuperación confiable ante fallos.
Aplicamos.
helm install vault hashicorp/vault -f custom-values.yaml --namespace vault --create-namespace
Deberías ver pods con el estado 0/1. Esto indica que los pods de Vault están en ejecución, pero aún no han sido inicializados ni desbloqueados (unsealed).
Vamos a inicializar el vault-0 de la siguiente manera.
kubectl exec -n vault vault-0 -- vault operator init
Ahora tenemos inicializado el pod master.
Importante: Guarda estas claves de forma segura, ya vas a necesitarár al menos 3 de ellas para desbloquear (unseal) Vault en caso de un reinicio o de que se vuelva a reseal.
Desbloquea (unseal) Vault utilizando tres de las claves de desbloqueo en cada pod de Vault. Esto hay que hacerlo en todos los podes vault-0, vault-1 & vault-2.
kubectl exec -n vault vault-0 -- vault operator unseal <Unseal Key1>
kubectl exec -n vault vault-0 -- vault operator unseal <Unseal Key2>
kubectl exec -n vault vault-0 -- vault operator unseal <Unseal Key3>
Nos queda sumar los demas pods al cluster raft.
kubectl exec -ti vault-1 -n vault -- vault operator raft join http://vault-0.vault-internal:8200
Luego hacemos el unseal.
kubectl exec -n vault vault-1 -- vault operator unseal <Unseal Key1>
kubectl exec -n vault vault-1 -- vault operator unseal <Unseal Key2>
kubectl exec -n vault vault-1 -- vault operator unseal <Unseal Key3>
Repetimos esta operación con el vault-2.
Una vez que terminamos el trabajo vamos a ver como nos quedo el cluster.
kubectl exec -ti vault-0 -n vault -- vault login
kubectl exec -ti vault-0 -n vault -- vault operator raft list-peers
Uala! Tenemos el “lider” y los seguidores.
Verificamos el estado de todos nuestros pods. Y vemos que estan listos para ser utilizados.
Ingress a Vault
Para exponer la interfaz web (UI) de HashiCorp Vault mediante Ingress y asegurarla con TLS, seguí estos pasos. Asegurate de tener Cert-Manager instalado para gestionar la provisión de certificados a través de Let’s Encrypt. Si no sabes como hacerlo, podes revisar aca.
Voy agregar el Token, que necesito de Cloudflare, en el Cluster.
kubectl create secret generic cloudflare-api-token-secret \
--namespace cert-manager \
--from-literal=api-token="TOKEN"
Vamos a crear nuestro cloudflare-clusterissuer.yaml
y realizar el challange.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns-cloudflare
spec:
acme:
email: TUMAIL
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-dns-cloudflare-key
solvers:
- dns01:
cloudflare:
email: TUMAIL
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
Ahora si! Vamos aplicar el ingress-vault.yaml
.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vault-ingress
namespace: vault
annotations:
cert-manager.io/cluster-issuer: letsencrypt-dns-cloudflare
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
rules:
- host: vault.esprueba.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vault # Ojo aca, puede ser que sea vault-ui
port:
number: 8200
tls:
- hosts:
- vault.esprueba.com
secretName: vault-tls
Listo! Ya esta con interfaz!
Siguiendo estos pasos, has configurado con éxito un clúster de HashiCorp Vault de múltiples nodos con alta disponibilidad, utilizando Raft para la replicación y consistencia de los datos. Esta configuración garantiza que tu sistema de gestión de secretos sea resiliente, escalable y seguro sobre Kubernetes. Al exponer la interfaz web de Vault de forma segura mediante Ingress, la administración y monitoreo de secretos se vuelve aún más ágil.
Implementación
Nos vamos a autenticar y generar unos secretos para consumir.
kubectl exec -ti vault-0 -n vault -- vault login
vault secrets enable -path=secret kv
vault kv put secret/demo username=santiago password=supersecreto
Ahora vamos a generar el código para consumirlo. Voy hacer algo sencillo en Python. Pero antes tengo que crear un Rol, que tenga permisos para consumirlo.
vault policy write read-secret - <<EOF
path "secret/demo" {
capabilities = ["read"]
}
EOF
Esto otorga permiso solo para leer secret/demo
. Ahora habilitamos el AppRole.
vault auth enable approle
Creamos el AppRole con tokens validos por 1 hora y renovables cada 4 horas. Lo vamos a llamar python-reader
.
vault write auth/approle/role/python-reader \
token_policies="read-secret" \
token_ttl=1h \
token_max_ttl=4h
Vamos a obtener los datos necesarios para nuestra aplicación. Solo esta vez.
/ $ vault read -field=role_id auth/approle/role/python-reader/role-id
3faac7e2-4ba8-1de1-eea6-92c420a47015
/ $ vault write -f -field=secret_id auth/approle/role/python-reader/secret-id
107f3d2d-9b18-8fd5-4194-74562d6339b9
Ya tenemos nuestro role-id
& secret-id
. Ahora vamos correr nuestro app.py
.
import hvac
import warnings
client = hvac.Client(url="https://vault.esprueba.com")
ROLE_ID = "3faac7e2-4ba8-1de1-eea6-92c420a47015"
SECRET_ID = "1803cfd7-ce7e-8e18-1a68-e1e512e77f9d"
# Login con AppRole
login_response = client.auth.approle.login(role_id=ROLE_ID, secret_id=SECRET_ID)
client.token = login_response["auth"]["client_token"]
# Si tu secreto está en KV v1 (como vimos antes)
secret_response = client.secrets.kv.v1.read_secret(path="demo", mount_point="secret")
data = secret_response["data"]
print("✅ Secreto leído:")
print("👤 Usuario:", data.get("username"))
print("🔑 Password:", data.get("password"))
Listo!
La advertencia del módulo SSL de Python (
ssl
) que está enlazado contra LibreSSL.urllib3
inspecciona esa biblioteca en tiempo de ejecución, y si ve que no es OpenSSL 1.1.1+ por eso lanza el warning.
Estas son las ventajas de implementar de esta manera.
Gestión centralizada de secretos
Vault se convierte en el único punto de control de secretos. Ya no hay credenciales hardcodeadas en el código, archivos.env
, imágenes de Docker ni pipelines de CI/CD.Autenticación robusta con AppRole
AppRole separa identidad (role_id
) de autenticación (secret_id
), lo que permite controlar y auditar accesos con precisión. Incluso si un atacante accede alrole_id
, no puede autenticarse sin elsecret_id
.Rotación de secretos sin downtime
Si cambiás un secreto en Vault, la app podrá leer el nuevo valor en el próximo acceso. No es necesario reiniciar ni desplegar nuevamente.Aplicación del principio de menor privilegio
Las políticas de Vault permiten que cada AppRole acceda únicamente a los paths que necesita. Si una app solo necesitasecret/demo
, no puede ver nada más.Auditoría completa y trazabilidad
Vault registra cada solicitud: qué secreto se accedió, desde qué AppRole, en qué momento. Esto permite trazabilidad y cumplimiento normativo.
Espero que les sirva!
Subscribe to my newsletter
Read articles from Santiago Fernandez directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Santiago Fernandez
Santiago Fernandez
I have a bachelor's degree in Technology from the University of Palermo, a Master in Information Security from the University of Murcia and different certifications such as CISSP | CISM | CDPSE | CCSK | CSX | MCSA | SMAC™️ | DSOE | DEPC | CSFPC | CSFPC | 5x AWS Certified. He is currently CISO at Klar, a Mexican Fintech. He was fortunate to be awarded as CISO of the Year in Argentina in 2021 and was among the Top 100 CISO's in the World in 2022. A lover of new technologies, he has developed a career in DevSecOps and Cloud Security at Eko Party, the largest security conference in Latin America.