Los secrets permanentes en pipelines suelen terminar replicados entre repositorios, expuestos accidentalmente en logs o activos durante meses sin rotación.
Con OpenID Connect (OIDC), un job de GitHub Actions puede demostrar su identidad ante Vault sin almacenar una credencial permanente. GitHub emite un JWT firmado con datos del repositorio, la rama y la ejecución; Vault valida esos datos y entrega un token de acceso de corta duración.
Importante: el token de Vault es temporal, pero un valor almacenado en KV puede seguir siendo estático. Para obtener credenciales realmente efímeras, utiliza un motor de secretos dinámicos de Vault, como Database, AWS o PKI.
Cuándo conviene aplicarlo
Este enfoque resulta especialmente útil para:
- Despliegues desde GitHub Actions.
- Repositorios con acceso a infraestructura o servicios cloud.
- Organizaciones que todavía rotan credenciales manualmente.
- Ambientes de producción que necesitan trazabilidad por repositorio y rama.
Requisitos previos
Necesitarás:
- Una instancia de Vault inicializada, desbloqueada y accesible desde el runner.
- Acceso administrativo para configurar métodos de autenticación, políticas y auditoría.
- Un secreto KV o un motor de secretos dinámicos ya configurado.
- Un entorno protegido de GitHub, por ejemplo
production. - Un workflow con
id-token: write. - La URL HTTPS y la cadena de confianza de Vault correctamente configuradas.
Los siguientes comandos administrativos deben ejecutarse desde una estación de operación segura, no dentro del pipeline.
Cómo funciona el intercambio
- El job solicita un JWT al proveedor OIDC de GitHub.
- GitHub firma el token con claims como repositorio, rama, workflow y ejecución.
- Vault valida la firma, el issuer, el audience y los claims permitidos.
- Si la identidad coincide, Vault emite un token con política y TTL limitados.
- El job utiliza ese token para leer únicamente el secreto autorizado.
- La credencial se entrega al proceso de despliegue y desaparece al finalizar el job.
El permiso id-token: write solo habilita la solicitud del JWT; no concede acceso de escritura al repositorio ni a Vault por sí mismo. GitHub documenta este comportamiento y el flujo con Vault.

Paso 1: configurar GitHub como emisor confiable
Comprueba primero si el método JWT ya está habilitado:
vault auth listSi no aparece montado en jwt/, habilítalo y configura el issuer:
vault auth enable jwt
vault write auth/jwt/config \
bound_issuer="https://token.actions.githubusercontent.com" \
oidc_discovery_url="https://token.actions.githubusercontent.com"Vault utilizará el documento de descubrimiento de GitHub y sus claves públicas para verificar la firma del JWT.
Paso 2: crear una política de mínimo privilegio
Para KV v2, la ruta utilizada por la política incluye el segmento /data/:
# deploy-production.hcl
path "secret/data/deploy/prod" {
capabilities = ["read"]
}Aplica y comprueba la política:
vault policy write deploy-production deploy-production.hcl
vault policy read deploy-productionNo concedas acceso a secret/data/* si el workflow solo necesita una ruta.
Paso 3: restringir repositorio, rama y audience
Crea el rol mediante JSON para evitar errores al representar bound_claims:
{
"role_type": "jwt",
"user_claim": "repository",
"bound_audiences": ["vault-prod"],
"bound_claims_type": "string",
"bound_claims": {
"repository": "ORG/REPO",
"ref": "refs/heads/main"
},
"claim_mappings": {
"repository": "repository",
"ref": "ref",
"workflow_ref": "workflow_ref",
"run_id": "run_id"
},
"policies": ["deploy-production"],
"ttl": "10m",
"max_ttl": "15m"
}Valida el JSON y aplica el rol:
jq empty deploy-production-role.json
vault write auth/jwt/role/deploy-production \
@deploy-production-role.json
vault read auth/jwt/role/deploy-productionEl valor vault-prod será el audience solicitado por el workflow. Desde Vault 1.17, cuando el JWT contiene aud, al menos un valor de bound_audiences debe coincidir exactamente. Documentación del método JWT de Vault.
Para mayor resistencia ante renombres o transferencias, considera restringir también repository_id y repository_owner_id. Si utilizas bound_subject, inspecciona el formato real: los repositorios creados desde el 15 de julio de 2026 pueden usar subjects inmutables con los identificadores del propietario y del repositorio. Referencia actual de claims OIDC de GitHub.
Paso 4: solicitar el secreto desde el workflow
El siguiente job autentica mediante OIDC y expone la credencial únicamente al proceso de despliegue:
permissions: {}
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Obtener credencial desde Vault
id: vault
uses: hashicorp/vault-action@v4
with:
url: https://vault.ejemplo.com
method: jwt
role: deploy-production
jwtGithubAudience: vault-prod
exportEnv: false
secrets: |
secret/data/deploy/prod token | DEPLOY_TOKEN
- name: Desplegar
env:
DEPLOY_TOKEN: ${{ steps.vault.outputs.DEPLOY_TOKEN }}
run: ./deploy.shLa acción solicita el JWT directamente a GitHub y realiza el login contra auth/jwt/login; no existe una variable automática llamada ACTIONS_ID_TOKEN. La sintaxis de secretos y el método JWT están documentados en HashiCorp Vault Action.
En producción, fija las acciones a un SHA completo revisado, protege el entorno production con aprobaciones y evita ejecutar este job para pull requests provenientes de forks.
Paso 5: validar antes de producción
Realiza la migración en paralelo:
- Conserva temporalmente la credencial anterior como punto de retorno.
- Prueba el rol OIDC en un entorno aislado.
- Ejecuta una prueba positiva desde
main. - Ejecuta una prueba negativa desde otra rama y confirma que Vault rechaza el login.
- Comprueba el despliegue, la salud del servicio y los eventos de auditoría.
- Revoca la credencial anterior y elimínala de GitHub solo después de completar las verificaciones.
No decodifiques ni imprimas el JWT completo en logs. Para investigar claims, utiliza una ejecución controlada sin datos sensibles o herramientas específicas de diagnóstico.
Paso 6: comprobar la auditoría
Primero confirma que Vault tiene dispositivos de auditoría activos:
vault audit list -detailedEste comando solo enumera los dispositivos configurados; no muestra los accesos. Los eventos deben consultarse en el archivo, syslog o plataforma donde se envía la auditoría.
Vault aplica HMAC a muchos valores de texto. Para localizar un repositorio en un dispositivo llamado file, calcula su representación y búscala en el registro:
REPO_HMAC="$(
vault write -field=hash \
sys/audit-hash/file \
input="ORG/REPO"
)"
grep -F "$REPO_HMAC" /var/log/vault_audit.logLa consulta debe realizarla un operador de Vault; el runner no debería tener acceso al archivo de auditoría. HashiCorp recomienda disponer de más de un dispositivo para evitar que un fallo de auditoría afecte la disponibilidad. Auditoría de Vault.
Problemas frecuentes
Vault rechaza el login
Verifica el issuer, la sincronización horaria, el audience y los claims. vault-prod debe coincidir exactamente entre jwtGithubAudience y bound_audiences.
El login funciona desde una rama no autorizada
Revisa que bound_claims contenga ref y que no se haya configurado bound_claims_type: glob con un patrón demasiado amplio.
Vault devuelve 403 al leer el secreto
Comprueba la política y la ruta. En KV v2, la política utiliza secret/data/deploy/prod, aunque con la CLI normalmente se consulte como vault kv get secret/deploy/prod.
El token expira durante el despliegue
Mide la duración real del job y asigna un TTL ligeramente superior. No utilices un TTL de horas para resolver un despliegue que debería durar minutos; divide el proceso o implementa renovación controlada cuando sea necesario.
La credencial aparece en los logs
Elimina echo, printenv, set -x y el modo debug. Aunque la acción enmascare sus salidas, otra herramienta podría transformar el valor e impedir que GitHub lo reconozca.

Buenas prácticas para producción
- Separa roles, políticas y rutas por ambiente.
- Protege los entornos de GitHub con aprobaciones y reglas de rama.
- Usa IDs inmutables del repositorio cuando el modelo de riesgo lo requiera.
- Mantén TTL y privilegios mínimos.
- Fija acciones de terceros a commits revisados.
- Supervisa fallos de autenticación y lecturas anómalas.
- Elimina los secrets heredados cuando la migración haya sido verificada.
- Usa motores dinámicos si la credencial final también debe ser temporal.