Published

Políticas de autorización del warehouse (Cedar)

Connect any source, model it as an ontology, transform it, and operationalize it, analytics, automation and machine learning, under one governed, self-hostable roof. --- Most teams stitch the...

Políticas de autorización del warehouse (Cedar)

Políticas Cedar para el catálogo Lakekeeper. Approach y decisión: warehouse-authorization.md. Encendido de la puerta que estas políticas cierran: runbook C5.

Por qué viven en el repo

Porque una política en un fichero es una decisión legible en un diff. Se revisa como cualquier otro cambio, se despliega con el código, y se puede razonar sin consultar el estado de un servicio. La alternativa —OpenFGA— guarda los permisos como un grafo de relaciones en un store: estado opaco que no aparece en ninguna revisión.

Es la misma disciplina que los cinco trinquetes de la plataforma: la regla se ve en la revisión.

🔴 Estado: NO SE PUEDEN ACTIVAR — Cedar no está en el build OSS de Lakekeeper

Medido el 2026-08-04 contra la v0.13.0 que corremos, tras ponerlo en producción:

  • con LAKEKEEPER__AUTHZ_BACKEND=cedar y la política dentro del contenedor, el proceso arranca sin una sola queja y escribe Using AllowAll authorizer;
  • en el código: el workspace sólo tiene crates/authz-openfga, no hay crate de Cedar, y el binario hace bail!("Unsupported authz backend") para todo lo que no sea openfga.

La documentación lo describe y las release notes lo anuncian en 0.13.0, pero la distribución Apache-2.0 no lo lleva — encaja con el license-status que devuelve /management/v1/info. Es de su build comercial.

⚠️ El modo de fallo es el peor posible: una configuración que parece aplicada y no lo está. Sólo lo cazó tener el gate calibrado contra el estado permisivo.

Estas políticas se quedan escritas y no se borran, por dos razones: valen tal cual el día que haya un autorizador que las lea (build comercial, o el trabajo de traducirlas a OpenFGA), y son la especificación legible de la regla — lo que cada identidad debe poder hacer en el catálogo, en un diff.

Lo que se hace mientras tanto: cerrar el camino en la autenticación con LAKEKEEPER__OPENID_AUDIENCE — ver runbook C5 §5. Es todo-o-nada por identidad en vez de por acción, y eso basta para lo que ⑤ persigue: que Trino no pueda hablar con el catálogo por su cuenta.

Lo que dicen hoy (npm run check:cedar → 6 permit · 3 forbid):

IdentidadQué puede en el CATÁLOGO
warehouse-writercrear · commitear · soltar · leer — el único que escribe
duck-serverleer. Y un forbid explícito sobre commit/drop/write
index-catalog (la cara)leer — reenvía lo que ya autorizó arriba
operatortodo — humanos y scripts
trinoNADA. forbid sobre toda acción
(el compartido) ml-runnernada: sin permit, y Cedar es deny-by-default

El forbid de Trino ES el paso ⑤. Desde el paso ④ Trino entra por la cara REST de Index Catalog, pero mientras pueda seguir hablando directamente con el catálogo lo hace porque se lo pedimos, no porque no le quede otra — y eso es una convención, no gobernanza. Su acceso al dato no desaparece: pasa a resolverse por la puerta, donde su grant es «listar todo el workspace, leer sólo main.default».


Cómo llegan estas políticas a la máquina

Cedar carga políticas SÓLO desde ficheros locales o ConfigMaps de Kubernetes — no hay variable de entorno con la política inline, ni fuente remota por URL (docs). Y Lakekeeper corre en Railway desde la imagen oficial, que obviamente no lleva las nuestras.

Ese hueco lo cierra ../Dockerfile: la imagen oficial + COPY policies/. Así la política desplegada es la revisada, y no una copia que alguien subió a un volumen y que diverge al segundo cambio.

⚠️ Si un fichero es inválido, Lakekeeper NO ARRANCA. No degrada: se apaga. Por eso existe el validador y por eso el orden de abajo separa desplegar el fichero de encender el authorizer.

npm run check:cedar    # sintaxis, ANTES de desplegar

Activación · el orden, y no es negociable

0 · Validar y decidir lo que falta

  • npm run check:cedar en verde.
  • ⚠️ Tu usuario HUMANO (Victor Obregon, oidc~33351116-c5ec-4e1f-ba62-78ed62b3e08a) no tiene permit. Si usas la UI de Lakekeeper, decide antes: o se le añade un permit, o asumes que la operación se hace con la credencial de operator. No lo descubras después de encenderlo.
  • ml-runner (oidc~b3f95571-…) se queda sin acceso, y eso es el sello final de la Fase A: la identidad compartida deja de servir para algo. Comprobado que ya no la usa nadie, pero conviene saber que ésta es la línea donde muere.

1 · Desplegar el fichero, con el authorizer TODAVÍA apagado

En Railway, servicio de Lakekeeper: pasar de deploy from image a construir el repositorio con Root Directory = infra/lakekeeper. Sin tocar AUTHZ_BACKEND.

  • el servicio arranca y /health sigue en 200;
  • facade-smoke en verde (nada ha cambiado de comportamiento: sigue en allow-all).

Este paso es reversible y no cambia ninguna decisión: sólo mete un fichero en la imagen.

2 · Encender el authorizer

LAKEKEEPER__AUTHZ_BACKEND=cedar
LAKEKEEPER__CEDAR__POLICY_SOURCES__LOCAL_FILES=/policies/warehouse.cedar

3 · Verificar en positivo Y en negativonpx tsx scripts/governance/c5-cedar-gate.ts

En positivo (si algo de esto falla, rollback inmediato):

  • warehouse-writer escribefacade-smoke completo, no un health-check;
  • duck-server lee — el SQL Editor resuelve tablas;
  • la cara sirve a Trino: SHOW SCHEMAS y DESCRIBE en main.default siguen yendo.

En negativo — sin esto sólo has cambiado una variable:

  • duck-server NO puede commitear;
  • trino NO puede hablar directamente con Lakekeeper… y sí por la cara.

Esa última pareja es la que convierte el paso ④ en gobernanza. Y la penúltima no es
ceremonia: en esta misma línea de trabajo, un token que decía read-only resultó
escribir, y sólo se supo al intentarlo.

Rollback: LAKEKEEPER__AUTHZ_BACKEND=allow-all. Una variable, y el fichero puede quedarse donde está.


Lo que sigue sin poder validarse antes de encender

/management/v1/permissions/cedar/schema devuelve 404 mientras el backend sea allow-all (medido 2026-08-04): sin authorizer cargado no hay schema que publicar. Así que un nombre de acción inexistente pasa el validador y no protege nada — ya pasó dos veces (NamespaceSelectActions, GetTableCredentials).

En cuanto Cedar esté activo, ese endpoint responde y toca cerrar el círculo: añadir cedar.validate({ policies, schema }) a scripts/governance/check-cedar.ts (hay un TODO en el fichero). Hasta entonces, el gate negativo es la única validación real.

Identidades (Fase A · 2026-08-03 → 2026-08-04)

ClienteId en LakekeeperQuién lo usa · dónde corre
lakekeeper-warehouse-writeroidc~21a12588-76ec-4da6-bee6-5fef4345d91awarehouse-writer · Railway
lakekeeper-duck-serveroidc~c3219179-5134-4f44-94e3-0afdb32f7af8duck-server · Railway
lakekeeper-trinooidc~4ae04c42-32c1-497f-8549-c9c9aa2417e2Trino · VM de GCP, fuera de Railway
lakekeeper-operatoroidc~8cd1e1be-f1df-465d-8de1-466af0486d75humanos y scripts · .env.local
lakekeeper-index-catalogoidc~bc8c8a9e-2557-4d0c-9367-9948a79317bala cara REST · Vercel
(el compartido, retirado) lakekeeper-ml-runneroidc~b3f95571-eaf6-48f5-a201-378f75ffdb1b

Un cliente de Keycloak es una IDENTIDAD, no un servicio. No corre en ningún
sitio: es un nombre y un secreto que algo ya existente usa para demostrar quién
es. Nunca hay que desplegar nada para dar de alta una identidad — se mete en el
entorno de lo que ya corre.

🔎 Para no perder media hora la próxima vez: un cliente recién creado en
Keycloak da 404 UserNotFound en whoami. No es un fallo de configuración —
Lakekeeper mantiene su propio registro de principals y el alta ocurre en la
primera llamada real al catálogo ("last-updated-with": "config-call-creation").
Para forzarlo:
FASE_A_PROVISION=true LAKEHOUSE_CATALOG_CREDENTIAL=… npx tsx scripts/governance/fase-a-identities.ts

⚠️ Los service accounts tienen is-instance-admin: false. Si al activar Cedar el operador necesita administrar a nivel de SERVIDOR (y no sólo de warehouse), eso es un flag de Lakekeeper, aparte de estas políticas.