> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Seguridad de Fetcher

> El modelo de seguridad de Fetcher: una clave maestra expandida en cuatro claves derivadas, mensajes firmados atados al tenant y a la ruta, cifrado de credenciales y resultados, validación de host y aislamiento de tenant.

Fetcher guarda credenciales de bases de datos que no controla, y mueve filas que salieron de ellas. Esta página describe qué protege cada una de esas cosas, y qué tiene que hacer un operador.

## Una clave maestra, cuatro claves derivadas

***

`APP_ENC_KEY` es la única clave que provees. Genérala con `make generate-master-key`, que produce un valor de 32 bytes codificado en base64. Define el mismo valor en el Manager y en el Worker.

Fetcher nunca usa esa clave de forma directa. La expande con HKDF-SHA256 (RFC 5869) en cuatro claves independientes, una por propósito.

| Clave derivada            | Se usa para                                                            | Etiqueta de derivación          |
| ------------------------- | ---------------------------------------------------------------------- | ------------------------------- |
| Credenciales              | Cifrado AES-256-GCM de las contraseñas de datasource en reposo.        | `fetcher-credentials-v1`        |
| HMAC interno              | Firma de cada mensaje entre el Manager y el Worker.                    | `fetcher-internal-hmac-v1`      |
| HMAC externo              | Firma de los resultados de extracción, para verificación por terceros. | `fetcher-external-hmac-v1`      |
| Cifrado de almacenamiento | Cifrado AES-GCM del resultado almacenado.                              | `fetcher-storage-encryption-v1` |

La separación es el punto. Un consumidor que tiene la clave externa puede verificar la firma de un resultado. No puede descifrar una credencial almacenada, y no puede falsificar un mensaje entre los dos servicios.

<Warning>
  **Una clave maestra incorrecta detiene el servicio.** Una clave sin definir, un base64 inválido o un valor por debajo de 32 bytes terminan el proceso al arrancar. El log dice `master key too short: got 0 bytes, minimum 32 required`. Fetcher no tiene modo alternativo en texto plano.
</Warning>

## Versión de clave y rotación

***

`APP_ENC_KEY_VERSION` etiqueta la clave que está en vigor. Cada registro de conexión guarda la versión que cifró su contraseña, de modo que un operador puede saber a qué clave pertenece un registro. Incrementa la versión cuando cambies la clave maestra.

Un cambio de clave maestra tiene dos consecuencias:

1. **Credenciales almacenadas.** Una conexión cifrada bajo la clave anterior pertenece a la clave anterior. Registra esas conexiones de nuevo bajo la clave nueva.
2. **Verificación externa.** La clave HMAC externa cambia junto con la clave maestra. Deriva la clave nueva y entrégala a cada consumidor que verifica firmas. Los resultados anteriores se verifican contra la clave anterior.

Genera la clave externa con `make derive-key KEY="<tu-clave-maestra-base64>"`. La herramienta también lee `APP_ENC_KEY` del entorno o la clave desde la entrada estándar, e imprime una clave hexadecimal de 64 caracteres.

## Credenciales en reposo

***

La contraseña de un datasource nunca llega a MongoDB en claro. El Manager la cifra con AES-256-GCM bajo la clave de credenciales derivada, y después guarda el texto cifrado y la versión de la clave.

Los datasources internos declarados con `DATASOURCE_{NAME}_*` son la excepción deliberada. Vienen del propio entorno del operador, y Fetcher los marca como internos con una versión de clave vacía.

## Resultados en reposo

***

El Worker protege un resultado almacenado en dos pasos:

1. Firma el JSON en texto plano con HMAC-SHA256 bajo la clave externa derivada, y registra el algoritmo y la firma junto con el resultado.
2. Cifra el payload con AES-GCM bajo la clave de almacenamiento derivada, con un nonce aleatorio nuevo de 12 bytes, y guarda el resultado codificado en base64.

La firma cubre el texto plano, así que un consumidor verifica los datos que recibió y no el sobre que los envuelve. El repositorio incluye una guía de verificación en `scripts/crypto/derive-key/verification-guide.md`.

El modo directo devuelve las filas en línea sin cifrado. El engine las reporta como texto plano y les adjunta un digest SHA-256 sobre los bytes exactos.

## Mensajes firmados entre los servicios

***

Cada mensaje de RabbitMQ que el Manager publica hacia el Worker lleva una firma HMAC-SHA256 bajo la clave interna derivada. La firma cubre más que el cuerpo. Ata:

* la marca de tiempo y la versión de la firma,
* el identificador de tenant,
* el identificador de job,
* el exchange y la routing key,
* el cuerpo del mensaje.

Ese amarre es lo que detiene la repetición. Un mensaje capturado y repetido bajo otro tenant falla la verificación, porque la firma cubre el identificador de tenant. El mismo mensaje repetido hacia otro exchange u otra routing key falla por la misma razón. El publicador además elimina cualquier header de seguridad provisto por quien llama antes de firmar, así que un cliente no puede inyectar el suyo.

El firmante rechaza una clave más corta que 32 bytes, y compara firmas en tiempo constante.

## Validación de host de datasource

***

Un tenant que registra su propia conexión podría apuntarla a tu red interna. Con `MULTI_TENANT_ENABLED=true`, Fetcher revisa el host antes de conectarse.

La validación corre en dos capas:

1. **Al parsear la petición.** Fetcher rechaza una dirección IP literal en un rango bloqueado, sin resolución DNS.
2. **En la fábrica de datasources.** Fetcher revisa el nombre de host contra una lista de bloqueo que cubre `localhost`, nombres de metadatos de nube y los sufijos `.local`, `.internal` y `.cluster.local`. Después resuelve el nombre de host y revisa cada dirección que obtiene.

Fetcher rechaza un host bloqueado con un error de host prohibido. Los rangos bloqueados cubren direcciones de loopback, privadas y de metadatos de nube, y viven en `lib-commons`, así que todos los productos Lerian comparten una sola lista.

Los datasources internos configurados por el operador quedan exentos por construcción. Vienen de tu entorno, no de la petición de un tenant.

## Aislamiento de tenant

***

El engine acota cada operación por identificador de tenant, y ese identificador es el único límite de aislamiento que tiene. Un identificador de tenant mal formado falla antes de que Fetcher toque cualquier recurso.

En modo multi-tenant, cada tenant recibe su propia base de datos de metadatos, resuelta a partir de los claims del JWT de la petición a través del middleware de tenant. El acceso falla cerrado. Una petición que lleva una identidad de tenant sin base de datos de tenant resuelta devuelve un error en lugar de leer la base de datos compartida.

<Warning>
  **La multi-tenancy exige autenticación.** El router del Manager se niega a construirse cuando un middleware de tenant corre con la autenticación desactivada, y reporta `tenant middleware requires effective authentication`. También se niega cuando `PLUGIN_AUTH_ENABLED=true` y la dirección de autenticación está vacía. En ambos casos el servicio no arranca.
</Warning>

## Autenticación y superficies de sonda

***

`PLUGIN_AUTH_ENABLED=true` pone el middleware de Access Manager delante de la API. Las peticiones llevan entonces un token bearer, y Fetcher autoriza cada operación contra un recurso y una acción.

`/health`, `/readyz`, `/readyz/tenant/:id`, `/metrics` y `/version` se montan antes de ese middleware, así que las sondas de Kubernetes y del balanceador de carga siguen sin autenticación.

## Los errores nunca filtran material de conexión

***

Fetcher descarta el error crudo del driver en la frontera del engine y devuelve un mensaje fijo en su lugar. Un error crudo del driver puede incrustar un DSN o una credencial, así que quien llama ve `failed to connect to datasource` en lugar de la cadena que produjo el driver.

Los fallos llegan clasificados en categorías estables — validación, no autorizado, prohibido, límite excedido, conexión, timeout y otras — así que un host las mapea a sus propios códigos de estado sin parsear texto.

## Próximos pasos

***

<CardGroup cols={2}>
  <Card title="Configuración" icon="gear" href="/es/fetcher/fetcher-configuration">
    Todas las variables de entorno, por componente.
  </Card>

  <Card title="Despliegue" icon="server" href="/es/fetcher/fetcher-deployment">
    Dependencias, retención en almacenamiento, escalado y verificaciones de arranque.
  </Card>

  <Card title="Observabilidad" icon="chart-line" href="/es/fetcher/fetcher-observability">
    Sondas, comportamiento de drenaje, métricas y trazas.
  </Card>

  <Card title="Conexiones" icon="plug" href="/es/fetcher/fetcher-connections">
    Registra, prueba y usa una conexión.
  </Card>
</CardGroup>
