Autorización de Grano Fino: Zanzibar, OpenFGA y afines
Cómo el paper Zanzibar de Google convirtió la autorización en un problema de alcanzabilidad de grafos, por qué eso resuelve el acceso con forma de relación (grupos anidados, carpetas compartidas, permisos delegados) que los motores de políticas planas manejan mal, y cómo OpenFGA, SpiceDB y Ory Keto llevaron el modelo fuera de Google — incluyendo el problema del 'nuevo enemigo' de consistencia y cómo lo resuelven las zookies.
De reglas planas a un grafo de relaciones
El artículo anterior cerró con una pregunta que los lenguajes que cubrimos nunca se construyeron para responder bien: ¿puede Sara ver este documento porque está en un grupo al que se le compartió una carpeta que lo contiene? Rego, Cedar y Casbin pueden comprobar todos “¿está Sara en este grupo?” o “¿está este documento en esta carpeta?” como un solo salto. Con lo que luchan es con la cadena — membresía de grupo, anidada arbitrariamente, que alimenta contención de carpeta, anidada arbitrariamente, que alimenta un sí/no final que depende de recorrer un camino a través de datos cuya forma no conoces de antemano. Esa cadena no es una regla plana. Es un grafo, y la pregunta “¿puede Sara alcanzar este documento?” es un problema de alcanzabilidad de grafos.
Este artículo cubre la familia de sistemas construidos específicamente para responder ese tipo de pregunta a escala de producción: Google Zanzibar, el paper de 2019 y sistema interno que formalizó por primera vez el enfoque, y sus descendientes de código abierto más importantes — OpenFGA, SpiceDB y Ory Keto. La técnica de modelado que todos comparten tiene un nombre: ReBAC, control de acceso basado en relaciones, que conociste por primera vez como uno de seis modelos de decisión en el artículo de ABAC/PBAC/ReBAC. Este artículo es donde ReBAC deja de ser una idea de modelado y se convierte en una arquitectura que realmente puedes operar.
Por qué una regla plana no escala a relaciones
Imagina la cadena de acceso en concreto. Sara es miembro del grupo finance-team. El grupo finance-team es viewer de la carpeta Q3-Reports. La carpeta Q3-Reports contiene un documento llamado board-summary. Sara debería poder ver board-summary — no porque alguna regla mencione a Sara o a ese documento por nombre, sino porque el camino entre ellos, trazado a través de membresía y contención, se resuelve como “viewer”.
Ahora intenta expresar “viewer de un documento” como una sola condición plana de la manera en que los motores del artículo anterior quieren que lo hagas. Necesitarías enumerar cada grupo al que Sara podría pertenecer, cada carpeta a la que cada uno de esos grupos podría tener acceso, y cada documento que cada una de esas carpetas podría contener — y en el momento en que alguien cree una subcarpeta dentro de Q3-Reports, o anide un grupo dentro de otro, tu regla plana ya está equivocada. El problema no es que Rego, Cedar o Casbin sean lenguajes malos; es que la pregunta en sí no es “evalúa esta condición”, es “¿existe un camino?”, y la existencia de un camino es recorrido de grafo, no evaluación de reglas. Un sistema basado en relaciones resuelve esto almacenando las aristas — los hechos crudos, Sara miembro-de finance-team, finance-team viewer-de Q3-Reports, Q3-Reports padre-de board-summary — y respondiendo cada pregunta de acceso recorriéndolas, sin importar cuán profundo resulte estar el grafo en el momento de la consulta.
Google Zanzibar: el paper que lo empezó todo
Google necesitaba un solo sistema de autorización para servir Drive, Calendar, YouTube, Photos, Cloud y decenas de otros productos — cada uno con su propio modelo de compartición, todos necesitando comprobaciones de acceso correctas y de baja latencia a una escala de billones de relaciones almacenadas y millones de consultas por segundo. El paper de 2019 Zanzibar: Google’s Consistent, Global Authorization System describe cómo lo construyeron, y su modelo de datos se convirtió en la plantilla que prácticamente todo sistema ReBAC desde entonces ha seguido.
Tuplas de relación: el único hecho que almacena Zanzibar
Todo en Zanzibar es una tupla de relación, escrita objeto#relacion@sujeto — “este objeto tiene esta relación con este sujeto”. doc:board-summary#viewer@user:sara significa que Sara es viewer del documento board-summary, directamente. De forma crucial, el sujeto de una tupla puede ser en sí mismo la relación de otro objeto — un userset — que es cómo la membresía de grupo y la jerarquía surgen del mismo primitivo en vez de necesitar manejo de caso especial: doc:board-summary#viewer@group:finance-team#member significa “todos los que son miembros del grupo finance-team” son viewers, no un usuario específico. Encadena unas cuantas de estas y obtienes exactamente el ejemplo de Sara: tuplas de membresía, tuplas de contención, y una regla de reescritura de userset (cubierta a continuación) que las conecta en un solo grafo.
Reescrituras de userset: cómo se calculan relaciones a partir de otras relaciones
Una tupla cruda solo afirma un hecho directo. El comportamiento interesante — “los editores automáticamente también son viewers”, “ver una carpeta significa ver todo lo que hay dentro” — viene de una configuración de namespace que define, para cada tipo de objeto, cómo se puede calcular cada relación a partir de otras. Dos operaciones de reescritura importan más: unión (la relación A incluye a todos los que tienen la relación B — una relación editor típicamente incluye por unión la relación viewer, así que cada editor es automáticamente un viewer sin una segunda tupla) y tuple-to-userset (la relación A sobre este objeto se calcula siguiendo una tupla hacia un objeto distinto y leyendo una relación ahí — esto es exactamente cómo “viewer de la carpeta” se convierte en “viewer de cada documento dentro de ella”, reescribiendo la relación viewer de un documento para incluir la relación viewer de la carpeta, seguida a través de la tupla de contención).
flowchart LR accTitle: Cómo se combinan las tuplas directas y una reescritura de userset en un grafo de relaciones recorrible accDescr: El diagrama muestra tres tuplas de relación directas como nodos conectados por aristas etiquetadas. Sara se conecta al grupo Equipo de Finanzas por una arista miembro. El grupo Equipo de Finanzas se conecta a la carpeta Reportes Q3 por una arista viewer. La carpeta Reportes Q3 se conecta al documento Resumen Directivo por una arista padre. Una nota aparte explica la regla de reescritura de userset: la relación viewer de un documento se calcula como la unión de cualquier tupla viewer directa más la relación viewer de su carpeta padre, seguida a través de la arista padre. Esta regla de reescritura es lo que permite que la membresía de Sara y el otorgamiento viewer de la carpeta se resuelvan, a través de la arista padre, en que Sara sea viewer del documento. Sara((Sara)) -- miembro --> Team[Equipo de Finanzas<br/>grupo] Team -- viewer --> Folder[Reportes Q3<br/>carpeta] Folder -- padre --> Doc[Resumen Directivo<br/>documento] Rule["Regla de reescritura en document.viewer:<br/>viewer directo O<br/>viewer de la carpeta padre"] Rule -.-> Doc
En la sintaxis interna propia de Zanzibar, la configuración de namespace que expresa esa regla de reescritura del documento se ve más o menos así (simplificado del formato basado en protobuf del paper):
name: "document"
relation { name: "parent" }
relation {
name: "viewer"
userset_rewrite {
union {
child { _this {} }
child { tuple_to_userset {
tupleset { relation: "parent" }
computed_userset { relation: "viewer" }
} }
}
}
}Esta es precisamente la configuración que los propios productos de Google autoran y mantienen, y es exactamente lo que el DSL de OpenFGA (mostrado más abajo) existe para hacer legible: la misma lógica de unión de viewer-directo-y-heredado, la misma indirección tuple-to-userset a través de la relación parent, expresada en una sintaxis que nadie querría escribir a mano a escala. Entender la forma cruda una vez es lo que hace que cada DSL más amigable después sea instantáneamente legible — nunca estás aprendiendo un modelo nuevo, solo una notación nueva para los mismos primitivos de unión y tuple-to-userset.
La API Check: responder una pregunta recorriendo el grafo
Todo sistema de la familia Zanzibar expone la misma operación central: Check(objeto, relación, sujeto) — ¿está este sujeto relacionado con este objeto por esta relación, directamente o a través de alguna cadena de tuplas y reglas de reescritura? Responderla significa recorrer recursivamente el grafo hacia afuera desde el objeto: leer su regla de reescritura, seguir cada rama de unión y tuple-to-userset, y recursar en las relaciones propias de cada objeto referenciado, hasta que se encuentre un camino hacia el sujeto o se agoten todas las ramas. Para el ejemplo de Sara, Check(doc:board-summary, viewer, user:sara) sigue la regla de reescritura hacia la carpeta padre, comprueba Folder#viewer@user:sara (sin coincidencia directa), expande finance-team#member, y encuentra a Sara — existe un camino, así que la respuesta es true.
sequenceDiagram accTitle: Una llamada Check recorriendo recursivamente el grafo de relaciones accDescr: El diagrama muestra una secuencia de pasos para evaluar Check sobre el documento Resumen Directivo, relación viewer, para el usuario Sara. El cliente llama a Check. El motor lee la regla de reescritura del documento y la sigue hacia la relación viewer de la carpeta padre. El motor lee la tupla viewer de la carpeta, que nombra un userset, la relación member del grupo Equipo de Finanzas, en vez de un usuario directo. El motor expande ese userset leyendo las tuplas member de Equipo de Finanzas y encuentra a Sara listada como miembro directo. Como se encontró un camino, el motor devuelve Allow al cliente. Client->>Engine: Check(doc:board-summary, viewer, user:sara) Engine->>Engine: aplica regla de reescritura (directo o viewer del padre) Engine->>Engine: lee tupla Folder#viewer (apunta al userset Team#member) Engine->>Engine: expande el userset Team#member Engine->>Engine: Sara encontrada como miembro directo de Equipo de Finanzas Engine-->>Client: Allow (camino encontrado)
El problema del nuevo enemigo: consistencia a la escala de Google
El recorrido de grafo por sí solo no basta para ser seguro — la otra contribución definitoria de Zanzibar es una garantía de consistencia específica, motivada por una forma de ataque específica que el paper llama el problema del nuevo enemigo. Imagina que Sara es removida de finance-team en el momento T porque deja la empresa — una revocación deliberada. Si, un momento después de T, una llamada Check para su acceso resulta ser servida por una réplica de base de datos que aún no se ha puesto al día con la remoción, todavía puede devolver Allow. El “enemigo” no es un hacker explotando un bug; es la consistencia eventual ordinaria, aplicada al único tipo de lectura donde la desactualización es un agujero de seguridad en vez de un inconveniente menor.
sequenceDiagram accTitle: El problema del nuevo enemigo y cómo lo arregla un token de consistencia accDescr: El diagrama contrasta dos líneas de tiempo. En la primera, un Administrador remueve a Sara de Equipo de Finanzas en el momento T, escribiendo en una base de datos primaria. Poco después, una llamada Check para el acceso de Sara a Resumen Directivo es servida por una réplica desactualizada que aún no ha recibido la remoción, y devuelve incorrectamente Allow, ilustrando el problema del nuevo enemigo. En la segunda línea de tiempo, la misma llamada Check en cambio lleva un token de consistencia zookie fijado a una instantánea en o después del momento T. El motor está forzado a leer datos al menos tan frescos como esa instantánea, ve la remoción, y devuelve correctamente Deny. Admin->>DB: remueve a Sara de Equipo de Finanzas (momento T) Note over DB: escritura aplicada al primario,<br/>aún no replicada en todas partes Client->>Engine: Check(sara, viewer, doc) — sin zookie Engine->>DB: lee de una réplica desactualizada DB-->>Engine: Sara sigue siendo miembro (desactualizado) Engine-->>Client: Allow (INCORRECTO — el nuevo enemigo) Client->>Engine: Check(sara, viewer, doc) — zookie fijada a T Engine->>DB: lee en instantánea >= T DB-->>Engine: Sara removida (fresco) Engine-->>Client: Deny (correcto)
La solución de Zanzibar es la zookie — un token de consistencia opaco, derivado de las marcas de tiempo globalmente ordenadas de Google Spanner (vía TrueTime), que un cliente adjunta a una llamada Check para decir “responde esto usando datos al menos tan frescos como este punto”. Una escritura devuelve una zookie que marca el momento en que surtió efecto; el cliente que acaba de realizar una escritura sensible (como una revocación) puede pasar esa zookie a su siguiente Check y tener la garantía de que la revocación es visible, en vez de competir contra una caché eventualmente consistente. Zanzibar por defecto hace que cada Check sea “al menos tan fresco como la última escritura que a este cliente le consta que importa”, haciendo del comportamiento seguro el predeterminado en vez de algo que cada llamador tiene que recordar pedir.
OpenFGA: Zanzibar para el resto de nosotros
OpenFGA es un motor de autorización de código abierto, construido originalmente en Auth0 y ahora un proyecto de la CNCF, que implementa el modelo de tuplas de relación y Check de Zanzibar sin requerir infraestructura a escala de Google por debajo. Es la manera más ampliamente adoptada de llevar ReBAC al estilo Zanzibar a un sistema de producción hoy.
El modelo de autorización: un DSL legible sobre los mismos primitivos
Donde la configuración de namespace de Zanzibar es un formato interno de Google, OpenFGA define tipos y relaciones en un DSL compacto y legible para humanos:
type user
type group
relations
define member: [user]
type folder
relations
define viewer: [user, group#member]
type document
relations
define parent: [folder]
define viewer: [user, group#member] or viewer from parentLee la última línea de la misma manera que leíste la regla de reescritura de Zanzibar antes: la relación viewer de un documento es una unión de otorgamientos viewer directos ([user, group#member], coincidiendo con usuarios individuales o miembros de grupo directamente) o la relación viewer heredada desde (from) su carpeta parent — exactamente la reescritura tuple-to-userset que dejó que el acceso a nivel de carpeta de Sara fluyera hacia abajo hasta el documento. Los hechos de relación en sí se almacenan como tuplas, estructuralmente idénticas a las de Zanzibar:
{"user": "user:sara", "relation": "member", "object": "group:finance-team"}
{"user": "group:finance-team#member", "relation": "viewer", "object": "folder:q3-reports"}
{"user": "folder:q3-reports", "relation": "parent", "object": "document:board-summary"}Y la consulta es la misma operación Check, ahora con la nomenclatura de OpenFGA:
POST /stores/{store_id}/check
{
"tuple_key": {
"user": "user:sara",
"relation": "viewer",
"object": "document:board-summary"
}
}La otra mitad del problema: listar, no solo comprobar
Check responde “puede este único sujeto hacer esto”. Las aplicaciones reales también necesitan las preguntas inversas — “qué documentos puede ver Sara” (para renderizar una lista de archivos) y “quién puede ver este documento” (para renderizar un diálogo de compartir) — y responder ninguna de las dos ingenuamente ejecutando Check contra cada objeto candidato o cada usuario candidato no escala. OpenFGA expone APIs dedicadas de ListObjects y ListUsers, respaldadas por índices optimizados para lectura y algoritmos de expansión inversa, específicamente porque “listar todo lo alcanzable” es una forma de consulta fundamentalmente distinta (y más difícil) que “es esto alcanzable” — una distinción que vale la pena recordar cada vez que evalúes un sistema de autorización de grano fino: pregunta no solo “qué tan rápido es Check” sino “cómo maneja List”.
Despliegue y ajuste
OpenFGA corre como un servicio independiente (gRPC o HTTP), típicamente la forma de despliegue de servicio centralizado del artículo de XACML — un servicio de autorización, llamado por cada aplicación que necesita una decisión de permiso, con SDKs en los lenguajes de servidor comunes. También está disponible como oferta gestionada (Auth0 FGA). La consistencia es ajustable por solicitud en vez de fija: los llamadores pueden pedir latencia mínima (aceptando una pequeña ventana de desactualización) o consistencia más alta (pagando más latencia por una garantía de frescura al estilo Zanzibar) según qué tan sensible a la seguridad sea esa comprobación en particular — la misma lección de “es un compromiso, no una respuesta fija” que la sección de despliegue del artículo de XACML dio sobre la ubicación del PDP, ahora aplicada a la consistencia en vez de a la topología de red.
Otros miembros de la familia
OpenFGA es la implementación de código abierto de Zanzibar más ampliamente adoptada, pero no es la única, y vale la pena conocer las diferencias entre ellas:
- SpiceDB (de Authzed) es otra implementación de Zanzibar de código abierto y calidad de producción, con su propio lenguaje de esquema y un fuerte enfoque en backends de almacenamiento conectables y fuertemente consistentes (PostgreSQL, CockroachDB, Spanner) y una CLI dedicada de pruebas/herramientas (
zed). Su API de consistencia es inusualmente explícita — los llamadores eligenminimize_latency,at_least_as_fresh(la garantía al estilo zookie) ofully_consistentpor solicitud, convirtiendo el compromiso entre latencia y frescura al que este artículo vuelve una y otra vez en un parámetro de primera clase y visible en vez de un valor por defecto oculto. - Ory Keto es un servidor de permisos de código abierto más ligero, también inspirado en Zanzibar, que enfatiza la simplicidad y se integra naturalmente con el resto del stack de identidad de Ory (Kratos para identidad, Hydra para OAuth/OIDC) — un buen ajuste para equipos ya estandarizados en Ory que quieren comprobaciones basadas en relaciones sin adoptar un ecosistema separado.
- Permify es un participante de código abierto más nuevo en la misma familia, apuntando a una experiencia de modelado accesible similar a la de OpenFGA, con soporte incorporado para filtrado de datos (campos basados en atributos junto a relaciones) y un validador de esquema para detectar errores de modelado antes de que lleguen a producción.
Los cuatro comparten el mismo ADN — tuplas de relación, un grafo de relaciones impulsado por reglas de reescritura, una API Check que responde alcanzabilidad — porque todos son implementaciones fieles del modelo que describió el paper de Zanzibar. Elegir entre ellos hoy es sobre todo una cuestión de preferencias de backend de almacenamiento, modelo de alojamiento (auto-operado versus gestionado), granularidad de ajuste de consistencia, y ajuste al ecosistema, no semánticas de autorización fundamentalmente distintas. Ninguno de ellos requiere Spanner o TrueTime como sí lo requiere el propio Zanzibar de Google — ese es precisamente el logro de ingeniería que representa cada proyecto: entregar las garantías de consistencia de Zanzibar sobre bases de datos ordinarias y ampliamente disponibles, en vez de la infraestructura globalmente distribuida de Google.
Cómo se compara esto con todo lo demás del módulo
Vale la pena ser precisos sobre qué cambió realmente, porque es fácil confundir los motores ReBAC con “otro lenguaje de políticas más” cuando en realidad resuelven un problema estructuralmente distinto.
| OPA / Cedar / Casbin (artículo anterior) | Zanzibar / OpenFGA y afines (este artículo) | |
|---|---|---|
| La pregunta que se responde | ¿Satisface esta solicitud esta regla, evaluada contra atributos? | ¿Existe un camino entre este sujeto y este objeto en un grafo de relaciones? |
| Qué se almacena | Reglas (políticas Rego/Cedar, modelo+política de Casbin) | Tuplas de relación (hechos crudos) + reglas de reescritura que describen cómo se componen las relaciones |
| Ajuste natural | Condiciones de atributos, comprobaciones de rol, membresía de grupo de un solo salto | Compartición arbitrariamente profunda, anidamiento, delegación — carpeta-dentro-de-carpeta, grupo-de-grupos |
| El problema de ingeniería difícil | Expresividad vs. analizabilidad (Cedar), o configurabilidad (Casbin) | Consistencia a escala — el problema del nuevo enemigo — más consultas de alcanzabilidad rápidas |
| Consultas inversas (“qué puede ver este usuario”) | No es una preocupación de primera clase | API de primera clase (ListObjects/ListUsers), porque estructuralmente es difícil aquí |
Las dos familias no son tanto competidoras como herramientas para formas de pregunta distintas, y los sistemas de producción comúnmente usan ambas — OPA o Cedar decidiendo “está permitida esta llamada de API en absoluto” mientras OpenFGA responde “qué documentos específicos puede incluir esta respuesta”, cada uno haciendo la parte para la que realmente es bueno.
Ejemplo práctico: los reportes compartidos de Sara, trazados por completo
Vuelve una última vez al escenario que abrió este artículo, ahora con cada pieza nombrada. La herramienta interna de operaciones de la empresa de cashback deja que los equipos organicen reportes guardados en carpetas compartidas. Sara se une al grupo finance-team cuando se incorpora — una tupla: group:finance-team#member@user:sara. Su manager comparte la carpeta Q3-Reports con todo el equipo de finanzas — una tupla: folder:q3-reports#viewer@group:finance-team#member. Alguien del equipo sube un documento board-summary a esa carpeta — una tupla: document:board-summary#parent@folder:q3-reports. Nadie le otorga jamás acceso explícito a Sara sobre board-summary — y sin embargo, en el momento en que abre la herramienta de operaciones, Check(document:board-summary, viewer, user:sara) recorre exactamente el camino trazado en el diagrama anterior y devuelve Allow.
Ahora observa qué pasa cuando Sara cambia de equipo. El sistema de RR. HH. remueve la tupla group:finance-team#member@user:sara — una sola eliminación, nada más se toca. Cada documento en cada carpeta compartida con finance-team, a cualquier profundidad, deja de ser alcanzable desde Sara al instante, porque el camino que los hacía alcanzables ya no existe — sin limpieza por documento, sin otorgamientos huérfanos que rastrear. Esa única eliminación, reflejada correcta e inmediatamente gracias a las garantías de consistencia que cubrió este artículo, es todo el beneficio de modelar el acceso como un grafo en vez de como un montón de permisos individuales: la forma del grafo es el modelo de acceso, así que cambiar una relación cambia todo lo que dependía de ella, todo a la vez, correctamente.
Probando y operando un modelo de relaciones
Un grafo de relaciones sigue siendo política, y la disciplina de “políticas como código” del artículo anterior sigue aplicando — la mecánica solo se ve un poco distinta porque estás probando alcanzabilidad, no condiciones de reglas. OpenFGA admite archivos de prueba del modelo de autorización (comúnmente .fga.yaml) que emparejan un modelo y un conjunto de tuplas con aserciones explícitas — “dadas estas tuplas, Check(document:board-summary, viewer, user:sara) debe ser true; dadas estas tuplas con la tupla de membresía removida, debe ser false” — ejecutables en CI de la misma manera que opa test ejecuta aserciones de Rego. La CLI zed de SpiceDB ofrece un flujo de pruebas y validación local equivalente contra un esquema y un conjunto de relaciones. En ambos casos, la disciplina es idéntica a lo que defendió el artículo anterior: un grafo de autorización roto debería fallar un pull request, no descubrirse en producción.
Operar uno en producción añade una preocupación que los motores de políticas planas no tienen: amplificación de escritura y profundidad de grafo. Una sola revocación es barata (una tupla eliminada), pero un Check contra un grafo profundamente anidado — grupos dentro de grupos dentro de grupos, carpetas anidadas muchos niveles — hace más trabajo de recorrido del que haría jamás una regla plana, que es exactamente por qué toda implementación de este artículo invierte tan fuertemente en caché, profundidad de recorrido acotada, e índices optimizados para lectura para las consultas List. Modelar un grafo de relaciones no es solo un ejercicio de diseño de esquema; es también, inevitablemente, un ejercicio de diseño de rendimiento.
Recapitulación
La autorización basada en relaciones resuelve el problema hacia el que ha estado construyendo todo este módulo — acceso que depende de un grafo, no de una regla plana:
- La pregunta cambió, no solo el lenguaje. “Puede Sara alcanzar este documento” es alcanzabilidad de grafo, no evaluación de reglas — la razón por la que OPA, Cedar y Casbin luchan todos con compartición y anidamiento arbitrariamente profundos, sin importar qué tan buenos sean sus lenguajes.
- El modelo de Zanzibar es tuplas de relación más reglas de reescritura. Los hechos directos (
objeto#relacion@sujeto) se combinan a través de reescrituras de unión y tuple-to-userset en un grafo recorrible, respondido por una sola operación Check que lo recorre. - El problema del nuevo enemigo es la parte más difícil del modelo. Una revocación debe ser visible de inmediato, no eventualmente — resuelto por zookies, tokens de consistencia que fijan un Check a una instantánea no más antigua que una escritura dada.
- OpenFGA (y SpiceDB, Ory Keto, Permify) llevan el modelo fuera de Google, con un DSL accesible sobre los mismos primitivos de tuplas y Check, más APIs de primera clase ListObjects/ListUsers para las consultas inversas que los motores planos rara vez manejan bien.
- Esta es una herramienta distinta para una forma de pregunta distinta, no un reemplazo de OPA/Cedar/Casbin — los sistemas reales comúnmente operan ambos, cada uno respondiendo el tipo de pregunta para el que realmente está construido.
Tres preguntas para autoevaluarte
- Explica, usando el ejemplo de Sara/finance-team/Q3-Reports/board-summary, por qué “puede Sara ver este documento” no puede expresarse como una sola regla plana de la manera en que sí puede una política
permitde Cedar o un matcher de Casbin — ¿qué es específicamente lo que la convierte en una pregunta de alcanzabilidad de grafo en vez de eso? - Describe el problema del nuevo enemigo con tus propias palabras: ¿qué tiene que salir mal, en qué orden, para que realmente cause un incidente de seguridad? Luego explica qué cambia una zookie sobre esa secuencia.
- Un equipo quiere construir una función de “compartir este documento con un grupo” y está decidiendo entre modelarla como una política de Cedar con un conjunto de grupos permitidos codificado, versus una tupla de relación en OpenFGA. ¿Cuál se ajusta mejor a medida que la función de compartir crece para soportar carpetas anidadas y grupos anidados, y por qué?
Ejercicios prácticos
- Modela tú mismo el ejemplo de Sara. Escribe un modelo de autorización de OpenFGA (o la configuración de namespace equivalente al estilo Zanzibar) para los tipos
user,group,folderydocumentcon las relaciones member, viewer y parent de este artículo. Añade las tres tuplas del escenario de Sara y confirma que un Check para(document:board-summary, viewer, user:sara)devuelve true; luego remueve la tupla de membresía y confirma que devuelve false. - Traza un grafo más profundo en papel. Extiende el ejemplo con una segunda carpeta anidada dentro de
q3-reports, y un segundo grupo anidado dentro definance-team(si tu motor elegido admite grupo-dentro-de-grupo). Escribe, paso a paso, cómo recorrería una llamada Check la capa extra, e identifica dónde el recorrido podría volverse costoso si el anidamiento fuera mucho más profundo. - Diseña un conjunto de pruebas para un modelo de relaciones. Usando el formato de prueba
.fga.yamlde OpenFGA (o un equivalente), escribe al menos cuatro aserciones para el escenario de Sara: acceso otorgado a través del grupo, acceso denegado después de remover la tupla de membresía, acceso denegado para un usuario no relacionado, y acceso otorgado para un segundo documento añadido a la misma carpeta sin ningún otorgamiento directo nuevo.
Preguntas frecuentes
¿Qué es la autorización de grano fino, y en qué se diferencia de ReBAC?
Autorización de grano fino significa decisiones de acceso tomadas al nivel de recursos individuales y relaciones individuales, en vez de roles amplios — 'puede Sara ver este documento específico' en vez de 'pueden los agentes de soporte ver cuentas'. ReBAC (control de acceso basado en relaciones) es la técnica de modelado específica que cubre este artículo para lograrlo: expresar los permisos como un grafo de relaciones entre sujetos y objetos, y responder preguntas de acceso comprobando alcanzabilidad en ese grafo. Los dos términos se usan juntos tan a menudo porque ReBAC es hoy la manera dominante en que los sistemas de producción implementan autorización genuinamente de grano fino a escala.
¿Qué es Google Zanzibar?
Zanzibar es el sistema de autorización que Google construyó para servir comprobaciones de control de acceso para Drive, Calendar, YouTube, Photos y decenas de otros productos desde un solo servicio global, descrito en un paper de USENIX de 2019. Su idea central es modelar cada permiso como una tupla de relación (objeto, relación, sujeto), responder 'puede este sujeto hacer esto sobre este objeto' comprobando alcanzabilidad a través de ese grafo de relaciones, y garantizar que una comprobación nunca use datos de autorización más antiguos que la escritura más reciente relevante para ella — la garantía de consistencia del 'nuevo enemigo'. Zanzibar en sí es interno de Google, pero su modelo de datos y su enfoque de consistencia se convirtieron en el plano que implementan OpenFGA, SpiceDB y Ory Keto.
¿OpenFGA es lo mismo que Zanzibar?
OpenFGA es un motor de autorización de código abierto que implementa las ideas centrales de Zanzibar — tuplas de relación, una API Check de grafo de relaciones, reescrituras de userset para herencia de grupo y jerarquía — con un DSL de modelado más accesible y sin requerir infraestructura a escala de Google como Spanner por debajo. No es el propio sistema Zanzibar de Google, sino una reimplementación fiel y de calidad de producción del modelo que describió Zanzibar, ahora un proyecto de la CNCF con sus propios compromisos de consistencia y consulta.
¿Qué es el 'problema del nuevo enemigo' y por qué importa?
Es el error de seguridad específico donde revocar el acceso de alguien no surte efecto lo bastante rápido: un atacante que es removido de un grupo en el momento T todavía podría pasar una comprobación de autorización poco después de T si esa comprobación lee de una réplica desactualizada, aún no actualizada, de los datos de permisos — efectivamente una condición de carrera entre una revocación y una lectura. La respuesta de Zanzibar es la 'zookie', un token de consistencia que fija una comprobación a una instantánea no más antigua que una escritura dada, garantizando que la revocación se respete de inmediato en vez de eventualmente. Importa porque la mayoría de los sistemas distribuidos usan consistencia eventual por defecto para el rendimiento de lectura, lo cual está bien para casi todo excepto la única consulta donde la desactualización significa que una puerta que debería estar cerrada sigue abierta.
¿Cuándo debería recurrir a ReBAC al estilo Zanzibar en vez de OPA, Cedar o Casbin?
Recurre a él cuando la pregunta de acceso es fundamentalmente sobre un grafo de relaciones en vez de una regla plana sobre atributos — compartir (un usuario comparte una carpeta con un grupo, y todos en ese grupo deberían ver todo lo que hay dentro, a cualquier profundidad de anidamiento), delegación, o permisos con forma de organigrama donde la profundidad y forma del grafo no se conocen de antemano. OPA, Cedar y Casbin pueden expresar todos un solo nivel de membresía de grupo, pero modelar grafos de relaciones arbitrariamente profundos y con formas arbitrarias como reglas planas se vuelve combinatoriamente doloroso rápido. Si tu modelo de acceso es naturalmente un grafo, usa un motor con forma de grafo.
¿No es lento comprobar un grafo en cada solicitud?
Puede serlo, que es por lo que todo sistema de la familia Zanzibar invierte fuertemente en hacer Check rápido — recorrido de profundidad acotada, caché agresivo de resultados intermedios, e índices optimizados para lectura para las dos formas de consulta costosas: 'puede este sujeto hacer esto' (Check) y 'a qué objetos puede acceder este sujeto' (List). El compromiso entre consistencia y latencia también es ajustable en la mayoría de las implementaciones, dejándote elegir consistencia más fuerte para comprobaciones sensibles a la seguridad y consistencia relajada para las más baratas y de alto volumen, en vez de pagar la garantía más estricta en cada consulta individual.