Volver a la wiki

Incident: tareas Huey (purga, integridad, monitor de conexiones) corrían sin RLS — inertes en PROD

⚠️ Revisión del 04-09-2026 (s301): el diagnóstico de abajo era una INFERENCIA y resultó falso en PROD

Medido el 04-09 en los contenedores db-1 de PROD y de STAGE (SELECT rolconfig FROM pg_roles WHERE rolname='crearack_app'): el rol con el que conecta la aplicación lleva app.current_org_id=0 como valor por defecto de rol desde el runbook 1g de RLS (julio 2026, AUDIT.md §7-1g: “bypass declarado por defecto de rol; el middleware web lo restringe por request con SET LOCAL”). Toda conexión nueva de un worker de Huey nace con el GUC en '0' = bypass, no en NULL. Prueba del efecto: monitoring_aiinsight tiene 175 filas expired entre el 24-07 y el 01-09, escritas por la tarea Huey expire_stale_insights, que no lleva ningún envoltorio. Las tareas Huey escribían y leían con normalidad.

Lo que sigue siendo cierto: rls_bypass() es la forma explícita correcta de declarar que un código de sistema opera sobre todas las organizaciones, y protege si algún día se retira el default del rol (el endurecimiento que la Auditoría Suprema 2 pedía: GUC por organización en cada tarea + quitar el default). Es defensivo, no un arreglo de algo roto. El resto de esta página se conserva como registro de lo que se creyó el 03-09 y de por qué: la revisión en Opus leyó migraciones y middleware sin medir el rol en la base de datos. Antes de afirmar “inerte en PROD”, leer pg_roles.rolconfig. Dónde SÍ muerde el NULL: un rol sin ese default (la BD de test NOSUPERUSER de tests/test_rls.py, un rol nuevo creado a mano).


Resumen

La cola de auditoría (task #286, 14 hallazgos MEDIA) destapó que tres tareas periódicas de Huey — la purga de organizaciones borradas, la comprobación de integridad entre tenants y el aviso de saturación de conexiones a BD — llevaban corriendo en PROD/STAGE sin efecto real: el middleware que fija el contexto de Row Level Security solo actúa dentro de una petición HTTP, y un worker de Huey nunca pasa por él.

Severidad: MEDIA (no hay fuga de datos entre organizaciones; el fallo es que ciertas tareas de mantenimiento no hacían nada).

Estado: corregido en el mismo commit que lo detectó — sin incidente abierto en producción ni remediación pendiente.

Cuándo

Detectado y corregido el 2026-09-03 (sesión s300), en la revisión de la cola de auditoría (task #286). No se conoce con precisión desde cuándo estaban activas así las tres tareas — llevaban tiempo corriendo antes de esta revisión.

Síntomas visibles

Ninguno directamente visible: las tres tareas corrían, no lanzaban excepción y dejaban log de “éxito” (logger.info), pero:

Causa raíz

core.middleware.tenant_rls.TenantRLSMiddleware fija app.current_org_id con SET LOCAL en cada petición HTTP. Las tablas protegidas tienen FORCE ROW LEVEL SECURITY, así que sin ese GUC las consultas devuelven 0 filas y los INSERT se rechazan (WITH CHECK). Un worker de Huey ejecuta código fuera de cualquier request — nunca pasa por el middleware — así que el GUC llegaba NULL a las tres tareas.

Fix aplicado

Commit d14ebb58 (PR #494, v1.103.0). Nuevo core/utils/rls.py con el context manager rls_bypass(): abre una transacción y ejecuta SET LOCAL app.current_org_id = '0' — el mismo valor que el middleware usa para superusers — antes de las consultas que deben ver todas las organizaciones. Se aplica en core/tasks.py:

27 tests nuevos cubren las tres tareas.

Lecciones

Preventivos futuros

Cualquier tarea periódica NUEVA (Huey) que lea o escriba tablas protegidas por RLS debe envolver ese código en rls_bypass(), siguiendo el patrón de core/tasks.py. Distinto pero emparentado con [[incident—20260710—rls-gap-tablas-nuevas]]: aquel incidente era ausencia de POLÍTICA de RLS en tablas nuevas; este es ausencia de CONTEXTO de RLS en código que nunca pasa por el middleware.

Véase también

Subir