Módulo 5: Credential And Secrets Security

8. Proyecto: endurecer credenciales y secretos

Descripción

Al terminar esta lección vas a haber transformado la instancia de Terra Market de un llavero desordenado a un llavero gobernado, en seis fases con entregables verificables. Vas a levantar el inventario completo y correr la auditoría; vas a comprobar —no suponer— que la llave de cifrado está respaldada y que restaura; vas a reducir erp_api de administrador a su alcance mínimo sin romper producción; vas a sacar los secretos que quedaron escritos en los workflows; vas a endurecer la instancia a nivel de aplicación con la revisión de acceso y el recorte de superficie; y vas a poner el presupuesto, las alertas y el runbook de llm_token. Al final vas a tener seis artefactos que se pueden enseñar y una instancia que es materialmente más difícil de comprometer, no solo mejor documentada.

Esto importa porque es donde el módulo deja de ser conocimiento. Las siete lecciones anteriores te dieron modelo mental, procedimientos y honestidad de planes; ninguna cambió una sola credencial. Este proyecto sí. Y está diseñado para ejecutarse sobre una instancia que está corriendo, con 4.000 ejecuciones diarias que no se pueden detener, porque esa es la condición real de cualquier operación en producción y es lo que separa un ejercicio de laboratorio de un trabajo defendible.

Conexión con el módulo: este proyecto cierra el módulo 5 y usa las siete lecciones en orden. La fase 1 es el inventario de la lección 1 más la auditoría de la 3. La fase 2 aplica el modelo de almacenamiento de la lección 2 —y remite a la guía de Self-Hosting y Operaciones, módulo 3, lección 5 para el montaje de la llave, que no se repite aquí—. La fase 3 es el procedimiento de mínimo privilegio y rotación de la lección 3. La fase 4 aplica la regla de la lección 5 —los secretos van en credenciales, el código no ve nada— con la decisión honesta de la lección 4 ya tomada. La fase 5 es la lección 6 completa, a nivel de aplicación, con el endurecimiento de red delegado a la guía de Self-Hosting, módulo 4, lección 6. Y la fase 6 es la lección 7. Hacia adelante, el módulo 6 va a escalar esta instancia con modo cola —donde la llave de cifrado que aseguraste en la fase 2 tiene que llegar a todos los workers— y el módulo 7 va a afinar el presupuesto de IA que pones en la fase 6.

El objetivo, en una frase

No estás comprando "seguridad" en abstracto. Estás comprando dos cosas concretas y medibles:

Primera: que el peor caso posible sea más chico. Hoy, si la llave del ERP de Terra Market se filtra, el peor caso es que alguien borre o modifique el sistema que gobierna pedidos, inventario y precios de la empresa. Al terminar, el peor caso será que alguien lea inventario y cree pedidos falsos. Sigue siendo malo, y es un orden de magnitud menos malo. Eso es la defensa 3 de la lección 1 hecha realidad.

Segunda: que las cosas dejen de depender de que alguien se acuerde. Hoy la rotación ocurre cuando alguien lo piensa, el gasto de IA se revisa cuando llega la factura, y las cuentas de exempleados se borran si alguien lo nota. Al terminar, cada una tendrá un dueño con nombre, una fecha y —donde se pueda— un mecanismo que avisa solo.

Y una nota antes de empezar: este proyecto no se hace en una tarde, y no debe hacerse en una tarde. Las fases 1, 2, 5 y 6 caben en un día. Las fases 3 y 4 tocan workflows que están corriendo, y su procedimiento correcto incluye periodos de observación. Un ritmo razonable es dos semanas, con la mayor parte del tiempo siendo espera, no trabajo. Si tu instancia es de práctica, puedes comprimirlo; si es producción, respeta las esperas.

Cada fase es un ejemplo trabajado completo y tiene la misma estructura: 📍 Dónde estás, el objetivo, los pasos con su Qué esperar, y el entregable. Y esto vale para las seis: tú ejecutas todo en tu instancia, en tu servidor y en los paneles de tus proveedores. La guía no ejecuta nada.


Fase 1 — Inventario y auditoría

📍 Dónde estás: al principio. No has cambiado nada todavía, y está bien: la primera fase no cambia nada a propósito. Lo que no se conoce no se puede proteger, y en una instancia heredada casi nada se conoce.

Objetivo de la fase: tener la lista completa y honesta de qué credenciales existen, quién las usa, qué permisos tienen, cuándo se rotaron y quién responde por ellas.

Paso 1 — Lista las credenciales de la instancia

En la interfaz de n8n, abre la sección de credenciales y anota todas: nombre, tipo, identificador.

Qué esperar: una lista más larga de lo que recordabas, con al menos una credencial cuyo propósito nadie recuerda. Es normal, y es justamente el hallazgo.

Paso 2 — Corre la auditoría integrada

Según la documentación, tienes tres caminos. El más directo desde el servidor:

n8n audit

Los otros dos: una llamada POST al endpoint /audit de la API autenticándote como dueño de la instancia, o el nodo n8n dentro de un workflow con Resource > Audit y Operation > Generate.

Qué esperar: un reporte con cinco secciones —credenciales, base de datos, sistema de archivos, nodos e instancia—. Guárdalo tal cual, con fecha. Es tu línea base, y en la fase 5 vas a volver a él.

De este reporte, para esta fase te interesa la sección de credenciales, que según la documentación señala tres cosas: credenciales que ningún workflow usa, que ningún workflow activo usa, y que ningún workflow activo recientemente usa.

Paso 3 — Responde las cuatro preguntas por credencial

Para cada fila: qué workflows la usan, qué permisos tiene en el sistema externo (esta se responde entrando al panel de ese sistema, no en n8n), cuándo se rotó y quién es su dueño.

Qué esperar: varias celdas con "por confirmar" y la columna de dueño vacía en todas las filas. Escribe los huecos como huecos: un inventario honesto con seis "por confirmar" vale más que uno completo pero inventado, porque cada hueco es una tarea y una suposición es una falsa tranquilidad.

Paso 4 — Asigna dueños

Cada credencial necesita una persona con nombre responsable de rotarla, decidir quién la usa y responder cuando aparezca algo raro. "El equipo de operaciones" no es un dueño.

Qué esperar: una conversación de veinte minutos y una columna que se llena. Y la regla que Terra Market adopta desde hoy: una credencial sin dueño no entra a producción.

Entregable 1 — INVENTARIO-CREDENCIALES.md

INVENTARIO DE CREDENCIALES — Terra Market — producción
Fecha: <hoy>   ·   Levantado por: <nombre>   ·   Próxima revisión: <hoy + 3 meses>

| Credencial  | Tipo        | La usan                      | Permisos que TIENE   | Permisos que USA | Distancia | Última rotación | Dueño        |
|-------------|-------------|------------------------------|----------------------|------------------|-----------|-----------------|--------------|
| erp_api     | Header Auth | order-sync, inventory-update | Administrador (todo) | Leer + escribir  | ENORME    | Nunca           | <nombre>     |
| carrier_api | Header Auth | shipment-notify              | Lectura de envíos    | Lectura          | ninguna   | Hace 14 meses   | <nombre>     |
| llm_token   | API Key     | 2 workflows de clasificación | Todos los modelos    | Un modelo        | media     | Nunca           | <nombre>     |
| store_api   | OAuth2      | order-sync, inventory-update | Por confirmar        | Leer + escribir  | ?         | Por confirmar   | <nombre>     |
| smtp_notify | SMTP        | shipment-notify              | Enviar correo        | Enviar correo    | ninguna   | Hace 8 meses    | <nombre>     |
| (huérfana)  | Header Auth | ninguno                      | Por confirmar        | —                | ?         | Por confirmar   | (a revocar)  |

HALLAZGOS
  H1  erp_api con permisos de administrador y distancia enorme        → Fase 3
  H2  1 credencial huérfana que ningún workflow usa                   → Fase 1, paso 5
  H3  store_api con permisos sin confirmar                            → tarea abierta
  H4  llm_token sin tope de gasto ni alerta                           → Fase 6
  H5  Ninguna credencial tenía dueño (corregido en esta fase)         → cerrado

Paso 5 — Cierra lo barato: las huérfanas

Antes de pasar a la fase 2, resuelve el hallazgo más barato. Pregunta al equipo con fecha límite —"si nadie reclama estas credenciales para el viernes, las revoco"—, y después, en este orden: revoca la llave en el sistema externo, y luego borra la credencial de n8n.

El orden importa: borrar la credencial de n8n sin revocar la llave deja la llave viva en el sistema externo y ya sin registro de que existía. Es lo peor de los dos mundos.

Qué esperar: una o dos credenciales menos y un riesgo eliminado en veinte minutos.


Fase 2 — La llave de cifrado, verificada

📍 Dónde estás: tienes el inventario y ya eliminaste las huérfanas. Ahora vas a asegurar la pieza de la que dependen todas las credenciales de la lista: la llave que las cifra. Si esta falla, el inventario entero se vuelve ilegible.

Objetivo de la fase: comprobar —no suponer— que la llave de cifrado existe de forma explícita, que está respaldada fuera del servidor, y que las credenciales sobreviven a un reinicio.

Frontera. El montaje de la llave —generarla con openssl, colocarla en el .env antes del primer arranque, qué pasa exactamente si se pierde o se cambia— está desarrollado a fondo en la guía de Self-Hosting y Operaciones, módulo 3, lección 5, y no se repite aquí. Esta fase verifica que ese trabajo esté bien hecho en tu instancia y añade lo que corresponde a este módulo.

Paso 1 — Confirma que la llave es explícita

Revisa el .env de tu stack y confirma que N8N_ENCRYPTION_KEY está presente con un valor real.

Qué esperar: una línea con una cadena larga y aleatoria. Si la variable no está, tu instancia está usando la llave automática que n8n genera y guarda en ~/.n8n —según la documentación— y tienes un problema importante: no conoces tu llave, así que no la puedes respaldar. Ese caso se resuelve con el procedimiento de la guía de Self-Hosting; no lo improvises, porque cambiar la llave sobre credenciales ya cifradas las vuelve ilegibles.

Paso 2 — Verifica el respaldo, de verdad

Esta es la parte de esta fase que aporta valor nuevo, y no consiste en preguntar si hay respaldo: consiste en usarlo.

Ve al lugar donde el equipo dice que está respaldada la llave —el gestor de contraseñas, la bóveda del equipo— y compárala carácter por carácter con la del .env.

Qué esperar: que coincidan exactamente. Si no coinciden, o si no encuentras la entrada, acabas de descubrir el hallazgo más grave posible: tu respaldo de base de datos no restaura nada, porque es una caja fuerte sin combinación. Corrígelo hoy, copiando la llave del .env al gestor con una etiqueta clara.

Paso 3 — Prueba que las credenciales sobreviven a un reinicio

docker compose down
docker compose up -d

Entra a n8n y abre cualquier credencial. Después ejecuta manualmente uno de los workflows.

Qué esperar: las credenciales siguen ahí y el workflow se autentica sin problemas. Esa es la confirmación de que la llave del .env es la misma con la que se cifraron. Si vieras errores de "no se pudo descifrar la credencial", detente: alguien cambió la llave en algún momento, y el procedimiento de recuperación está en la guía de Self-Hosting.

Paso 4 — Decide sobre la rotación de llaves de cifrado

Según la documentación, n8n ofrece rotación de llaves de cifrado en instancias self-hosted, habilitada por el dueño con N8N_ENV_FEAT_ENCRYPTION_KEY_ROTATION=true en todas las instancias (proceso principal y workers). El modelo es de dos llaves: la de instancia protege a la llave de datos, que es la que se rota.

Y las advertencias, que la documentación marca con claridad: es un cambio de una sola dirección, exige un respaldo completo de la base de datos antes, y una vez habilitada, desactivar la variable o bajar de versión deja los datos permanentemente inaccesibles.

Para este proyecto, la decisión razonable es documentar la decisión, no ejecutarla a la ligera. Si Terra Market la quiere, se agenda en una ventana con respaldo verificado. Lo que sí produce esta fase es la decisión escrita, con fecha y responsable.

Entregable 2 — RESPALDO-LLAVE-CIFRADO.md

VERIFICACIÓN DE LA LLAVE DE CIFRADO — Terra Market
Fecha: <hoy>   ·   Verificado por: <nombre>

[x] N8N_ENCRYPTION_KEY explícita en el .env (no automática)
[x] Respaldo localizado en <gestor de contraseñas del equipo>, entrada "<etiqueta>"
[x] Comparación carácter por carácter: COINCIDE
[x] Prueba de reinicio: credenciales descifran correctamente
[x] Ejecución manual de order-sync tras el reinicio: OK
[x] El .env NO está en el repositorio (verificado con git status)

ROTACIÓN DE LLAVES DE CIFRADO
    Estado: no habilitada
    Decisión: <habilitar en la ventana del <fecha> / no habilitar por ahora>
    Responsable: <nombre>
    Requisito previo: respaldo completo de la base de datos, verificado restaurando

RECORDATORIO DE OPERACIÓN
    En modo cola (módulo 6), TODOS los workers deben recibir la misma
    N8N_ENCRYPTION_KEY. Verificar al escalar.

Fase 3 — Mínimo privilegio en erp_api

📍 Dónde estás: el inventario está hecho y la llave que cifra todo está verificada. Ahora vas al hallazgo más grave: la credencial con permisos de administrador que solo necesita leer y crear pedidos. Esta es la fase que más reduce el peor caso, y la que más cuidado requiere porque toca workflows en producción.

Objetivo de la fase: que erp_api pase de administrador a su alcance mínimo, sin que order-sync ni inventory-update fallen ni una ejecución.

Paso 1 — Inventaria las operaciones reales

Recorre los dos workflows y anota cada llamada al ERP: método y ruta.

Operaciones observadas de erp_api

order-sync
  POST /api/v1/orders             → crear pedido
  GET  /api/v1/customers/{id}     → leer cliente

inventory-update
  GET  /api/v1/inventory          → leer existencias
  GET  /api/v1/products/{sku}     → leer producto

Total: 1 escritura, 3 lecturas. Cero borrados. Cero configuración.

Qué esperar: una lista corta. Si tienes observabilidad de las llamadas salientes (módulo 4), cruza esta lista contra lo que se llamó de verdad en el último mes: cubre las ramas que solo se ejecutan en casos raros y que leer el workflow no revela.

Paso 2 — Traduce al vocabulario del ERP

Alcance mínimo propuesto:
  orders:write · customers:read · inventory:read · products:read

Se elimina respecto del rol de administrador:
  orders:delete, inventory:write, products:write, customers:write,
  settings:*, users:*, y todo lo demás del sistema.

Paso 3 — Crea una credencial NUEVA, no modifiques la vieja

En el ERP, genera una llave con el alcance del paso 2. En n8n, crea una credencial nueva con ella, llamada erp_api_scoped. Si el ERP permite cuentas de servicio, aprovecha para crearla desde una identidad dedicada —svc-n8n-erp— en vez de desde la cuenta personal de alguien.

Qué esperar: dos credenciales en la lista y ningún workflow usando la nueva todavía. Ese estado intermedio es correcto: la llave vieja intacta es tu vuelta atrás y no cuesta nada tenerla.

Paso 4 — Migra el workflow de menor riesgo y observa

Cambia solo inventory-update —que únicamente lee— para que use erp_api_scoped.

Qué esperar: las ejecuciones siguen en verde y las existencias se siguen actualizando en la tienda. Si algo falla, verás un error de autorización del ERP (típicamente un 403) que te dice qué operación falta. Eso no es un fracaso: es el descubrimiento de una operación que tu inventario del paso 1 no incluía. Agrega ese alcance a la llave nueva y vuelve a probar.

Deja pasar al menos un ciclo completo de operación antes de seguir.

Paso 5 — Migra order-sync y observa mejor

Ahora el que escribe. Cambia sus nodos a erp_api_scoped.

Qué esperar: ejecuciones en verde y —esto es lo que hay que verificar de verdad— los pedidos llegando al ERP. Una llamada que devuelve 200 sin crear nada es un fallo silencioso; el módulo 3 te dio las herramientas para detectarlo.

Paso 6 — Revoca la llave vieja

Con los dos workflows estables, entra al ERP y revoca la llave anterior. No la desactives "por si acaso": revócala. Después borra la credencial erp_api de n8n.

Qué esperar: las ejecuciones siguen en verde, porque nadie usaba la vieja desde el paso 4. Y un beneficio extra que cierra el hallazgo 1 del módulo: la llave que ocho personas tenían en su chat acaba de dejar de funcionar, sin que hiciera falta pedirle a nadie que la borrara. Si algo se cayera al revocar, acabas de descubrir un consumidor de esa llave fuera de n8n, y eso es un hallazgo valioso por sí solo.

Entregable 3 — Actualización del inventario y bitácora

BITÁCORA DE REDUCCIÓN DE ALCANCE — erp_api
<fecha>  Operaciones inventariadas: 1 escritura, 3 lecturas
<fecha>  Llave nueva creada en el ERP con alcance mínimo (svc-n8n-erp)
<fecha>  inventory-update migrado a erp_api_scoped
<fecha>  Observación 48 h: sin incidencias
<fecha>  order-sync migrado; pedidos verificados en el ERP
<fecha>  Observación 48 h: sin incidencias
<fecha>  Llave antigua REVOCADA en el ERP · credencial erp_api borrada de n8n
         Efecto colateral buscado: la llave que circuló por chat quedó inservible

| Credencial     | Permisos que TIENE                                          | Permisos que USA | Distancia | Última rotación | Dueño    |
|----------------|-------------------------------------------------------------|------------------|-----------|-----------------|----------|
| erp_api_scoped | orders:write, customers:read, inventory:read, products:read | los mismos       | ninguna   | <fecha>         | <nombre> |

Fase 4 — Sacar los secretos de los workflows

📍 Dónde estás: las credenciales están inventariadas, la llave que las cifra está verificada, y la más peligrosa ya está acotada. Pero todo ese trabajo se anula si un secreto está escrito fuera de una credencial: en un campo de un nodo, en un nodo Code, o —el peor caso— en un item que queda guardado en el historial de ejecuciones.

Objetivo de la fase: que ningún secreto de Terra Market viva fuera de una credencial de n8n o del .env, y que ninguno pase por los datos.

Paso 1 — Busca secretos escritos a mano en los workflows

Exporta los workflows y busca en su JSON cadenas que parezcan llaves: en campos de cabeceras, en parámetros de consulta, en URLs, en nodos Edit Fields, en nodos Code.

# Exporta los workflows a archivos para poder buscar en su texto.
n8n export:workflow --all --output=/tmp/wf-review.json

Qué esperar: con suerte, nada. Lo habitual es encontrar uno o dos, casi siempre en una API que autentica de forma poco estándar y donde alguien pegó la llave en el campo de cabeceras "por ahora".

Qué hacer con cada hallazgo. La corrección es una credencial genérica: Header Auth si el secreto va en una cabecera, Query Auth si va como parámetro de URL. Y el reparto correcto: lo secreto a la credencial, lo no secreto —un número de cuenta, una URL base— al campo del nodo. En Terra Market, carrier_api es el caso típico: la llave va en la credencial de tipo Header Auth y el número de cuenta va como parámetro normal.

Y una advertencia importante: si encontraste un secreto en un workflow que está versionado, ese secreto se considera comprometido, porque Git no olvida. Además de moverlo a una credencial, hay que rotarlo.

Paso 2 — Busca secretos que pasan por los datos

Abre ejecuciones recientes de cada workflow y recorre las salidas de los nodos buscando cadenas que parezcan llaves. Presta atención especial a los nodos Code y a los Edit Fields.

Qué esperar: cualquier campo del estilo auth_header, token, api_key o Bearer … en la salida de un nodo es un hallazgo. Recuerda de la lección 2: el historial de ejecuciones no está cifrado, así que un secreto ahí está en texto, replicado en cada ejecución.

La corrección no es ocultarlo mejor: es sacarlo del código. En n8n 2.0 el nodo Code no hace llamadas HTTP, así que nunca necesita el token. Quien llama es el nodo HTTP Request o el nodo del servicio, con su credencial.

// ============================================================
// Nodo: Code — "Build ERP payload"
// Modo: Run Once for All Items
//
// ENTRADA:  pedidos de la tienda
// SALIDA:   un item por pedido, listo para el ERP
// NOTA:     este nodo NO conoce ningún token ni ninguna URL.
//           Los resuelve el nodo HTTP Request siguiente con la
//           credencial erp_api_scoped.
// ============================================================

return $input.all().map((item, i) => ({
  json: {
    external_id: item.json.order_id,
    customer: item.json.customer_name,
    total: item.json.order_total,
  },
  pairedItem: i,
}));

Qué esperar tras la corrección: el panel de salida muestra los datos del pedido y ninguna cadena que se parezca a una llave. Esa ausencia es la señal de éxito.

Y si encontraste secretos en el historial, la corrección no termina en el workflow: borra las ejecuciones afectadas y rota el secreto, porque estuvo visible.

Paso 3 — Protege el .env

Verifica las tres piezas de la lección 5:

# .gitignore
.env
.env.*
!.env.example

Confirma que existe el .env.example con los nombres de las variables y sin valores, y adopta el reflejo: git status antes de cada commit, confirmando que ningún .env aparece en la lista.

Qué esperar: que git status no muestre ningún .env. Si tu repositorio ya tuvo uno en algún commit, el secreto se considera comprometido y hay que rotarlo: borrarlo del último commit no lo borra del historial.

Paso 4 — Verifica el comportamiento de $env en tu instancia

Recuerda la contradicción de la lección 5: la página de variables de seguridad de la documentación lista N8N_BLOCK_ENV_ACCESS_IN_NODE con valor por defecto false, y la página de cambios de la versión 2.0 dice que pasó a true. Las dos son oficiales. La instancia decide.

// Nodo Code de diagnóstico. Ejecútalo UNA VEZ y borra el nodo.
return [{ json: { env_access: typeof $env } }];

Qué esperar: "undefined" o un error de acceso significa que el bloqueo está activo, que es la postura segura. "object" significa que está permitido, y eso es un hallazgo: cualquiera con permiso de edición puede volcar el entorno completo del proceso —incluida la llave de cifrado— en el panel de salida. En ese caso, fíjalo explícitamente en true en lugar de depender de un valor por defecto ambiguo. Y borra el nodo después de la prueba: si el acceso resultara permitido, dejarlo guardado deja el volcado en el historial.

Entregable 4 — AUDITORIA-SECRETOS-EN-WORKFLOWS.md

SECRETOS FUERA DE CREDENCIALES — Terra Market
Fecha: <hoy>   ·   Revisado por: <nombre>

BÚSQUEDA EN EL JSON DE LOS 18 WORKFLOWS
  Hallazgos: <N>
  <workflow> · <nodo> · <campo>  → movido a credencial <tipo>  · ROTADO: sí/no

BÚSQUEDA EN EL HISTORIAL DE EJECUCIONES
  Hallazgos: <N>
  <workflow> · <nodo> · <campo>  → nodo corregido · ejecuciones borradas · ROTADO: sí/no

PROTECCIÓN DEL .env
  [x] .gitignore cubre .env y .env.* con excepción para .env.example
  [x] .env.example presente, sin valores
  [x] git status limpio de .env
  [ ] El repositorio tuvo un .env en el historial → si sí, rotar TODO lo que contenía

COMPORTAMIENTO DE $env EN ESTA INSTANCIA
  Resultado de `typeof $env` en nodo Code: <undefined / object>
  Decisión: N8N_BLOCK_ENV_ACCESS_IN_NODE fijado explícitamente en <true/false>
  Nota: la documentación oficial se contradice sobre el valor por defecto.
        Este resultado es el de NUESTRA instancia y manda sobre la doc.

Fase 5 — Endurecer la instancia a nivel n8n

📍 Dónde estás: los secretos están donde deben, con el alcance que deben, y no pasan por los datos. Falta la otra mitad: quién puede llegar a ellos desde dentro de la aplicación.

Objetivo de la fase: que solo las personas que deben tengan cuenta, que las cuentas estén protegidas, y que la superficie de la aplicación sea la mínima.

Frontera. El endurecimiento de red y servidor —cerrar el puerto 5678, el reverse proxy con HTTPS, el firewall, las cabeceras de seguridad, el túnel SSH— es la guía de Self-Hosting y Operaciones, módulo 4, lección 6, y no se hace aquí. Esta fase es endurecimiento de la aplicación. Si esa otra mitad no está hecha en tu instancia, hazla: es prerequisito, no alternativa.

Paso 1 — Revisión de acceso

Lista las cuentas y responde por cada una: ¿sigue esta persona en el área?, ¿necesita editar automatizaciones?, ¿tiene 2FA?, ¿cuándo entró por última vez?

Qué esperar: los tres patrones clásicos —cuentas de personas que ya no están (se eliminan hoy), cuentas compartidas de área que destruyen la trazabilidad, y cuentas de prueba que sobrevivieron años—. Es normal terminar con la mitad de las cuentas.

Y no olvides el disparador de la lección 3: si alguien que se fue conocía el valor de alguna credencial, además de eliminar la cuenta hay que rotar esa credencial.

Paso 2 — Protege la cuenta del dueño

Contraseña larga, única y guardada en un gestor. 2FA activado. Y la regla que Terra Market adopta: la cuenta del dueño no se comparte, nunca.

Recuerda la honestidad de planes de la lección 6: según la documentación, el rol de Admin está en los planes Pro y Enterprise, y el RBAC está en todos los planes excepto Community. En Community hay dueño y miembros, y nada en medio. Esa fricción es real; la solución no es compartir la cuenta del dueño, es documentar la fricción como argumento para cuando el equipo crezca.

Qué esperar: una instancia donde una sola persona puede entrar como dueño, con segundo factor.

Paso 3 — Recorta la superficie de la aplicación

# .env — apagar lo que Terra Market no usa.

# La API pública de n8n, si nadie la usa:
N8N_PUBLIC_API_DISABLED=true
N8N_PUBLIC_API_SWAGGERUI_DISABLED=true

# Nodos que dan acceso al servidor donde corre n8n.
# El valor es un arreglo JSON escrito como cadena.
NODES_EXCLUDE="[\"n8n-nodes-base.executeCommand\", \"n8n-nodes-base.readWriteFile\"]"

Qué esperar tras reiniciar el stack: los nodos bloqueados dejan de aparecer en el buscador de nodos del editor, y la API pública deja de responder. Verifica las dos cosas en vez de suponerlas.

Antes de bloquear, comprueba con el reporte de la auditoría si algún workflow usa esos nodos. Si alguno los usa, hay que rediseñarlo primero —muy a menudo lo que hacen se resuelve fuera de n8n— y bloquear después.

Paso 4 — Cierra los hallazgos de la auditoría

Vuelve al reporte de la fase 1 y recorre las cinco secciones. Credenciales ya quedó cerrado en la fase 1. Sistema de archivos y nodos son los candidatos del paso 3, más los nodos de comunidad instalados —código de terceros con el acceso de tu instancia, que merece una decisión consciente—. Base de datos lista expresiones en campos de consulta SQL, que son riesgo de inyección. Y Instancia lista los webhooks sin protección: cada uno es una puerta abierta, porque cualquiera que descubra la dirección dispara el workflow. En Terra Market, un shipment-notify sin autenticar permitiría inyectar confirmaciones falsas y mandar mensajes a clientes reales; autentícalos con las opciones del propio nodo y valida la firma cuando el proveedor la envíe.

Paso 5 — Automatiza la auditoría

Monta un workflow que corra la auditoría el primer lunes de cada mes y mande el reporte a donde el equipo mira. Con el nodo n8n: Resource > Audit, Operation > Generate.

Qué esperar: un reporte mensual que llega solo. Esto convierte una tarea que se hace cuando alguien se acuerda en una que ocurre sola, y es probablemente el entregable más duradero de todo el proyecto.

Entregable 5 — REVISION-ACCESO.md y la lista de verificación

REVISIÓN DE ACCESO — Terra Market — <fecha>   ·   Próxima: <fecha + 3 meses>

| Cuenta   | Rol    | ¿En el área? | ¿Necesita editar? | 2FA | Último acceso | Decisión  |
|----------|--------|--------------|-------------------|-----|---------------|-----------|
| ...      | ...    | ...          | ...               | ... | ...           | ...       |

Cuentas antes: <N>   ·   Cuentas después: <M>   ·   Eliminadas: <N-M>
Credenciales rotadas por salida de personal: <lista>

ENDURECIMIENTO A NIVEL n8n
  [x] Sin cuentas de personas que ya no están
  [x] Una cuenta por persona (sin cuentas de área)
  [x] Cuenta del dueño con contraseña única, no compartida
  [x] 2FA activo en la cuenta del dueño
  [x] API pública apagada (N8N_PUBLIC_API_DISABLED)
  [x] Nodos de riesgo bloqueados (NODES_EXCLUDE) y verificados en el editor
  [x] Webhooks del reporte de instancia autenticados
  [x] Auditoría automatizada: workflow mensual → <destino>

  No disponible en Community (documentado como fricción conocida):
  [ ] 2FA obligatorio para todos (Business/Enterprise)
  [ ] Proyectos y roles granulares (todos los planes salvo Community)
  [ ] Compartir credenciales sin revelar su valor (Cloud / Business+ self-hosted)
  [ ] Redacción de datos de ejecución (Enterprise)

ENDURECIMIENTO DE RED — fuera del alcance de este módulo
  Ver guía de Self-Hosting y Operaciones, módulo 4, lección 6.
  Estado en nuestra instancia: <hecho / pendiente>

Fase 6 — Presupuesto de IA y runbook

📍 Dónde estás: todo lo anterior protege llaves que abren datos. Falta la que abre una cuenta que cobra por uso, y que hoy no tiene tope, ni alerta, ni dueño de gasto.

Objetivo de la fase: que llm_token tenga un techo conocido, una alerta que llegue a alguien que la lee, y un runbook escrito para cuando dispare.

Paso 1 — Mide el consumo actual

En el panel del proveedor, revisa el consumo de los últimos meses y anota el promedio mensual P.

Qué esperar: una cifra. Si no hay histórico suficiente, estima a partir del volumen (cuántas de las 4.000 ejecuciones diarias tocan el modelo) y trátalo como hipótesis a revisar en un mes.

Paso 2 — Configura los tres números

Alerta 1 (temprana):  a la mitad de lo esperado para esa fecha del mes
Alerta 2 (seria):     al 100% de P
Límite duro:          en torno a 2 × P

Los nombres de estas opciones varían entre proveedores —"usage limits", "budgets", "billing alerts"—; búscalas en la sección de facturación de tu panel y verifica ahí, no en un tutorial.

Qué esperar: una confirmación en el panel de que los tres quedaron activos. Y una conversación con finanzas sobre el intercambio del límite duro: si el uso legítimo lo alcanza, los workflows de clasificación se detienen. Es mejor decirlo antes que explicarlo después.

Paso 3 — Prueba que la alerta llega

Baja temporalmente el umbral de la alerta 1 por debajo del consumo actual, espera a que dispare, confirma que el aviso llegó a donde alguien lo lee, y vuelve a subirlo.

Qué esperar: un aviso en minutos u horas. Este paso es el que separa "tenemos alertas" de "tenemos alertas que funcionan", y es el que casi nadie hace.

Configura al menos dos destinatarios y, mejor todavía, haz que llegue al canal donde el equipo ya mira las alertas operativas del módulo 4.

Paso 4 — Separa las llaves por uso y acota el alcance

Si el mismo token lo usan producción y experimentos, sepáralos: llm_token_prod y llm_token_experiments, con presupuestos distintos. Y si el proveedor permite restringir una llave a modelos concretos, restringe la de producción al que tus workflows realmente usan.

Qué esperar: poder responder "¿de dónde viene este gasto?" mirando el panel, en minutos en vez de horas.

Paso 5 — Rota llm_token y observa qué se rompe

Aprovecha que estás aquí. Genera una llave nueva, actualiza la credencial, observa un día, y revoca la vieja.

Qué esperar: las ejecuciones siguen normales. Y presta atención al panel del proveedor después de revocar: si aparece algún error de autenticación de la llave revocada, alguien más la estaba usando desde otro lado. Ese hallazgo, en un token de IA, justifica la rotación por sí solo.

Paso 6 — Revisa los prompts

Abre una ejecución de cada workflow de IA y lee el prompt completo como lo leería alguien de afuera.

Qué esperar: que no haya ningún secreto y que los datos personales sean los mínimos que la tarea necesita. Para clasificar un mensaje casi nunca hacen falta el nombre, el teléfono ni el correo del cliente. Recuerda que el prompt viaja al proveedor y queda guardado en el historial de ejecuciones, que no está cifrado, y que la redacción de datos es Enterprise: en Community la defensa es no meterlos.

Entregable 6 — PRESUPUESTO-IA-Y-RUNBOOK.md

CONTROL DE GASTO DE IA — Terra Market — <fecha>

CONSUMO
  Promedio mensual observado (P): <monto>   ·   Base: <meses de histórico / estimación>

TOPES Y ALERTAS
  Alerta 1: <monto> → <destinatarios / canal>   ·   Probada el <fecha>: LLEGÓ
  Alerta 2: <monto> → <destinatarios / canal>
  Límite duro: <monto>  (efecto: el proveedor deja de atender; workflows de
               clasificación se detienen. Comunicado a finanzas el <fecha>.)

LLAVES
  llm_token_prod         → workflows de producción · restringida a <modelo> · dueño <nombre>
  llm_token_experiments  → pruebas del equipo · presupuesto bajo · dueño <nombre>
  Rotación: <fecha> · próxima: <fecha + 4 meses>
  Hallazgo al revocar la llave vieja: <ninguno / consumidor externo detectado>

PROMPTS REVISADOS
  [x] Ningún secreto en ningún prompt
  [x] Datos personales reducidos al mínimo necesario para la tarea

RUNBOOK — sospecha de fuga de llave de IA
  0–10 min   1. REVOCAR la llave en el panel del proveedor. Antes de investigar.
                Cada minuto de análisis con la llave viva es gasto adicional.
             2. Confirmar que el límite duro sigue activo.
  10–30 min  3. Generar llave nueva, actualizar la credencial, verificar ejecuciones.
  30–90 min  4. Revisar consumo: cuánto, desde cuándo, qué modelos, desde dónde.
             5. Buscar la vía de salida: repositorio (historial, no solo el estado
                actual), export descifrado, token en ticket o chat, cuenta de alguien
                que ya no está, token en un prompt o en un item.
  90–120 min 6. Rotar todo lo que pudo salir por la misma vía.
             7. Escribir qué pasó y avisar a finanzas con la cifra.

  Responsable: <nombre>   ·   Suplente: <nombre>

Criterios de aceptación

El proyecto está terminado cuando puedes responder a todo esto, con evidencia:

Inventario y gobierno

  • Inventario completo con las dos columnas de permisos y la distancia entre ellas.
  • Cada credencial tiene un dueño con nombre y una fecha de próxima rotación.
  • Ninguna credencial huérfana: las que había se revocaron en el sistema externo y se borraron de n8n, en ese orden.

Llave de cifrado

  • La llave es explícita en el .env, no automática.
  • El respaldo se verificó comparándolo carácter por carácter, no se dio por hecho.
  • Las credenciales sobreviven a un reinicio del stack, comprobado.
  • La decisión sobre rotación de llaves de cifrado está escrita, con responsable.

Mínimo privilegio

  • erp_api quedó reducida a su alcance mínimo y la llave vieja está revocada.
  • La migración se hizo con credencial nueva, un workflow a la vez, con observación entre pasos.
  • La distancia entre "tiene" y "usa" es ninguna donde se pudo confirmar; el resto está marcado como tarea abierta con responsable.

Secretos fuera de los datos

  • Ningún secreto escrito a mano en el JSON de ningún workflow.
  • Ningún secreto en la salida de ningún nodo, verificado abriendo ejecuciones reales.
  • Todo secreto encontrado fuera de una credencial fue rotado, no solo movido.
  • El .env está protegido con .gitignore (incluidas las variantes) y hay .env.example.
  • El comportamiento de $env se probó en la instancia y quedó fijado explícitamente.

Instancia

  • Sin cuentas de personas que ya no están, y las credenciales que conocían se rotaron.
  • Una cuenta por persona; ninguna cuenta de área. La del dueño con 2FA y sin compartir.
  • API pública apagada si no se usa y nodos de riesgo bloqueados, verificado en el editor.
  • Los webhooks sin protección del reporte están autenticados.
  • La auditoría corre sola y llega a alguien.
  • Lo que no está disponible en Community quedó documentado como fricción conocida.

IA

  • llm_token tiene límite duro y dos alertas, y la alerta se probó y llegó.
  • Las llaves están separadas por uso y acotadas al modelo que se usa.
  • Ningún prompt contiene secretos, y los datos personales son los mínimos.
  • El runbook está escrito, con responsable y suplente.

Errores comunes

Producir documentos en vez de cambios (conceptual, y el que arruina el proyecto). Qué pasa: se escriben los seis entregables con detalle, y al terminar erp_api sigue con permisos de administrador y llm_token sigue sin tope. El proyecto se siente hecho porque hay artefactos. Por qué pasa: documentar es cómodo y visible; cambiar una credencial en producción da miedo. Cómo detectarlo: pregúntate qué cambió en el sistema, no en la carpeta de documentación. Si la respuesta es "nada", es esto. Cómo corregirlo: los entregables son el registro del trabajo, no el trabajo. La prueba de este proyecto no es el archivo: es que la llave vieja del ERP ya no funciona y que la alerta de gasto llegó cuando la probaste.

Hacer las seis fases en un día (práctico, y causa incidentes). Qué pasa: se comprime todo en una jornada, se migran los dos workflows del ERP seguidos sin observar entre medias, y un fallo que solo aparece en cierta rama pasa desapercibido hasta la semana siguiente. Por qué pasa: la sensación de terminar es fuerte y los periodos de observación se sienten tiempo muerto. Cómo detectarlo: si migraste order-sync el mismo día que inventory-update, es esto. Cómo corregirlo: las fases 3 y 4 tocan producción y su valor está en los periodos de espera. Dos semanas con la mayor parte del tiempo siendo observación es el ritmo correcto. La prisa aquí no compra nada y puede costar una noche.

Mover un secreto sin rotarlo (práctico, y deja el riesgo intacto). Qué pasa: se encuentra una llave escrita en un campo de un nodo, se crea la credencial correspondiente, se borra del campo, y se da por resuelto. Pero esa llave estuvo en el JSON del workflow —que probablemente se versionó, se exportó o se compartió alguna vez—. Por qué pasa: mover el secreto se siente como la corrección completa. Cómo detectarlo: si en tu registro de la fase 4 hay hallazgos con "ROTADO: no", es esto. Cómo corregirlo: un secreto que estuvo fuera de una credencial se considera expuesto. La corrección tiene dos mitades: moverlo y rotarlo. La primera arregla el diseño; la segunda arregla el riesgo.

Revocar una llave antes de confirmar que la nueva funciona (práctico, y causa caída). Qué pasa: se revoca la llave vieja en el mismo momento en que se crea la nueva, y algo en la migración no estaba bien; order-sync deja de funcionar en plena operación. Cómo detectarlo: si tu procedimiento no tiene un periodo de observación entre "la nueva funciona" y "revoco la vieja", es esto. Cómo corregirlo: llave nueva → migrar → observar un ciclo completo → revocar. La llave vieja intacta durante ese periodo es tu vuelta atrás, y no cuesta nada.

Declarar el proyecto terminado con la mitad de red pendiente (conceptual, y de frontera). Qué pasa: se ejecutan las seis fases impecablemente y el puerto 5678 sigue abierto a internet sin reverse proxy. La instancia con credenciales endurecidas está expuesta a todo internet. Por qué pasa: este módulo trata la aplicación y es fácil olvidar que la otra mitad existe. Cómo detectarlo: comprueba desde fuera si tu instancia responde en el 5678 y si el editor se sirve por HTTPS. Cómo corregirlo: la guía de Self-Hosting y Operaciones, módulo 4, es prerequisito de este trabajo, no complemento opcional. Hazla y anótalo en el entregable 5, donde hay una línea justamente para eso.

Ejercicios

Ejercicio 1 — Planifica el calendario. Terra Market te da dos semanas para este proyecto, con la restricción de que los workflows no pueden detenerse y de que el administrador del ERP solo atiende solicitudes los martes. Escribe el calendario, día por día, indicando qué fase avanza y dónde están las esperas.

Ver solución

Un calendario defendible:

Semana 1. Lunes: fase 1 completa, y —clave— preparar la solicitud al administrador del ERP con el alcance mínimo, para entregarla mañana. Martes: entregar la solicitud (único día que atiende) y ejecutar la fase 2 mientras se procesa. Miércoles: fase 4, pasos 1 y 2 —buscar secretos en el JSON y en el historial, corregir y rotar—. Jueves: fase 4, pasos 3 y 4, más la revocación de las huérfanas de la fase 1. Viernes: fase 5, pasos 1 y 2 —revisión de acceso, eliminar cuentas, 2FA del dueño—.

Semana 2. Lunes: con la llave ya entregada, fase 3 pasos 3 y 4: crear erp_api_scoped y migrar solo inventory-update. Empieza la observación. Martes: observación; mientras, fase 6 pasos 1 a 3 —y el administrador del ERP está disponible por si un 403 reveló un alcance faltante—. Miércoles: fase 3 paso 5, migrar order-sync y verificar los pedidos en el ERP; mientras, fase 6 pasos 4 a 6. Jueves: observación; mientras, fase 5 pasos 3 a 5, con el reinicio del stack en horario de baja actividad. Viernes: revocar la llave vieja del ERP, verificar, y recorrer los criterios de aceptación.

Las tres decisiones que hacen bueno el calendario:

  1. La solicitud al ERP se prepara el lunes y se entrega el martes, porque el administrador solo atiende ese día. Si esperas a necesitarla, pierdes una semana entera.
  2. Las observaciones no son tiempo muerto: cada una se solapa con una fase que no toca producción. Así todo cabe en dos semanas sin comprimir ninguna espera.
  3. La revocación va al final, cuando las dos migraciones llevan días estables.

Por qué funciona: en un proyecto de producción, la restricción que manda casi nunca es el trabajo técnico —es la disponibilidad de terceros y los tiempos de observación—. Un calendario que identifica esas dos cosas primero y acomoda el trabajo alrededor es el que se cumple.

Ejercicio 2 — Decide bajo presión. Estás en el miércoles de la semana 2. Migraste order-sync a erp_api_scoped esta mañana y las ejecuciones están en verde. A las 16:00, operaciones reporta que faltan pedidos en el ERP desde el mediodía. ¿Qué haces, en qué orden, y qué NO haces?

Ver solución

Lo primero: restaurar la operación, no diagnosticar. Hay pedidos de clientes que no están llegando al ERP, y cada minuto son más. La vuelta atrás existe justamente para esto y es de un minuto: cambia los nodos de order-sync de vuelta a la credencial vieja erp_api. Todavía existe y todavía es válida —por eso el procedimiento dice que la revocación va al final—. Qué esperar: los pedidos vuelven a llegar; confírmalo en el ERP, no solo en el panel de n8n.

Lo segundo: recuperar lo que se perdió. Identifica los pedidos del mediodía a las 16:00 que no llegaron y reprocésalos, con las herramientas del módulo 3.

Lo tercero, ya sin presión: diagnosticar. La hipótesis principal es que hay una operación que el inventario del paso 1 no capturó. Fíjate en el detalle que orienta la búsqueda: las ejecuciones estaban en verde, así que probablemente no fue un 403 ruidoso sino un fallo silencioso —una respuesta aceptada que no produjo el efecto—. Es exactamente el escenario que el paso 5 de la fase 3 advierte cuando pide verificar los pedidos en el ERP y no solo el color de la ejecución.

Lo cuarto: corregir y reintentar el martes siguiente, cuando el administrador del ERP pueda agregar el alcance faltante.

Qué NO haces:

  • No revocas la llave vieja. Es tu vuelta atrás y es lo único que te permitió resolver esto en un minuto.
  • No "arreglas" el problema dándole permisos de administrador a la llave nueva. Eso deshace todo el trabajo de la fase 3 y, peor, deja la sensación de que el mínimo privilegio "no funcionó". Lo que faltó fue una operación concreta; se agrega esa.
  • No diagnosticas antes de restaurar. Con pedidos de clientes sin llegar al ERP, cada minuto de análisis tiene un costo de negocio. Diagnostica cuando la operación esté corriendo.

Por qué funciona: el ejercicio pone a prueba si entendiste por qué el procedimiento tiene la forma que tiene. Los tres elementos que te salvan —la credencial vieja intacta, la observación como parte del plan, y la verificación del efecto y no solo del estado— son exactamente los pasos que en un día tranquilo parecen exceso de cuidado. Este es el día para el que estaban.

Ejercicio 3 — Presenta el resultado. Al terminar, el director de Terra Market te pide que expliques en cinco minutos qué cambió. No es una persona técnica. Escribe lo que dirías, sin lista de tareas: con el antes y el después en términos de riesgo de negocio.

Ver solución

Una versión posible:

"Voy a contarlo con dos preguntas: qué es lo peor que nos podía pasar antes, y qué es lo peor que nos puede pasar ahora.

Antes. La llave con la que nuestras automatizaciones entran al ERP tenía permisos de administrador: podía leer, escribir, borrar y cambiar la configuración del sistema completo. Y había circulado por chat hasta llegar a ocho personas, algunas de las cuales ya no están en el área. Lo peor que nos podía pasar era que alguien con esa llave borrara o alterara el ERP: pedidos, inventario, precios. Además teníamos una llave de inteligencia artificial que cobra por uso, sin tope y sin alerta, así que si se filtraba nos íbamos a enterar cuando llegara la factura, hasta un mes después.

Ahora. La llave del ERP solo puede hacer las cuatro operaciones que las automatizaciones realmente ejecutan: crear pedidos y leer tres tipos de información. No puede borrar nada ni cambiar configuración. Lo peor que nos puede pasar sigue siendo malo —alguien podría leer inventario y crear pedidos falsos— pero es reversible, y es mucho menos que perder el ERP. Y la llave vieja, la que circuló por chat, está revocada. Sobre la de inteligencia artificial, ahora tiene un techo y dos avisos antes de llegar a él, que probamos para asegurarnos de que llegan a alguien que los lee: pasamos de enterarnos en un mes a enterarnos en horas.

Y lo que más me importa que quede. Antes, todas estas cosas dependían de que alguien se acordara. Ahora cada llave tiene un dueño con nombre, una fecha de rotación, y hay una revisión automática mensual que nos avisa si algo se desordena. Eso es lo que hace que dentro de un año esto siga bien.

Lo que queda pendiente y quiero que sepas. Hay tres cosas que nuestro plan actual de n8n no incluye: separar por áreas quién ve qué, obligar el segundo factor a todos, y ocultar automáticamente los datos sensibles en el historial. Hoy las cubrimos con acuerdos y con tener pocas cuentas, y funciona con este tamaño de equipo. Si crecemos, conviene revisarlo, y te preparo los números cuando quieras."

Por qué funciona: la respuesta no lista tareas —el director no las puede evaluar— sino que traduce el trabajo a peor caso antes contra peor caso después, que es la unidad en la que se piensa el riesgo. Cuantifica lo cuantificable (de un mes a horas), dice qué cambió estructuralmente, y —lo más importante— declara lo que sigue pendiente en vez de vender el trabajo como completo. Esa última parte es la que hace que te crean la primera.

Resumen y siguiente paso

En esta lección ejecutaste el proyecto que cierra el módulo. En seis fases convertiste el llavero desordenado de Terra Market en uno gobernado: levantaste el inventario con la distancia entre los permisos que cada credencial tiene y los que usa, corriste la auditoría integrada y asignaste dueños; verificaste —comparando carácter por carácter— que la llave de cifrado está respaldada y que las credenciales sobreviven a un reinicio; redujiste erp_api de administrador a su alcance mínimo con credencial nueva, migración de un workflow a la vez, observación entre pasos y revocación al final —que de paso dejó inservible la llave que ocho personas tenían en su chat—; sacaste los secretos escritos fuera de las credenciales y los que pasaban por los datos, rotando cada uno porque moverlo no basta; endureciste la instancia a nivel de aplicación con la revisión de acceso, el 2FA del dueño, el recorte de superficie y la auditoría automatizada; y le pusiste a llm_token un techo, dos alertas probadas, llaves separadas por uso y un runbook cuyo primer paso es revocar antes de investigar.

Antes de avanzar deberías poder: recorrer los criterios de aceptación de tu instancia y responder sí con evidencia; explicar por qué la revocación va al final de cada migración; justificar por qué un secreto encontrado fuera de una credencial se rota además de moverse; y presentar el resultado a alguien no técnico en términos de peor caso antes y después.

El módulo 6 escala esta instancia. Vas a pasar Terra Market a modo cola, donde además del proceso principal corren varios trabajadores que se reparten las ejecuciones —lo que hace falta cuando 4.000 ejecuciones diarias empiezan a ser 40.000—. Y ahí va a reaparecer, con más peso, la pieza que aseguraste en la fase 2: todos los workers tienen que compartir la misma llave de cifrado, porque cada uno necesita descifrar credenciales para ejecutar. El trabajo que acabas de hacer no es un episodio cerrado: es el terreno sobre el que se construye lo que sigue.

Recursos