Gestión de Secretos en Pipelines CI/CD: Patrones, Anti-Patrones e Integración con Vault

Introducción: Por Qué los Secretos Son la Causa #1 de Compromiso en CI/CD

Si examina la causa raíz de casi todas las grandes brechas de CI/CD en los últimos años — desde el ataque a la cadena de suministro de Codecov hasta el incidente de seguridad de CircleCI — encontrará al mismo culpable: secretos comprometidos. Claves de API, credenciales de nube, contraseñas de bases de datos, certificados de firma — estas son las llaves maestras que los atacantes persiguen, y los pipelines de CI/CD son donde concentran sus esfuerzos.

La razón es estructural. Los pipelines existen en una posición singularmente peligrosa: deben tener acceso a credenciales de producción para desplegar software, pero son inherentemente efímeros, multi-tenant y están expuestos a código no confiable. Cada pull request, cada actualización de dependencia, cada push de un contribuidor desencadena la ejecución del pipeline — y cada ejecución es un vector potencial para la exfiltración de secretos.

El desafío no es simplemente «no pongas secretos en el código.» Es mucho más profundo que eso. ¿Cómo se le da a un entorno de cómputo efímero y desechable acceso a sus credenciales más sensibles sin que esas credenciales se filtren en logs, artefactos, jobs posteriores o en manos de actores maliciosos? Esa es la pregunta que responde esta guía.

Cubriremos cómo se exponen los secretos, cómo inyectarlos de forma segura, cómo integrar HashiCorp Vault y la federación de identidad nativa de la nube, y qué anti-patrones evitar. Esta es una guía para profesionales — espere YAML real, comandos CLI reales y decisiones arquitectónicas reales.

Cómo Se Exponen los Secretos en CI/CD

Antes de discutir soluciones, necesitamos comprender el panorama de amenazas. Los secretos se filtran de los pipelines a través de varios vectores bien documentados.

Secretos Codificados en Configs de Pipeline e IaC

El vector de fuga más básico — y aún perturbadoramente común — son las credenciales codificadas directamente en archivos de configuración de pipeline o plantillas de Infrastructure as Code. Un desarrollador probando un despliegue podría insertar una clave de acceso de AWS en un archivo .github/workflows/deploy.yml o un archivo Terraform main.tf, hacer commit y olvidarse. Incluso si se elimina en un commit posterior, el secreto vive para siempre en el historial de Git.

# NUNCA HAGA ESTO — credenciales codificadas en un archivo de workflow
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE
      AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    steps:
      - run: aws s3 sync ./build s3://my-bucket

Secretos en Variables de Entorno Impresos en Logs

Las plataformas de CI típicamente inyectan secretos como variables de entorno. El problema surge cuando los pasos del pipeline imprimen inadvertidamente esas variables a stdout. Un comando env descuidado, un printenv de depuración, o una herramienta verbosa que vuelca su configuración puede exponer secretos en los logs de compilación que a menudo se retienen durante días o semanas y son accesibles para todos los miembros del proyecto.

# Peligroso: esto imprime TODAS las variables de entorno, incluyendo secretos
- run: printenv | sort

# También peligroso: flags verbosos en herramientas que vuelcan la configuración
- run: terraform plan -debug

Secretos Persistidos en Artefactos de Compilación o Capas de Contenedores

Un secreto inyectado durante un build de Docker podría persistir en una capa intermedia incluso después de ser eliminado en una instrucción RUN posterior. De manera similar, los artefactos de compilación — JARs, ZIPs, binarios compilados — podrían incrustar archivos de configuración que contienen credenciales presentes en el momento de la compilación.

# MAL: El secreto persiste en la capa creada por la instrucción COPY
COPY .env /app/.env
RUN /app/setup.sh
RUN rm /app/.env   # Demasiado tarde — todavía está en una capa anterior

Secretos Accesibles a Workflows de PR No Confiables

Este es uno de los vectores más peligrosos, particularmente en proyectos de código abierto. GitHub Actions, por ejemplo, no proporciona secretos a workflows desencadenados por pull_request desde forks — por diseño. Sin embargo, el evento pull_request_target tiene acceso a secretos, y si el workflow hace checkout y ejecuta el código del autor del PR, crea un camino directo para la exfiltración de secretos.

Alcances de Secretos Excesivamente Amplios

Muchas organizaciones configuran secretos a nivel de organización o grupo cuando deberían estar limitados a repositorios individuales o entornos. Un secreto a nivel de organización en GitHub Actions está disponible para cada repositorio en esa organización. Si cualquiera de esos repositorios es comprometido — o simplemente tiene un workflow mal configurado — todos los secretos a nivel de organización están en riesgo.

Patrones de Inyección de Secretos

Ahora que entendemos cómo se filtran los secretos, examinemos cómo introducirlos en los pipelines de forma segura.

Secretos Nativos de la Plataforma

Cada plataforma principal de CI/CD proporciona un mecanismo integrado de gestión de secretos. GitHub Actions tiene secretos de repositorio, entorno y organización. GitLab CI tiene variables CI/CD a nivel de proyecto y grupo con enmascaramiento y protección opcionales. Estos son el punto de partida más sencillo.

# GitHub Actions: referenciando un secreto de repositorio
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production
        env:
          API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
        run: ./deploy.sh
# GitLab CI: usando una variable enmascarada y protegida
deploy:
  stage: deploy
  script:
    - echo "Deploying with masked credentials"
    - ./deploy.sh
  variables:
    API_KEY: $PRODUCTION_API_KEY
  only:
    - main

Los secretos nativos de la plataforma son adecuados para muchos casos de uso, pero tienen limitaciones significativas: no hay generación dinámica, registro de auditoría limitado, rotación manual y no hay gestión centralizada a través de múltiples plataformas.

Gestores Externos de Secretos

Para organizaciones con requisitos de seguridad maduros, los gestores externos de secretos — HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault — proporcionan control centralizado, registro de auditoría, generación dinámica de secretos, rotación automática y políticas de acceso granulares. Profundizaremos en la integración con Vault en la siguiente sección.

Inyección Just-in-Time vs Secretos Precargados

Los secretos precargados se configuran una vez y se ponen a disposición de todas las ejecuciones del pipeline. Así es como funcionan la mayoría de los secretos nativos de la plataforma. La inyección just-in-time (JIT) recupera los secretos en el momento en que se necesitan, a menudo con TTLs cortos. La inyección JIT es superior porque reduce la ventana de exposición, permite credenciales dinámicas y proporciona registros de auditoría por ejecución.

# Inyección JIT: obtener el secreto solo cuando se necesita
- name: Get database credentials
  run: |
    DB_CREDS=$(vault kv get -format=json secret/data/myapp/db)
    export DB_USER=$(echo $DB_CREDS | jq -r '.data.data.username')
    export DB_PASS=$(echo $DB_CREDS | jq -r '.data.data.password')
    ./run-migrations.sh

Secretos Enmascarados vs Cifrados

Un concepto erróneo común: «enmascarado» no significa «seguro.» Cuando GitHub Actions enmascara un secreto, realiza reemplazo de cadenas en la salida del log. Si el valor del secreto es corto (por ejemplo, un token de 4 caracteres), el enmascaramiento podría no activarse. Si el secreto está codificado en base64 o transformado de alguna manera, el valor transformado no será enmascarado. El enmascaramiento es una conveniencia, no un límite de seguridad. Los secretos cifrados en reposo (que todas las plataformas principales proporcionan) protegen contra el compromiso del almacenamiento del lado de la plataforma, pero no hacen nada para prevenir la exfiltración en tiempo de ejecución.

Integración de HashiCorp Vault con CI/CD

HashiCorp Vault es el gestor externo de secretos más ampliamente adoptado para pipelines de CI/CD. Soporta múltiples métodos de autenticación adecuados para sistemas automatizados, generación dinámica de secretos y políticas granulares. A continuación se explica cómo integrarlo con las dos plataformas de CI/CD más comunes.

Vault AppRole Auth para CI Runners

AppRole es el método de autenticación orientado a máquinas de Vault. Utiliza un Role ID (como un nombre de usuario) y un Secret ID (como una contraseña) para autenticarse. El Secret ID puede configurarse para un solo uso y con un TTL, lo que lo hace adecuado para CI runners.

# Habilitar el método de autenticación AppRole
vault auth enable approle

# Crear una política para CI
vault policy write ci-deploy - <<EOF
path "secret/data/myapp/*" {
  capabilities = ["read"]
}
path "database/creds/myapp-role" {
  capabilities = ["read"]
}
EOF

# Crear un AppRole con la política de CI
vault write auth/approle/role/ci-deploy \
  token_policies="ci-deploy" \
  token_ttl=15m \
  token_max_ttl=30m \
  secret_id_ttl=10m \
  secret_id_num_uses=1

# Recuperar el Role ID (almacenar en la plataforma CI como variable no sensible)
vault read auth/approle/role/ci-deploy/role-id

# Generar un Secret ID de un solo uso (almacenar en la plataforma CI como secreto)
vault write -f auth/approle/role/ci-deploy/secret-id

Vault JWT/OIDC Auth con GitHub Actions

El enfoque moderno y preferido para GitHub Actions es la autenticación JWT/OIDC. GitHub Actions puede emitir un token OIDC para cada ejecución de workflow, y Vault puede validar ese token para autenticar el pipeline — eliminando la necesidad de almacenar cualquier credencial de Vault en GitHub.

# Configurar Vault JWT auth para GitHub Actions
vault auth enable jwt

vault write auth/jwt/config \
  bound_issuer="https://token.actions.githubusercontent.com" \
  oidc_discovery_url="https://token.actions.githubusercontent.com"

# Crear un rol que se vincule a un repositorio y rama específicos
vault write auth/jwt/role/github-deploy \
  role_type="jwt" \
  bound_audiences="https://github.com/my-org" \
  bound_claims_type="glob" \
  bound_claims='{"sub": "repo:my-org/my-repo:ref:refs/heads/main"}' \
  user_claim="repository_owner" \
  token_policies="ci-deploy" \
  token_ttl="10m"

Luego en su workflow de GitHub Actions, use la acción hashicorp/vault-action:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Import secrets from Vault
        uses: hashicorp/vault-action@v3
        with:
          url: https://vault.mycompany.com
          method: jwt
          role: github-deploy
          jwtGithubAudience: https://github.com/my-org
          secrets: |
            secret/data/myapp/db username | DB_USER ;
            secret/data/myapp/db password | DB_PASS

      - name: Run deployment
        run: |
          echo "Deploying with fetched credentials"
          ./deploy.sh

Vault JWT Auth con GitLab CI

GitLab CI tiene soporte nativo para la integración con Vault usando id_tokens. GitLab puede generar un JWT que Vault valida, similar al enfoque de GitHub Actions.

# Configurar Vault para GitLab JWT auth
vault auth enable -path=gitlab jwt

vault write auth/gitlab/config \
  bound_issuer="https://gitlab.com" \
  jwks_url="https://gitlab.com/-/jwks" \
  supported_algs="RS256"

vault write auth/gitlab/role/gitlab-deploy \
  role_type="jwt" \
  bound_claims='{"project_id": "12345", "ref_protected": "true"}' \
  user_claim="user_email" \
  token_policies="ci-deploy" \
  token_ttl="10m"

Y en su .gitlab-ci.yml:

deploy:
  stage: deploy
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.mycompany.com
  secrets:
    DB_USER:
      vault: myapp/db/username@secret
      token: $VAULT_ID_TOKEN
    DB_PASS:
      vault: myapp/db/password@secret
      token: $VAULT_ID_TOKEN
  script:
    - ./deploy.sh

Secretos Dinámicos

Una de las características más poderosas de Vault es la generación dinámica de secretos. En lugar de almacenar contraseñas de bases de datos estáticas, Vault puede generar credenciales de corta duración bajo demanda. Cuando el pipeline finaliza, las credenciales expiran automáticamente.

# Habilitar el motor de secretos de base de datos
vault secrets enable database

# Configurar una conexión PostgreSQL
vault write database/config/myapp-db \
  plugin_name=postgresql-database-plugin \
  connection_url="postgresql://{{username}}:{{password}}@db.mycompany.com:5432/myapp" \
  allowed_roles="myapp-role" \
  username="vault_admin" \
  password="vault_admin_password"

# Crear un rol que genere credenciales con un TTL de 1 hora
vault write database/roles/myapp-role \
  db_name=myapp-db \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="2h"

# En su pipeline, obtenga credenciales dinámicas
# vault read database/creds/myapp-role
# Devuelve un par único de usuario/contraseña válido por 1 hora

Los secretos dinámicos eliminan por completo el problema de la rotación de credenciales. Cada ejecución de pipeline obtiene sus propias credenciales únicas, y las credenciales comprometidas expiran automáticamente.

Credenciales de Corta Duración e Identidad de Carga de Trabajo

El avance más significativo en la gestión de secretos de CI/CD en los últimos años es la federación de identidad de carga de trabajo (workload identity federation) — la capacidad de una plataforma de CI/CD para autenticarse directamente con un proveedor de nube usando su propia identidad, sin ninguna credencial almacenada.

GitHub Actions OIDC con AWS

GitHub Actions puede asumir un rol de AWS IAM directamente usando federación OIDC. No se almacenan claves de acceso de AWS en ningún lugar.

# Primero, crear un proveedor de identidad OIDC en AWS (vía Terraform)
resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}

# Crear un rol IAM que GitHub Actions pueda asumir
resource "aws_iam_role" "github_actions" {
  name = "github-actions-deploy"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Federated = aws_iam_openid_connect_provider.github.arn
      }
      Action = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringEquals = {
          "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
        }
        StringLike = {
          "token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:ref:refs/heads/main"
        }
      }
    }]
  })
}
# Workflow de GitHub Actions usando OIDC
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
          aws-region: us-east-1
          role-duration-seconds: 900   # 15 minutos

      - name: Deploy
        run: aws s3 sync ./build s3://my-bucket

GitHub Actions OIDC con GCP

Google Cloud soporta el mismo patrón a través de Workload Identity Federation.

# Crear un Workload Identity Pool y Provider (gcloud CLI)
gcloud iam workload-identity-pools create "github-pool" \
  --project="my-project" \
  --location="global" \
  --display-name="GitHub Actions Pool"

gcloud iam workload-identity-pools providers create-oidc "github-provider" \
  --project="my-project" \
  --location="global" \
  --workload-identity-pool="github-pool" \
  --display-name="GitHub Provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
  --attribute-condition="assertion.repository_owner == 'my-org'" \
  --issuer-uri="https://token.actions.githubusercontent.com"

# Conceder a la Workload Identity la capacidad de suplantar una cuenta de servicio
gcloud iam service-accounts add-iam-policy-binding \
  deploy-sa@my-project.iam.gserviceaccount.com \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo"
# Workflow de GitHub Actions para GCP
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/123456/locations/global/workloadIdentityPools/github-pool/providers/github-provider
          service_account: deploy-sa@my-project.iam.gserviceaccount.com

      - name: Deploy to Cloud Run
        run: gcloud run deploy my-service --image=gcr.io/my-project/my-app:latest

Federación OIDC en GitLab CI

GitLab CI soporta el mismo patrón de federación OIDC con AWS, GCP y Azure. La configuración es similar — se configura el proveedor de nube para confiar en el emisor OIDC de GitLab y se vincula el acceso a IDs de proyecto, ramas o entornos específicos.

# GitLab CI con AWS OIDC
assume_role:
  stage: deploy
  id_tokens:
    AWS_OIDC_TOKEN:
      aud: https://sts.amazonaws.com
  script:
    - >
      STS_CREDS=$(aws sts assume-role-with-web-identity
      --role-arn arn:aws:iam::123456789012:role/gitlab-deploy
      --role-session-name "gitlab-ci-${CI_PIPELINE_ID}"
      --web-identity-token "${AWS_OIDC_TOKEN}"
      --duration-seconds 900)
    - export AWS_ACCESS_KEY_ID=$(echo $STS_CREDS | jq -r '.Credentials.AccessKeyId')
    - export AWS_SECRET_ACCESS_KEY=$(echo $STS_CREDS | jq -r '.Credentials.SecretAccessKey')
    - export AWS_SESSION_TOKEN=$(echo $STS_CREDS | jq -r '.Credentials.SessionToken')
    - aws s3 sync ./build s3://my-bucket

Por Qué las Credenciales de Corta Duración Son Superiores

Las ventajas de las credenciales federadas de corta duración sobre los secretos estáticos de larga duración son sustanciales:

  • No hay secretos que robar. No hay credenciales almacenadas que exfiltrar. El pipeline se autentica con un JWT firmado que es válido solo para esa ejecución específica.
  • No se necesita rotación. Las credenciales se generan por ejecución y expiran automáticamente. No hay nada que rotar.
  • Alcance granular. El acceso puede restringirse a repositorios, ramas, entornos e incluso jobs específicos de workflow.
  • Registro de auditoría completo. Los logs del proveedor de nube muestran exactamente qué ejecución de pipeline accedió a qué recursos, vinculado al claim OIDC.
  • Reducción del radio de impacto. Incluso si una credencial es de alguna manera exfiltrada, expira en minutos, no en meses.

Anti-Patrones a Evitar

Saber qué no hacer es tan importante como conocer los patrones correctos. Estos anti-patrones se observan regularmente en entornos de producción.

Uso de Personal Access Tokens en CI

Los personal access tokens (PATs) vinculados a cuentas individuales de desarrolladores son uno de los patrones más comunes y más peligrosos. Cuando un desarrollador deja la organización, su PAT puede seguir funcionando. Los PATs típicamente tienen permisos amplios — mucho más de lo que el pipeline necesita. Si se exfiltran, el atacante obtiene acceso a todo lo que ese desarrollador podía acceder.

En su lugar: Use cuentas de máquina con tokens de alcance limitado, o mejor aún, use tokens de instalación de GitHub App o federación OIDC.

Compartir Secretos Entre Entornos

Usar la misma contraseña de base de datos para desarrollo, staging y producción — o la misma clave de API para todos los entornos — significa que un compromiso de su entorno menos seguro (generalmente dev) le da a los atacantes acceso a producción. La separación de entornos no tiene sentido si las credenciales son las mismas.

En su lugar: Use secretos con alcance por entorno. En GitHub Actions, configure entornos de despliegue con sus propios almacenes de secretos. En GitLab, use variables protegidas con alcance a entornos específicos.

No Rotar Secretos Después de una Exposición

Cuando un secreto se registra accidentalmente en logs, se hace commit a un repositorio o se expone en un artefacto de compilación, muchos equipos simplemente eliminan el log o borran el commit sin rotar la credencial. Esto es inadecuado. Debe asumir que el secreto ha sido observado y rotarlo inmediatamente.

En su lugar: Trate cualquier exposición como un compromiso. Rote inmediatamente. Automatice la rotación donde sea posible. Use secretos dinámicos para hacer que el problema sea irrelevante.

Confiar en pull_request_target con Secretos

El evento pull_request_target en GitHub Actions se ejecuta en el contexto de la rama base, lo que significa que tiene acceso a secretos. Esto está destinado a operaciones seguras como etiquetar PRs. Sin embargo, si su workflow hace checkout del head ref del PR y ejecuta ese código, le ha dado a un contribuidor externo acceso completo a sus secretos.

# PELIGROSO: Esto le da al autor del PR acceso a todos los secretos del repositorio
on: pull_request_target
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}  # Haciendo checkout de código no confiable!
      - run: make test  # Ejecutando código no confiable con acceso a secretos!

En su lugar: Nunca haga checkout ni ejecute código de PR en un workflow pull_request_target. Si necesita ejecutar pruebas en código de PR con secretos, use un enfoque de dos workflows: ejecute código no confiable en un workflow pull_request (sin secretos), luego use un trigger workflow_run separado para operaciones confiables.

Defensa en Profundidad: Un Enfoque por Capas

Ningún control único es suficiente. La gestión efectiva de secretos requiere múltiples capas de defensa superpuestas.

Escaneo de Secretos

Implemente escaneo en tres etapas:

  • Pre-commit: Use herramientas como gitleaks o detect-secrets como hooks de pre-commit para evitar que los secretos ingresen al repositorio.
  • En el pipeline: Ejecute escaneo de secretos como un paso de CI en cada pull request. Herramientas como trufflehog pueden escanear diffs, historial de commits e incluso archivos binarios.
  • Post-commit: Habilite el escaneo de secretos integrado de GitHub o la detección de secretos de GitLab para escanear continuamente todo el contenido del repositorio y alertar sobre hallazgos.
# Hook pre-commit con gitleaks
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks:
      - id: gitleaks
# Escaneo en pipeline con trufflehog
- name: Scan for secrets
  run: |
    docker run --rm -v "$PWD:/repo" trufflesecurity/trufflehog:latest \
      git file:///repo --only-verified --fail

Registro de Auditoría para Acceso a Secretos

Cada acceso a un secreto debe ser registrado. Vault proporciona logs de auditoría detallados por defecto. Los gestores de secretos de proveedores de nube (AWS Secrets Manager, GCP Secret Manager) se integran con CloudTrail y Cloud Audit Logs respectivamente. Para secretos nativos de la plataforma, habilite las funciones de registro de auditoría disponibles en GitHub Enterprise o GitLab Ultimate.

# Habilitar registro de auditoría de Vault
vault audit enable file file_path=/var/log/vault/audit.log

# Cada acceso genera una entrada de log como:
# {"type": "response", "auth": {"token_type": "service", "policies": ["ci-deploy"]},
#  "request": {"path": "secret/data/myapp/db", "operation": "read"}, ...}

Alcance de Privilegios Mínimos

Aplique el principio de privilegios mínimos de forma agresiva:

  • Limite los secretos al repositorio específico que los necesita, no a la organización.
  • Use secretos a nivel de entorno para que las credenciales de producción solo estén disponibles para workflows que despliegan a producción.
  • Configure protección de ramas para que solo los workflows que se ejecutan en ramas protegidas puedan acceder a secretos de producción.
  • En Vault, escriba políticas que concedan acceso a la ruta más estrecha posible con capacidades de solo lectura.
# Política de Vault: acceso mínimo para el CI de un microservicio específico
path "secret/data/payments-service/production" {
  capabilities = ["read"]
}

# Denegar acceso a todo lo demás por defecto (comportamiento predeterminado de Vault)
# Sin comodines, sin rutas amplias

Rotación Automatizada

Los secretos estáticos deben rotarse de forma regular y de inmediato después de cualquier sospecha de exposición. Automatice este proceso:

  • Use los secretos dinámicos de Vault para eliminar la necesidad de rotación por completo.
  • Para secretos que deben ser estáticos (por ejemplo, claves de API de terceros), use la rotación integrada de AWS Secrets Manager con funciones Lambda o soluciones nativas de la nube similares.
  • Implemente alertas para secretos que no se han rotado dentro de su tiempo de vida esperado.
# AWS Secrets Manager: configurar rotación automática
aws secretsmanager rotate-secret \
  --secret-id myapp/api-key \
  --rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:rotate-api-key \
  --rotation-rules '{"ScheduleExpression": "rate(30 days)"}'

Conclusión: La Gestión de Secretos Es Continua

La gestión de secretos no es una casilla que marcar durante la configuración inicial del pipeline. Es una disciplina continua que debe evolucionar a medida que su infraestructura crece, a medida que surgen nuevas técnicas de ataque y a medida que su equipo cambia. Los patrones descritos en esta guía — federación OIDC, secretos dinámicos, inyección just-in-time, alcance de privilegios mínimos y escaneo por capas — representan el estado actual del arte, pero requieren atención continua.

Comience auditando sus pipelines actuales. Identifique cada credencial almacenada. Para cada una, pregunte: ¿puede ser reemplazada por una credencial de corta duración o federación de identidad de carga de trabajo? ¿Se puede reducir este alcance? ¿Se está registrando este secreto en algún lugar? ¿Existe un registro de auditoría para cada acceso?

Las organizaciones que sufren brechas de CI/CD no son las que nunca almacenaron un secreto — eso es imposible. Son las que trataron la gestión de secretos como una tarea de configuración única en lugar de una práctica de seguridad viva. Construya la automatización, aplique las políticas, monitoree los logs de acceso e itere. Sus pipelines serán significativamente más difíciles de comprometer como resultado.