Todos los recursos

Que se filtre una API key es algo de rutina, no una catástrofe, siempre que hayas dejado la rotación lista antes de necesitarla. Este es el recorrido completo que sigo en cada contenedor: dónde van de verdad los secretos, el orden emitir-desplegar-verificar-revocar que nunca tumba el tráfico en producción, las trampas de Docker que dejan tus claves grabadas en la imagen para siempre, y un ejemplo paso a paso que puedes copiar. Terminas sabiendo rotar bajo presión sin tener que adivinar.

Rota un secreto filtrado con env y Docker, en el orden seguro y sin downtime

En resumen

  • Rota en el orden seguro: emite la clave nueva, despliégala, verifica que funcione y solo entonces revoca la vieja. Nunca revoques primero.
  • Los secretos van en runtime, no dentro de la imagen. Un ENV en el Dockerfile queda legible en el historial de capas para siempre; pasa los valores con env_file o Docker secrets.
  • Durante la ventana de solape las dos claves son válidas. Esa ventana es tu colchón de seguridad, porque si la clave nueva falla, la vieja sigue atendiendo el tráfico.
  • Verifica antes de revocar. Un health check, una línea de log o un request de prueba contra el contenedor en vivo marca la diferencia entre una rotación limpia y una caída.
  • Toma cualquier fuga como 'da por hecho que la usaron.' Audita a qué pudo llegar la clave, revisa los logs de acceso y rota todo lo que esa clave haya podido alcanzar.

Una credencial filtrada se siente como una emergencia, y es justo ese pánico el que la convierte en una. El instinto es revocar la clave expuesta de inmediato, y ese solo movimiento es el que convierte una rotación tranquila en una caída, porque apenas revocas, cada contenedor en ejecución se queda con una clave muerta. Esta guía te lleva de la mano por la disciplina que hace que rotar sea aburrido: dónde van de verdad los secretos en un montaje con Docker, el orden de cuatro pasos que mantiene el tráfico arriba todo el tiempo, y un ejemplo paso a paso que puedes copiar. Al final vas a poder rotar una clave bajo presión sin adivinar qué paso sigue.

Trabajo así todos los días en Ilustrari (que opera como NexoString): sistemas multiagente y de apoyo a la decisión sobre Anthropic Claude, TypeScript, Next.js, Docker detrás de Traefik, todo en un solo VPS. Es también la misma disciplina que hay detrás de Infuse, el gestor de API keys que construyo, donde rotar es el camino por defecto y no una proeza.

Importante

Requisitos previos: una app en contenedor que controles (docker compose o docker run a secas), poder emitir y revocar claves en el proveedor que vas a rotar (Stripe, Anthropic, una base de datos, un servicio interno), acceso por shell al host donde corre el contenedor, y una forma de confirmar que la app está sana: un endpoint de health, una línea de log, o un request de prueba que sabes que debería funcionar. Si no puedes verificar que la clave nueva sirve antes de revocar la vieja, detente y arma esa comprobación primero; todo el método depende de ella.

01 · Por qué los secretos nunca van dentro de la imagen

Antes de rotar nada, arregla dónde vive el secreto, porque el lugar es lo que decide qué tan doloroso es rotar. El peor sitio donde puedes poner un secreto es grabado en la imagen de Docker con un ENV en el Dockerfile.

La razón es el historial de capas. Una imagen de Docker es una pila de capas inmutables, y una instrucción ENV escribe su valor en esa capa de forma permanente. Cualquiera que haga pull de la imagen, o que corra docker history sobre ella, puede leer el valor de vuelta. Aplanar las capas no lo elimina de forma confiable, y subir la imagen a un registry significa que el secreto viaja con ella. Un secreto grabado en una imagen es, en la práctica, un secreto publicado.

Hay un segundo costo, más silencioso: la fricción. Si la clave vive en la imagen, rotarla implica reconstruir la imagen, volver a subirla y redesplegar. Son minutos de trabajo y un build de CI completo para lo que debería ser un cambio de una sola línea. Esa fricción es justo la razón por la que los equipos posponen la rotación hasta que no les queda otra. Todo el sentido de sacar los secretos de la imagen es hacer que rotar salga tan barato que nadie lo evite.

Dónde van en su lugar

Pasa los secretos en runtime, fuera de git:

  • env_file en compose: un archivo como .env.production listado bajo env_file:, agregado al .gitignore, con los valores reales. Compose los inyecta como variables de entorno cuando el contenedor arranca.
  • Docker secrets: para Swarm o compose con soporte de secrets, el valor se monta como archivo en /run/secrets/ y la app lo lee al arrancar. No queda nada en el entorno, que es la opción más sólida.
  • Un gestor de secretos: trae el valor desde un gestor al arrancar (esto es lo que hace Infuse), para que el host nunca guarde en disco una copia de larga duración.
# MAL — la clave queda en el historial de capas para siempre.
ENV ANTHROPIC_API_KEY=sk-ant-xxxxxxxx

# BIEN — el Dockerfile no nombra nada secreto.
# El valor llega en runtime vía env_file o docker secrets.
# (Ninguna línea ENV con secreto, en absoluto.)

Atención

Si un secreto se escribió alguna vez en una línea ENV del Dockerfile, en un ARG pasado en build, o se imprimió en un log de build, dalo por filtrado. Se puede recuperar desde la imagen y desde el historial de CI. Rótalo como parte de moverlo a runtime; no basta con borrar la línea y cruzar los dedos.

02 · El orden seguro para rotar

Rotar son cuatro pasos, y el orden es todo el truco. El error está en reordenarlos bajo presión.

  1. Emite una credencial nueva en el proveedor. Lo importante: lo haces sin tocar la vieja, porque casi todos los proveedores permiten varias claves válidas a la vez. Ahora sirven tanto la vieja como la nueva. Ese solape es tu colchón de seguridad.
  2. Despliega el valor nuevo al contenedor en ejecución. Con Docker los secretos vienen del entorno o de un archivo montado, así que actualizas tu env_file (o tu almacén de secretos) y recreas el contenedor para que tome el cambio.
  3. Verifica que la app de verdad esté usando la clave nueva: un health check, una línea de log, un request de prueba real que ejercite la credencial. No te saltes esto. Es el paso que hace seguro el siguiente.
  4. Revoca la clave vieja, y apenas ahora. Si la clave nueva dio problemas, la vieja estuvo en vivo durante los pasos 2 y 3, así que el tráfico nunca se detuvo.

Nota

La ventana de solape del paso 1 es lo que hace esto sin downtime. Mientras las dos claves sean válidas, puedes desplegar y verificar la nueva sin presión, y hacer rollback al instante apuntando de vuelta al valor viejo. Revoca primero y desperdiciaste ese colchón justo antes de necesitarlo más que nunca.

Recrear el contenedor

Actualizar el env_file en disco no hace nada por sí solo. Un contenedor en ejecución conserva el entorno con el que arrancó. Tienes que recrearlo para que lea los valores nuevos:

# Edita el valor primero:
#   nano .env.production   (pon la clave nueva)
# Luego recrea solo el servicio afectado, tomando el env nuevo:
docker compose up -d --force-recreate web

# Confirma que el contenedor reinició y está sano:
docker compose ps web
docker compose logs --tail=50 web

El flag --force-recreate importa: sin un tag de imagen distinto, compose puede asumir que nada cambió y dejar corriendo el contenedor viejo, con la clave vieja todavía en memoria. Forzar la recreación garantiza un contenedor nuevo que lee el env actualizado.

03 · Verifica antes de revocar

Este es el paso que la gente se salta cuando anda apurada, y es justo el que te protege. "Desplegado" no es lo mismo que "funcionando." El contenedor puede arrancar, pasar un health check superficial y aun así estar usando una clave vieja o equivocada.

Verifica contra la credencial en sí, no solo contra el proceso:

  • Un endpoint de health que ejercite la dependencia. Una ruta /health que de verdad llame al servicio downstream con la clave nueva te dice mucho más que una que devuelve 200 sin condiciones.
  • Una línea de log al arrancar. Loguea un fingerprint no secreto, los últimos cuatro caracteres de la clave o su fecha de emisión, para confirmar a simple vista cuál clave está cargada sin imprimir nunca el secreto.
  • Un request de prueba dirigido. Llama a un endpoint real que use la clave y confirma que la respuesta downstream sea exitosa.
# Ejercita la dependencia a través del contenedor corriendo.
curl -fsS http://localhost:8080/health/anthropic
# esperado: {"ok":true,"keyFingerprint":"...x4f2"}

# O revisa el log estructurado de arranque por el fingerprint:
docker compose logs web | grep "key loaded"
# esperado: una línea con los últimos 4 / la fecha de la clave NUEVA

Consejo

Loguea un fingerprint, nunca la clave. Los últimos cuatro caracteres más la fecha de emisión alcanzan para confirmar que está cargada la credencial correcta y para distinguir la vieja de la nueva durante una rotación, y no le sirven de nada a un atacante que lea el log. Una clave completa en un log no es más que otra fuga esperando a ocurrir.

Si la verificación falla, no perdiste nada: la clave vieja sigue válida en el proveedor, así que revierte el env_file al valor anterior, recrea el contenedor otra vez y vuelves a un estado conocido y bueno. Diagnostica la clave nueva con calma y luego reintenta la rotación desde el paso 2.

04 · Ejemplo paso a paso: rotar una clave de Anthropic detrás de Traefik

Aquí va un caso concreto, del tipo que hago en el VPS. La app es un servicio de Next.js detrás de Traefik que lee ANTHROPIC_API_KEY del env_file. Una clave terminó pegada en un hilo de Slack, así que está comprometida.

  1. Emite una clave nueva en la consola de Anthropic. Deja la vieja activa por ahora. Existen dos claves válidas.
  2. Despliega. Edita .env.production con el valor nuevo y recrea solo el servicio web para no tocar el tráfico de los demás servicios.
  3. Verifica. Hazle curl a la ruta de health que llama a Claude con la clave cargada; confirma que el fingerprint del log de arranque coincida con la clave nueva.
  4. Revoca. Borra la clave vieja en la consola. Lanza un request de verificación más para confirmar que la app no se ve afectada. No debería, porque viene usando la clave nueva desde el paso 2.
  5. Audita. Toma la clave filtrada como usada: revisa los logs de uso del proveedor en busca de llamadas que no reconozcas, mira el gasto y deja registrado el incidente.
# 2) despliega el valor nuevo al stack corriendo
docker compose up -d --force-recreate web
docker compose ps web                       # ¿sano?

# 3) verifica que la dependencia de verdad funciona con la clave nueva
curl -fsS https://app.example.com/health/anthropic
docker compose logs web | grep "key loaded" # ¿fingerprint == clave nueva?

# 4) solo ahora: revoca la clave VIEJA en la consola del proveedor,
#    y luego comprueba que la app viva no se afecta
curl -fsS https://app.example.com/health/anthropic   # sigue ok

Trampas comunes que pegan en producción

  • Revocar primero. La causa más común de una caída por rotación. Emite y despliega siempre antes de revocar; la ventana de solape existe justo para esto.
  • Editar el env sin recrear. Un contenedor en ejecución conserva su entorno original. Cambiar el archivo y no recrear significa que la clave vieja sigue viva, y vas a "verificar" un éxito falso contra un contenedor que nunca cambió.
  • Verificar el proceso, no la credencial. Un 200 de una ruta de health que no llama a la dependencia te dice que el contenedor está arriba, no que la clave sirve. Ejercita el servicio downstream real.
  • El secreto grabado en la imagen. Si es un ENV en el Dockerfile, recrear no ayuda. Tienes que reconstruir y volver a subir, y el valor viejo se queda en el historial del registry. Muévelo a runtime como parte de la rotación.
  • Filtrar la clave nueva al verificar. Imprimir la clave completa para confirmar que cargó solo crea una fuga nueva en tus logs. Loguea un fingerprint en su lugar.
  • Saltarte la auditoría. Una clave rotada cierra la puerta, pero todavía no sabes qué hizo la clave filtrada mientras estuvo válida. Da por hecho que la usaron y revisa.

Una rotación bien hecha no llama la atención: emites, despliegas, verificas, revocas, y el tráfico en vivo ni se entera. Todo el método se apoya en dos hábitos. Mantén el secreto fuera de la imagen para que el cambio salga barato, y mantén las dos claves válidas hasta que la nueva esté probada para que una falla sea recuperable. Arma el paso de verificación una sola vez y rotar deja de ser una emergencia para volverse un martes cualquiera.

Puntos clave

  • Rota en el orden seguro, emite luego despliega luego verifica luego revoca, para que el tráfico en vivo nunca cargue una clave muerta.
  • Mantén los secretos fuera de la imagen. Un ENV en el Dockerfile los graba en el historial de capas para siempre; pásalos en runtime vía env_file o Docker secrets.
  • Editar el archivo env no cambia nada hasta que recreas el contenedor con --force-recreate.
  • Verifica contra la credencial en sí antes de revocar, y loguea un fingerprint en vez de la clave para que verificar nunca se vuelva una fuga nueva.
  • Toma cada fuga como 'da por hecho que la usaron': rota y luego audita a qué pudo llegar la clave y revisa el uso del proveedor.

Preguntas frecuentes

¿Por qué no revocar la clave filtrada de inmediato para cortar la hemorragia?

Porque apenas revocas, cada contenedor en ejecución se queda con una clave muerta y tu tráfico en vivo empieza a fallar. Convertiste una fuga en una caída. Que la clave filtrada siga válida unos minutos más es un riesgo menor que tumbar todo el servicio, sobre todo porque el atacante, si tiene la clave, ya la tiene. Emite y despliega la clave nueva primero, verifícala y luego revoca; los últimos minutos de vida de la clave vieja son tu colchón de seguridad, no tu enemigo.

Cambié el archivo env pero el contenedor sigue usando la clave vieja. ¿Por qué?

Un contenedor en ejecución conserva el entorno con el que arrancó, así que editar el archivo en disco no llega a un proceso ya vivo. Tienes que recrear el contenedor para que lea los valores nuevos, y por lo general necesitas --force-recreate, porque sin un tag de imagen distinto compose puede asumir que nada cambió y dejar el contenedor viejo corriendo. Corre docker compose up -d --force-recreate <servicio> y luego confirma con los logs que la clave nueva (su fingerprint) está cargada.

¿Está bien alguna vez usar ENV en un Dockerfile?

Sí, para configuración que no sea secreta. Poner NODE_ENV, un puerto por defecto, una base URL pública o un feature flag con ENV está perfecto, porque nada de eso es sensible. La regla es bien acotada: nunca pongas un secreto (API key, password, token, connection string con credenciales) en ENV o ARG, porque queda grabado en el historial de capas y viaja con la imagen a cualquier registry. Los secretos entran en runtime vía env_file, Docker secrets o un gestor de secretos.

¿Cuál es la diferencia entre env_file y Docker secrets, y cuál uso?

Con env_file, los valores se vuelven variables de entorno dentro del contenedor: simple, muy soportado y suficiente para la mayoría de los montajes de un solo VPS, siempre que el archivo esté en gitignore y con permisos cerrados en el host. Docker secrets monta el valor como archivo en /run/secrets/ y nunca lo expone como variable de entorno, lo que es más sólido porque las env vars pueden filtrarse por listados de procesos, crash dumps y procesos hijos. Empieza con env_file por lo sencillo; pásate a Docker secrets (o a un gestor de secretos) cuando el secreto sea de alto valor o estés en Swarm.

La clave se filtró pero los logs se ven limpios. ¿Igual necesito rotar?

Sí. 'Los logs se ven limpios' significa que no viste el abuso, no que no lo hubo. Los logs de uso van con retraso, muestrean o simplemente se les escapan cosas, y un atacante que sabe lo que hace pasa desapercibido. Toma cualquier credencial filtrada como comprometida y da por hecho que la usaron: rótala y luego audita a qué pudo llegar y revisa el uso y el gasto del proveedor. Rotar sale barato una vez que lo montaste así; apostar a que nadie lo notó, no.

¿Cómo verifico la clave nueva sin imprimirla en los logs?

Loguea un fingerprint, no el secreto. Al arrancar, loguea los últimos cuatro caracteres de la clave más su fecha de emisión. Eso alcanza para confirmar a simple vista cuál credencial está cargada y para distinguir la vieja de la nueva a media rotación, sin servirle de nada a quien lea el log. Como prueba funcional, llama a un endpoint de health que de verdad llame al servicio downstream con la clave cargada y devuelva un estado no secreto; un curl contra esa ruta confirma que la credencial sirve sin exponerla nunca.

¿Prefieres que lo hagamos por ti?

Esto mismo lo construimos para negocios como el tuyo. La primera conversación es gratis y sin compromiso.

Escríbenos por WhatsApp

Escríbenos por WhatsApp

Escanéalo con tu teléfono para escribirnos por WhatsApp.

Escanéalo con tu teléfono para escribirnos por WhatsApp.

¿Estás desde el teléfono y no puedes escanear? Escríbenos a info@ilustrari.com

Primera conversación gratis. Te responde el fundador.

Recursos relacionados

GuíaSupabase

Gestión de secretos que sobrevive a una filtración

Los secretos se filtran: por un env file que alguien subió al repo, por un bundle público, por una línea de log demasiado parlanchina. La pregunta nunca es si alguno se va a escapar, sino cuánto daño puede hacer una sola clave filtrada. Esta es la disciplina con la que trabajo: dónde van de verdad los secretos, cómo acotar una clave para que no te vacíe una cuenta, por qué conviene centralizar en vez de regar copias, y cómo convertir la rotación en un hábito de un solo paso. Terminas sabiendo diseñar pensando en la fuga, en lugar de fingir que nunca va a pasar.

21 abr 202612 min de lectura
ArtículoInfuse

Secretos para flotas de agentes: credenciales de vida corta, mínimo privilegio y nunca en el prompt

Una sola API key estática compartida entre toda una flota de agentes es una brecha que tarde o temprano va a pasar. Estos son los patrones que usamos para acotar, rotar y auditar credenciales, de modo que cuando un secreto se filtre, el daño sea mínimo.

3 jun 202612 min de lectura
SkillClaude Code

Skill de auditoría de seguridad: una revisión repetible de tu código antes de que se te complique

Un skill de auditoría de seguridad le da siempre la misma revisión aburrida y estructurada a tu código (secretos filtrados, authz que falta, inyección, cripto débil) y te devuelve una lista priorizada con archivo:línea y una etiqueta de confianza. Aquí te explico cómo armar uno que de verdad ayude en vez de ahogarte en hallazgos de «quizás deberías revisar esto».

31 may 202611 min de lectura