← Todas las entradas
14 ago 2026Matias Aguirre

Kernl 0.2.0: el stack Docker deja de correr sin autenticación

Loopback no es un límite de confianza contra un navegador. Esta versión saca el modo sin autenticar del quick start, pone CORS en deny-by-default, deja de entregar licencias por email y arregla backups que copiaban la base equivocada.

kernlreleaseseguridadcorsself-hostedopen-source
Ilustración de Kernl: la mascota rodeada de paneles holográficos de datos
Ilustración de Kernl: la mascota rodeada de paneles holográficos de datos

Esta versión de Kernl es casi toda seguridad, y el problema principal era el camino que recorría todo el mundo: el quick start con Docker.

Loopback no es un límite de confianza

El docker-compose.yml traía KERNEL_ALLOW_UNAUTH=1, justificado en que el dashboard se publicaba en 127.0.0.1. El razonamiento no se sostiene contra un navegador.

Combinado con un Access-Control-Allow-Origin comodín en todas las respuestas, cualquier página que el usuario tuviera abierta podía leer —y escribir— el kernel entero cross-origin, incluida la superficie de herramientas MCP. Que el puerto sea local no impide que el navegador del usuario sea el que hace el pedido.

El quick start ahora corre con el token que el kernel genera en el primer arranque.

CORS por denegación

Access-Control-Allow-Origin se manda únicamente para un origen que esté explícitamente listado en CORS_ALLOWED_ORIGINS. Una lista vacía significa same-origin, que es lo que necesita el dashboard incluido.

Antes una lista vacía significaba *, y a un origen que no coincidía se le respondía con el primer origen configurado en lugar de una negativa.

La recuperación de licencia entregaba la licencia

GET /api/license/lookup devolvía el JWT completo de la licencia —la credencial de descarga de todas las extensiones que ese cliente compró— a cualquiera que supiera su dirección de email. De yapa, su 404 confirmaba qué direcciones eran de clientes y cuáles no.

Ahora manda la licencia por mail a la dirección registrada y responde 202 igual en los dos casos, sepa o no de quién es el mail.

Además, Torrents pasa a estar gateado en runtime. La extensión paga se distribuía sin verificación de licencia ni en sus herramientas MCP ni en sus rutas /api/torrents/*; lo único protegido era la descarga del bundle.

El token de auth del primer arranque, por último, se le entrega al navegador por el fragmento de la URL, que nunca viaja al servidor, en lugar de por query string.

Los backups copiaban la base equivocada, y lo festejaban

backup.sh copiaba el ./data/kernel.db del repo —casi siempre un sobrante viejo— en lugar de la base que está adentro del volumen de Docker. Usaba cp sobre una base en modo WAL abierta por el kernel. Y salía con código 0 cuando no encontraba nada.

Ahora toma el snapshot a través de SQLite (VACUUM INTO), verifica el resultado antes de llamarlo backup, y falla fuerte. También guarda .kernel-encryption-key, sin la cual los secretos cifrados de una base restaurada son irrecuperables.

Los restores tenían el problema simétrico: restore.sh copiaba la base al ./data del repo incluso en deployments con Docker, se salteaba la clave de cifrado, e informaba “starting fresh” cuando el archivo no tenía base ninguna. Ahora exige un destino explícito con --docker o --native y rechaza un archivo vacío.

Dos que reportaban éxito sin hacer nada

  • Las licencias pagas se rechazaban en toda instalación que no fuera Docker. El kernel esperaba un issuer issuer.mtwkernel.com mientras las licencias se emiten desde issuer.lifekernl.com, y el override sólo estaba en el docker-compose.yml. Las instalaciones por rpm, deb, dmg y msi no podían activar una clave válida.
  • La firma de código nunca corría, con o sin certificados. Los pasos de firmado del workflow estaban condicionados con if: env.X != '', con X definida en el bloque env: de ese mismo paso, que no está en alcance para la condición del propio paso. Todos los releases publicaban artefactos sin firmar e informaban éxito.

El kernel también quedaba atado a loopback aun con autenticación habilitada: loadConfig() resolvía el bind antes de que el bootstrap generara el token, reescribiendo de forma permanente un 0.0.0.0 explícito. La decisión ahora se toma donde se conoce el token final.

Y el primer arranque de una instalación nativa terminaba en una pantalla de login pidiendo un token que al usuario nunca se le había mostrado. Los launchers de macOS y Windows ahora se lo entregan; el de Linux imprime dónde encontrarlo.

Cómo actualizar

curl -fsSL https://lifekernl.com/install.sh | sh

Si venís de 0.1.x con el stack de Docker, revisá tu CORS_ALLOWED_ORIGINS antes de actualizar: una lista vacía ya no significa *. El detalle completo está en el CHANGELOG.

Compartir
Siguiente paso

¿Listo para Iniciar su Proyecto?

Hablemos sobre cómo la tecnología puede transformar su organización. Ofrecemos consultas técnicas y soluciones a medida adaptadas a requisitos institucionales. Agende una sesión estratégica sin compromiso.

Hablemos
Consulta gratuitaSin compromisoRespuesta rápida