Skip to main content
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. 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.
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.

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.
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.

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


Configuración

Todas las variables de entorno, por componente.

Despliegue

Dependencias, retención en almacenamiento, escalado y verificaciones de arranque.

Observabilidad

Sondas, comportamiento de drenaje, métricas y trazas.

Conexiones

Registra, prueba y usa una conexión.