Gestión de secretos

Cómo guardar contraseñas, tokens y claves fuera del spec del job, con cifrado en reposo y rotación versionada.

Seguridad·8 min de lectura·última revisión 2026-04-26

Contraseña de base de datos, clave de API, token OAuth, credencial de proveedor. Todo eso es secreto y nada de eso puede estar en el spec del job.

Por dos motivos. Primero, los specs suelen estar versionados en algún repositorio. Cualquier persona con acceso de lectura al repositorio pasa a tener acceso al secreto. Segundo, los specs transitan por logs, por terminales compartidas, por screenshots de revisión. Cada uno de esos puntos es una fuga esperando ocurrir.

HeroCtl trae un cofre de secretos integrado. No montas nada por fuera.

Creando un secreto

Por la CLI, tres formas:

# valor literal direto na linha de comando
heroctl secret create db_password --from-literal="s3nh4-l0nga-e-aleatoria"

# de um arquivo
heroctl secret create tls_key --from-file=./privkey.pem

# stdin (não fica no histórico do shell)
echo -n "valor-secreto" | heroctl secret create api_token --from-stdin

Por el panel web, hay un formulario equivalente.

Listando e inspeccionando

# lista todos os segredos visíveis pelo seu token
heroctl secret list

# mostra metadados (versão, autor, data de criação) — nunca o valor
heroctl secret describe db_password

# mostra o valor (exige permissão explícita)
heroctl secret get db_password

heroctl secret get es la operación más sensible del sistema. Aparece en el log de auditoría con el nombre de quien leyó, cuándo, y desde qué IP.

Atención: En producción, restringe secret:read al mínimo de roles. Para la mayoría de los equipos, nadie necesita leer valores manualmente — el cluster los inyecta directamente en el job.

Cifrado en reposo

Cada secreto se cifra con AES-256 antes de ser persistido. La clave de cifrado del cluster queda protegida y nunca se almacena junto con los datos cifrados.

Operacionalmente eso significa que un backup del estado del cluster, sin la clave, es inútil para un atacante. Puedes subir un snapshot a un disco duro externo sin preocuparte.

La clave en sí se genera en la inicialización del cluster y queda en archivo protegido por permisos de filesystem (0600, owner root) en cada nodo servidor. Para rotarla:

heroctl cluster rekey

Ese comando re-cifra todos los secretos con una clave nueva e invalida la anterior. Hazlo en ventana de mantenimiento — la operación puede demorar minutos en clusters con miles de secretos.

Inyectando en el job

La inyección se hace en el momento en que la asignación inicia. El valor descifrado existe solo en la memoria del proceso, nunca en disco.

job: api-pagamentos
env:
  DATABASE_URL: ${secret.database_url}
  STRIPE_KEY: ${secret.stripe_secret}
  REDIS_PASSWORD: ${secret.redis_password}

El proceso dentro del contenedor ve las variables como cualquier otra. Ningún cambio en el código de la aplicación.

Para inyectar como archivo (útil para claves TLS, certificados, JSON de service account):

job: webhook-handler
files:
  - path: /etc/app/credentials.json
    content: ${secret.gcp_service_account}
    mode: "0400"

El archivo se crea en tmpfs (no toca disco), con el permiso indicado, antes de que el proceso principal arranque.

Versionado y rotación

Toda actualización de un secreto crea una versión nueva. La versión antigua queda disponible por una ventana de tiempo configurable (default 7 días) para casos de rollback.

# atualiza, mantendo histórico
heroctl secret update db_password --from-literal="senha-nova"

# lista versões
heroctl secret history db_password

# faz rollback para versão anterior
heroctl secret rollback db_password --to-version 3

Por defecto, los jobs en ejecución siguen usando la versión que estaba activa cuando arrancaron. Para forzar relectura, redespliega el job:

heroctl job redeploy api-pagamentos

Para un job que necesita siempre la versión más reciente sin redeploy, declara:

env:
  DATABASE_URL: ${secret.database_url:latest}

En ese modo, el cluster reinicia la asignación automáticamente cuando el secreto cambia.

Permisos por rol (Business+)

En el plan Business, ganas control granular sobre quién puede hacer qué con qué secreto. El modelo trabaja con scopes jerárquicos:

policy "deploy-prod-secrets":
  secret:
    path: "prod/*"
    capabilities: ["read", "list"]
  secret:
    path: "prod/admin/*"
    capabilities: []  # nem listar

Asigna la política a un token o a un rol y listo. Ver el documento de RBAC para el modelo completo.

Auditoría (Business+)

Todo acceso queda registrado:

heroctl audit secret --name db_password --since 30d

Salida tabular con timestamp, usuario, acción (read, update, delete), origen (IP) y resultado. Exporta para análisis externo:

heroctl audit secret --since 30d --format json > audit.jsonl

Configura alertas para patrones anómalos: lectura fuera de horario laboral, mismo secreto leído por usuarios diferentes en ventana corta, fallos de permiso repetidos.

Backup cifrado

Los backups de los secretos salen cifrados, listos para enviar a storage S3-compatible:

# config do cluster
backup:
  secrets:
    enabled: true
    schedule: "0 3 * * *"  # 3h da manhã todo dia
    destination:
      type: s3
      bucket: heroctl-backups
      region: us-east-1
      prefix: secrets/
      credentials: ${secret.backup_s3_creds}
    retention_days: 90

El snapshot se cifra con la clave del cluster antes de ser enviado. El bucket puede estar accesible para otros propósitos sin riesgo — quien tenga acceso de lectura no consigue descifrar nada.

Para restaurar en un cluster nuevo, necesitas el snapshot y la clave del cluster original. Guarda la clave separada del storage de backup.

Cofres externos

Para equipos con requisitos de cumplimiento que exigen uso de cofre corporativo ya existente, hay integración opcional:

secret_backend:
  type: aws_kms
  region: us-east-1
  key_id: alias/heroctl-prod

Soportado de fábrica: AWS KMS, GCP KMS, HashiCorp Vault. En ese modo, HeroCtl delega operaciones de cifrado al cofre externo. Los secretos siguen accesibles por la misma sintaxis ${secret.nome}.

Usa esto si ya tienes el cofre operando y auditado. Para la mayoría de los equipos, el backend nativo es suficiente y más simple.

Buenas prácticas

En orden de importancia:

  1. Sin hardcode en spec. Ni en commit, ni temporalmente, ni "solo para test". El hábito filtra.
  2. Separación dev / staging / prod. Usa prefijos en el nombre (dev/db_password, prod/db_password) o clusters separados. Nunca compartas credencial de producción con ambiente de desarrollo.
  3. Rotación periódica. Mínimo cada 90 días para credenciales de base de datos y proveedores. Más frecuente para tokens de API que se filtran fácil.
  4. Least privilege en la lectura. Casi nadie necesita secret:read. La inyección automática en el job no exige que la persona que hizo deploy tenga acceso al valor.
  5. Revisión de auditoría mensual. Mira quién leyó qué. Las anomalías aparecen si se buscan.
  6. No loguees secretos. Configura la aplicación para enmascarar variables sensibles en logs. El cluster cifra en reposo, pero si tu aplicación imprime print(os.environ) en el startup, se acabó.

Próximos pasos

#secretos#seguridad#cifrado#rotacion