Políticas como Código: OPA/Rego, Cedar y Casbin
Cómo escriben, prueban y despliegan los motores de autorización modernos la política que XACML dejó en abstracto. Un recorrido por Open Policy Agent y Rego, AWS Cedar y Casbin: sus lenguajes, sus modelos de decisión, sus patrones de despliegue, sus ecosistemas (Gatekeeper, Conftest, Verified Permissions) y cómo probarlos y elegir entre ellos, con el mismo ejemplo implementado en los tres.
El cambio del viernes
Un viernes por la tarde, alguien del equipo de plataforma abre la pantalla de administración del servicio de autorización y edita una regla de permisos para darle a un contratista nuevo acceso a la consola de soporte. A las 16:40, todos los agentes de soporte de la empresa pierden el acceso a las cuentas de clientes. Se tarda tres horas en averiguar qué ha pasado. No hay un diff que muestre cómo era la regla antes, ninguna revisión que hubiera podido detectar el error, ningún test que hubiera fallado y ninguna forma de volver atrás salvo reescribir de memoria la regla antigua. El registro de auditoría dice que una regla cambió; no dice desde qué.
El artículo de XACML terminaba con una promesa: ya tienes la arquitectura, PEP, PDP, PIP, PAP, el flujo de petición y decisión, los algoritmos de combinación, pero no el lenguaje. Los equipos reales ya no escriben XML de XACML a pelo, y tampoco deberían editar reglas en un formulario de administración un viernes. Escriben políticas como código: reglas de autorización en un lenguaje de verdad, guardadas en un repositorio de verdad, probadas en un pipeline de verdad y desplegadas como cualquier otro artefacto. Este artículo llena ese hueco con tres de los lenguajes que mueven hoy la autorización en producción, el Rego de Open Policy Agent, AWS Cedar y Casbin, y las herramientas que han crecido a su alrededor.
Qué significa políticas como código
“Políticas como código” no es solo “la política resulta que es un fichero de texto”. Es un conjunto concreto de prácticas tomadas de la ingeniería de software y aplicadas a los papeles de PAP y PRP de la arquitectura XACML, la escritura y el almacenamiento de las reglas. Las políticas viven en Git o algo equivalente, no en una fila de base de datos editada desde una pantalla, así que cada cambio tiene autor, fecha y diff. Los cambios pasan por pull requests, y un segundo ingeniero, a menudo de seguridad o de plataforma, revisa el diff antes de fusionarlo, igual que con el código de la aplicación. Las políticas tienen tests unitarios que comprueban que entradas concretas producen decisiones concretas, y esos tests se ejecutan en CI con cada cambio, no solo cuando alguien se acuerda de pulsar “probar”. Las políticas se empaquetan como un artefacto versionado, un bundle, un binario compilado o una capa de contenedor, de modo que una decisión siempre se puede rastrear hasta la versión exacta de la política que la produjo. Y promover una política de staging a producción sigue el mismo camino de CI/CD que el despliegue de una aplicación, con las mismas aprobaciones, la misma marcha atrás y el mismo rastro de auditoría. Cualquiera de esas prácticas habría salvado el viernes.
Cada práctica corresponde a algo que ya nombró el artículo de XACML: esto es el PAP y el PRP hechos con la disciplina de una cadena de suministro de software en lugar de una pantalla de administración. Nada de esto es exclusivo de un motor; OPA, Cedar y Casbin admiten este ciclo de vida, con más o menos herramientas incorporadas.
Que el vocabulario nuevo no esconda lo que no ha cambiado. Un PDP construido con Rego, Cedar o Casbin sigue recibiendo una petición, evaluándola frente a la política cargada y devolviendo permitir o denegar, o algo equivalente. Lo que cambia respecto al XACML a pelo está todo antes de ese momento: cómo se escribió, se revisó, se probó y se llevó la regla al PDP. Las políticas como código son una afirmación sobre el proceso de ingeniería, no sobre la arquitectura de decisión, y por eso el modelo de PEP, PDP, PIP y PAP encaja con todos los motores que siguen.
Tres motores, una forma
Cada motor responde a la misma pregunta, si este principal puede hacer esta acción sobre este recurso, con un lenguaje, un modelo de evaluación y un despliegue por defecto distintos.
| OPA / Rego | AWS Cedar | Casbin | |
|---|---|---|---|
| Qué es | Motor de políticas de propósito general (graduado en la CNCF) | Lenguaje y motor de autorización hechos a propósito, publicados en abierto por AWS | Librería de autorización configurable |
| Estilo de lenguaje | Lenguaje de consulta declarativo inspirado en Datalog | Gramática restringida de sentencias permit y forbid | Un fichero de modelo (tu propia forma de petición y política) más una expresión de coincidencia |
| Forma de la petición | input JSON libre más data | Principal, acción, recurso, contexto (PARC) | Lo que definas en model.conf |
| Despliegue típico | Sidecar o demonio por HTTP, librería integrada o WebAssembly | Crate de Rust integrado, o AWS Verified Permissions (gestionado) | Llamada a librería dentro del proceso, sin salto de red |
| Lo que lo distingue | Un solo motor para Kubernetes, CI, infraestructura y apps | Análisis formal y comprobable por máquina de las políticas | Modelo totalmente configurable; reproduce RBAC, ABAC y ACL con poco coste |
| Ecosistema | Gatekeeper, Conftest, opa-envoy-plugin | AWS Verified Permissions, Cedar Analysis | Adaptadores para SQL, MongoDB, Redis, etcd; SDK en más de 10 lenguajes |
| Pruebas incorporadas | opa test, un ejecutor de tests dedicado | Validador de esquema más herramientas de análisis de políticas | Tests unitarios normales en el lenguaje anfitrión |
Todas las columnas siguen respondiendo a la forma de sujeto, acción, recurso y entorno de los artículos anteriores. Las diferencias están en el diseño del lenguaje y en el despliegue, no en lo que es una decisión de autorización.
Open Policy Agent y Rego
Open Policy Agent (OPA) es un motor de políticas de propósito general graduado en la CNCF: el mismo motor que admite o rechaza un despliegue de Kubernetes puede, sin cambios, autorizar una llamada a una API o bloquear un pipeline de CI. Consigue esa generalidad manteniéndose neutral a propósito sobre lo que decides. OPA solo sabe evaluar Rego, su lenguaje de consulta, sobre dos documentos JSON que le das.
Cada evaluación de OPA combina exactamente dos entradas. input es la petición que se está decidiendo ahora, el JSON que describe esta acción concreta, entregado de nuevo en cada consulta; en términos de XACML es la petición del PEP. data es todo lo que OPA ya tiene cargado, datos de referencia como asignaciones de roles, registros de tickets o estructuras de la organización, normalmente sincronizados de antemano; es el material del PIP, preparado por adelantado en lugar de buscado en vivo. Las reglas Rego leen ambos y producen un documento de decisión, que no tiene por qué ser un simple booleano: Rego puede devolver JSON estructurado, útil para obligaciones o para una explicación junto al veredicto.
flowchart LR accTitle: Cómo se compone una evaluación de OPA accDescr: Dos nodos de origen, Input y Data, confluyen en un nodo de evaluación de la política Rego, que produce un documento de decisión con permitir o denegar y detalles opcionales. Input["Input<br/>(esta petición, JSON)"] --> Eval["Evaluación de<br/>la política Rego"] Data["Data<br/>(datos de referencia precargados:<br/>roles, tickets, organización)"] --> Eval Eval --> Decision["Documento de decisión<br/>(permitir/denegar + detalle opcional)"]
Esta es la regla de soporte de la app de cashback, un agente de soporte solo puede ver una cuenta con un ticket abierto asignado a él, en horario laboral, escrita en Rego:
package cashback.authz
import future.keywords.in
default allow := false
allow if {
input.action == "view"
input.resource.type == "account"
input.subject.role == "support-agent"
ticket_open_for_resource
business_hours
}
ticket_open_for_resource if {
some ticket in data.tickets
ticket.assignee == input.subject.id
ticket.resource_id == input.resource.id
ticket.status == "open"
}
business_hours if {
input.environment.hour >= 9
input.environment.hour < 18
}Léela como leíste la pseudorregla de XACML. default allow := false es la denegación por defecto, el instinto que defendía la sección de algoritmos de combinación del artículo de XACML. La regla allow es una conjunción de condiciones que tienen que cumplirse todas, porque las líneas dentro del cuerpo de una regla Rego van unidas por un AND implícito; si falla cualquiera, la evaluación cae en el valor por defecto. ticket_open_for_resource busca en data.tickets, el material precargado del PIP, un ticket abierto que coincida, igual que el PDP de XACML le pedía hechos a su PIP. Con el vocabulario de XACML no hay nada exótico; es la misma regla en un lenguaje que puedes analizar, formatear y probar.
OPA admite las tres formas de despliegue que el artículo de XACML describía en abstracto. Como sidecar o demonio, corre como un proceso propio junto a tu servicio y se le consulta por HTTP o gRPC local, el patrón dominante en entornos nativos de nube y el que usa opa-envoy-plugin para añadir autorización a una service mesh con Envoy delante. Como librería integrada, se compila dentro de un binario de Go, o corre como WebAssembly en otros entornos, eliminando el salto de red a cambio de un ciclo de actualizar y redesplegar por binario. Como servicio central, un clúster de OPA recibe consultas de muchos clientes por red, lo más sencillo de mantener coherente a cambio de una dependencia de red en cada decisión.
Las políticas y los datos llegan a un OPA en marcha como bundles, archivos .tar.gz versionados con ficheros Rego y datos JSON que se descargan de un servidor de bundles, a menudo un simple bucket de almacenamiento o un registro OCI, cada cierto intervalo y se cargan sin reiniciar. Es el mecanismo concreto detrás del diagrama del pipeline: publicar en un punto de distribución es publicar un bundle nuevo, y cargar sin redesplegar es OPA dándose cuenta de que ha cambiado la revisión del bundle y cambiando la política de forma atómica.
Como OPA es de propósito general, ha reunido un ecosistema de envoltorios hechos a propósito. OPA Gatekeeper es un controlador de admisión de Kubernetes que usa OPA para validar o modificar recursos al crearlos o actualizarlos, rechazando un Deployment que pide hostNetwork o un Pod sin las etiquetas obligatorias; es política como código aplicada a la propia infraestructura. Conftest es una herramienta de línea de comandos que ejecuta políticas Rego sobre ficheros de configuración, manifiestos de Kubernetes, planes de Terraform, Dockerfiles, como un paso de CI, para que un recurso mal configurado haga fallar la pull request antes de llegar al clúster. Y opa-envoy-plugin ejecuta OPA como filtro de autorización externa de Envoy, de modo que una service mesh puede autorizar cada petición sin que cada servicio lleve su propia lógica: un PEP construido una vez, en la capa de la mesh, para todos los servicios que hay detrás.
OPA trae su propio ejecutor de tests unitarios. Los ficheros de test van junto a la política y usan la misma sintaxis Rego:
package cashback.authz_test
import data.cashback.authz.allow
test_agent_with_open_ticket_in_hours_is_allowed if {
allow with input as {
"action": "view",
"resource": {"type": "account", "id": "4471"},
"subject": {"id": "sara", "role": "support-agent"},
"environment": {"hour": 14},
} with data.tickets as [{"assignee": "sara", "resource_id": "4471", "status": "open"}]
}
test_agent_after_hours_is_denied if {
not allow with input as {
"action": "view",
"resource": {"type": "account", "id": "4471"},
"subject": {"id": "sara", "role": "support-agent"},
"environment": {"hour": 21},
} with data.tickets as [{"assignee": "sara", "resource_id": "4471", "status": "open"}]
}opa test ./policies ejecuta todos los tests del árbol y hace fallar la compilación ante cualquier discrepancia. Eso es lo que convierte las políticas como código de eslogan en práctica: una regla de autorización rota hace fallar CI igual que un test unitario roto, antes de llegar a producción. Un test que comprobara que los agentes de soporte pueden ver cuentas con tickets abiertos habría frenado el cambio del viernes en la pull request.
AWS Cedar
Cedar es un lenguaje de políticas y un motor de evaluación que AWS diseñó desde cero y publicó en abierto en 2023, pensado específicamente para la autorización de aplicaciones y, algo poco habitual entre estos tres, con la verificación formal como objetivo de primer orden y no como añadido.
Cada decisión de Cedar evalúa un principal, una acción, un recurso y un contexto, el modelo PARC, primo directo del sujeto, acción, recurso y entorno de XACML. Las políticas de Cedar no son código de propósito general; son sentencias declarativas permit o forbid, cada una con las combinaciones a las que se aplica y una cláusula opcional when o unless para condiciones más finas:
permit (
principal in Role::"support-agent",
action == Action::"view",
resource in ResourceType::"Account"
)
when {
context.ticket.status == "open" &&
context.ticket.assignee == principal &&
context.time.hour >= 9 && context.time.hour < 18
};Compárala con la pseudorregla de XACML. principal in Role::"support-agent", action == Action::"view" y resource in ResourceType::"Account" son juntas el objetivo, el filtro barato que decide si la política viene al caso; el bloque when es la condición, la comprobación más fina sobre los atributos; y permit es el efecto. Cedar no inventó una forma nueva; le dio a la forma de XACML un lenguaje que de verdad apetece escribir.
Cedar representa a principales y recursos como entidades, registros JSON con atributos y, lo que es clave, relaciones de parentesco que modelan la pertenencia a grupos y la jerarquía. Una entidad de usuario puede tener Role::"support-agent" como padre, y eso es lo que hace que principal in Role::"support-agent" coincida. Un esquema opcional pero muy recomendable declara qué tipos de entidad, acciones y atributos son válidos, y eso es lo que permite la capacidad que distingue a Cedar: el análisis estático de las políticas antes de que evalúen una sola petición real.
sequenceDiagram accTitle: Cómo evalúa Cedar una petición de autorización accDescr: La aplicación le pide a Cedar que autorice un principal, una acción, un recurso y un contexto. Cedar carga los atributos de las entidades y la jerarquía de parentesco desde el almacén de entidades, evalúa la petición frente al conjunto de políticas permit y forbid escrito de antemano a través del PAP, donde gana cualquier política forbid que coincida, y devuelve Allow o Deny a la aplicación. participant App as App (PEP) participant Cedar as Cedar (PDP) participant Entities as Entidades (PIP) participant PolicySet as Políticas (PAP) PolicySet-->>Cedar: políticas permit/forbid (preparación, antes de la petición) App->>Cedar: isAuthorized(principal, acción, recurso, contexto) Cedar->>Entities: carga atributos + jerarquía de parentesco Entities-->>Cedar: entidades Cedar->>Cedar: evalúa permit/forbid (gana forbid) Cedar-->>App: Allow o Deny
La regla de evaluación de Cedar es sencilla y conservadora a propósito: gana cualquier forbid que coincida, coincidan los permit que coincidan. Es el instinto de deny-overrides que el artículo de XACML llamaba la opción segura por defecto, incorporado al lenguaje en lugar de dejarlo como ajuste.
La verificación formal es lo que más separa a Cedar de Rego. Como la gramática de Cedar está restringida, sin bucles ni recursión arbitrarios y con un conjunto acotado de operadores, el conjunto completo de políticas se puede pasar a un solucionador SMT y demostrar que tiene, o no tiene, ciertas propiedades, no solo probarlo con ejemplos. Las herramientas abiertas de Cedar Analysis pueden responder preguntas como si dos políticas se aplican alguna vez a la misma petición, si una política está totalmente implicada por otra y sobra, o si una política nueva estrecha el acceso respecto a la antigua o podría ampliarlo por accidente. Los tests unitarios solo pueden tomar muestras de esas preguntas, porque una batería de tests comprueba las entradas que se te ocurrió escribir, mientras que el análisis formal comprueba todas las entradas a la vez. La generalidad de Rego es justo lo que hace inabordable el análisis equivalente para Rego arbitrario. Ese equilibrio entre expresividad y demostrabilidad es lo más importante que hay que entender al elegir entre los dos.
Cedar se distribuye como un crate de Rust, cedar-policy, que las aplicaciones integran directamente, con enlaces para otros lenguajes, así que la librería integrada es el despliegue habitual. Los equipos que prefieren no gestionar ningún PDP pueden usar AWS Verified Permissions, un servicio de Cedar totalmente gestionado: un PDP alojado al que llamas por red, que configuras con un esquema y políticas desde la consola o la API de AWS, y que AWS opera y escala. En los términos del artículo de XACML, es un PDP central gestionado que habla Cedar.
Casbin
Casbin toma un camino distinto de los dos. En lugar de traer un modelo de decisión fijo, trae un modelo configurable y te pide que describas el tuyo. Eso lo convierte menos en un motor de autorización al estilo de OPA y Cedar y más en una caja de herramientas de autorización, muy buena para reproducir con poco coste patrones conocidos, ACL, RBAC y ABAC, dentro de una aplicación, con SDK en más de diez lenguajes, entre ellos Go, Java, Node.js, Python, PHP, .NET y Rust.
Todo despliegue de Casbin empieza con un pequeño fichero de modelo, model.conf, que define cuatro cosas: la forma de una petición entrante, la forma de una regla de política guardada, el efecto, que es cómo se combinan varias reglas coincidentes en una decisión, el algoritmo de combinación propio de Casbin, y un matcher, una expresión booleana que decide si una petición coincide con una línea de política.
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub_rule, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.actEl resaltado dice .ini porque los ficheros de modelo de Casbin usan el mismo formato de secciones y claves que un .ini clásico: corchetes para las secciones, como [request_definition], y asignaciones debajo. El lector de Casbin acepta cualquier texto con esa estructura sea cual sea la extensión, aunque la convención recomendada es .conf, para no confundirlo con ficheros de configuración corrientes.
Los datos de política van aparte, normalmente en un CSV o en una fila de base de datos por regla:
p, r.sub.Role == "support-agent" && r.sub.HasOpenTicketFor(r.obj) && r.sub.Hour >= 9 && r.sub.Hour < 18, account, viewEn el momento de la petición, la aplicación llama al enforcer, enforcer.Enforce(sub, obj, act), con el sujeto, el objeto y la acción actuales. Casbin evalúa el matcher de cada línea de política frente a la petición, combina los resultados con el efecto de la política y devuelve un booleano. HasOpenTicketFor es una función normal que la aplicación anfitriona registra en Casbin; su soporte de ABAC funciona dejando que las expresiones del matcher llamen a código real, y así llega a los datos de tickets o atributos sin una abstracción de PIP propia.
flowchart LR accTitle: Cómo resuelve una petición el enforcer de Casbin accDescr: La app llama a Enforce con sujeto, objeto y acción. El enforcer se apoya en un fichero de modelo que describe la forma de la petición y de la política más el matcher, y en datos de política cargados aparte. El matcher evalúa cada línea de política, el efecto de la política combina las coincidencias y el enforcer devuelve permitir o denegar a la app. App["La app llama a<br/>Enforce(sub, obj, act)"] --> Enforcer["Enforcer<br/>de Casbin"] Model["Modelo (model.conf):<br/>forma de petición+política, matcher"] --> Enforcer PolicyData["Datos de política<br/>(policy.csv / adaptador de BD)"] --> Enforcer Enforcer --> Matcher["El matcher evalúa<br/>cada línea de política"] Matcher --> Effect["El efecto combina<br/>las coincidencias"] Effect --> Result["permitir / denegar<br/>(vuelve a la app)"]
Casbin separa el modelo del sitio donde viven las filas de política mediante adaptadores, backends de almacenamiento intercambiables para bases de datos SQL, MongoDB, Redis, etcd o un fichero, todos con la misma interfaz. Cambiar una política en fichero por una base de datos en la que escribe otra herramienta de administración es cambiar una línea del adaptador, no reescribir el modelo ni las llamadas de la aplicación, la misma propiedad de “cambiar las fuentes de datos sin tocar la lógica de la política” que el artículo de XACML atribuía a un PIP bien separado.
Casbin no compite a propósito con los otros dos en los mismos ejes. No tiene un lenguaje de consulta de propósito general, porque el matcher es una expresión booleana y no un programa, así que la lógica al estilo Rego con reglas auxiliares es incómoda de escribir directamente en un matcher, aunque la vía de escape de las funciones de callback cubre casi todo el hueco en la práctica. No tiene verificación formal comparable a Cedar Analysis. Y por defecto no es un servidor independiente: el enforcer es un objeto de librería dentro de tu proceso, así que no hay un PDP único y natural compartido entre muchas aplicaciones, como un clúster de OPA o un endpoint de Verified Permissions, salvo que montes a propósito el servidor gRPC opcional de Casbin. Lo que pierde en generalidad y demostrabilidad lo recupera en una latencia casi nula, sin llamada de red y a menudo sin serialización, y en lo rápido que un equipo ya cómodo con RBAC o ACL puede montar justo ese patrón con unas pocas líneas de configuración.
Otros miembros de la familia
Esos tres merecen conocerse a fondo, pero el patrón es más amplio. Kyverno resuelve el mismo problema que OPA Gatekeeper, la política de admisión de Kubernetes, con YAML sencillo en lugar de Rego, cambiando la generalidad de Rego por un comienzo más fácil para los equipos que ya viven en manifiestos de Kubernetes; es la alternativa “sin lenguaje nuevo” a Gatekeeper, no un competidor general de OPA. Styra DAS y plataformas comerciales parecidas envuelven OPA en una capa de gestión, con interfaz para escribir políticas, distribución, registro de decisiones y análisis de impacto, para organizaciones que quieren el motor de OPA sin construir la plataforma de alrededor. Y los motores al estilo Zanzibar y OpenFGA resuelven otro problema: la autorización basada en relaciones, con forma de grafo, a la escala de compartir en Google Drive, que trató el artículo del ReBAC y a la que vuelve el siguiente artículo.
Cómo elegir
No hay una elección correcta para todo, solo la correcta para lo que tiene que decidir un PDP concreto, la misma lección con la que cerraba el artículo de XACML. Como punto de partida y no como reglamento:
| Si necesitas… | Recurre a… | Porque… |
|---|---|---|
| Autorizar igual en Kubernetes, CI, infraestructura y apps | OPA / Rego | Un motor y un lenguaje generales para dominios muy distintos, con herramientas maduras para cada uno (Gatekeeper, Conftest) |
| Demostrar que las políticas nunca se solapan ni amplían el acceso por accidente | Cedar | El análisis formal solo es posible porque la gramática está restringida, y ningún otro motor de aquí lo ofrece |
| Lanzar una autorización de aplicación que opere AWS por ti | Cedar con AWS Verified Permissions | Un PDP central gestionado, sin infraestructura que mantener |
| Añadir RBAC o ABAC a un servicio con latencia casi nula y sin infraestructura nueva | Casbin | Una llamada a librería dentro del proceso, sin sidecar ni salto de red, con un modelo a la medida de tu petición |
| Rechazar en CI un plan de Terraform o un manifiesto de Kubernetes mal configurado | Conftest (OPA / Rego) | Hecho para comprobar políticas sobre configuración, no solo sobre peticiones en vivo |
| Modelar el acceso como un grafo de relaciones (compartir, anidar, delegar) | Ninguno; ver ReBAC | Es otra forma de problema, que encaja directamente con los sistemas de la familia Zanzibar |
Los sistemas reales suelen usar varias filas a la vez: OPA Gatekeeper protegiendo el clúster, Cedar o Verified Permissions protegiendo los permisos de la aplicación y Casbin integrado en una herramienta interna de administración que nunca necesitó un salto de red. Esa pluralidad no es indecisión; es elegir la herramienta por punto de decisión, ahora con tres opciones concretas.
Una regla, tres lenguajes
Fija la regla de la fintech y mírala en los tres motores: un agente de soporte solo puede ver la cuenta de un cliente si tiene abierto un ticket asignado a él para esa cuenta, y solo en horario laboral, de 9:00 a 18:00.
En Rego es un default allow := false más una conjunción de condiciones, con una regla auxiliar, ticket_open_for_resource, que busca en los datos de tickets precargados. Es el que más código lleva de los tres y el más explícito sobre cómo se busca el ticket. En Cedar es un único permit con una cláusula when que usa campos de context.ticket aportados por quien llama, más corto que Rego porque Cedar da por hecho que quien llama ya ha reunido el contexto necesario en lugar de buscar en un documento de datos grande. En Casbin es una expresión matcher más una fila de política, que deja la comprobación del ticket a un callback del lenguaje anfitrión, HasOpenTicketFor, en lugar de expresar la búsqueda en el lenguaje de políticas; el texto de política más corto de los tres, porque Casbin devuelve más lógica al código normal de la aplicación.
Esas diferencias son la filosofía de cada lenguaje hecha visible. Rego quiere la búsqueda dentro de la política, donde se puede probar y revisar junto a la regla. Cedar quiere la búsqueda hecha antes de que corra la política, para que la política siga siendo demostrable. Casbin quiere la búsqueda fuera, en código en el que ya confías, para que la capa de políticas sea fina. Ninguno se equivoca; optimizan cosas distintas.
Pruebas: que “como código” sea verdad
Un lenguaje de políticas sin una historia de pruebas no son políticas como código; son políticas en otro formato de fichero. OPA tiene la historia más completa de serie: opa test ejecuta tests unitarios escritos en el propio Rego, informa de la cobertura con opa test --coverage y entra en CI como un solo comando con un código de salida de éxito o fallo, como el ejecutor de tests de cualquier otro lenguaje. Cedar prueba en dos capas. Los tests unitarios normales, que comprueban que isAuthorized devuelve la decisión esperada para una tabla de entradas, cubren escenarios concretos como los tests de Rego, mientras que el validador de esquema y Cedar Analysis añaden algo que los tests unitarios no pueden dar por estructura: garantías sobre todas las entradas, no solo sobre las que a alguien se le ocurrió escribir. Un pipeline de Cedar maduro hace las dos cosas. Casbin no tiene un ejecutor de tests propio de políticas, porque el enforcer es una función normal; lo pruebas como cualquier otra cosa, llamando a Enforce() con peticiones de prueba en tu framework de siempre, go test, pytest o jest. Es menos ceremonia, pero también significa que no hay nada equivalente a la cobertura de opa test ni a las garantías formales de Cedar, así que el rigor depende por completo de lo disciplinada que sea ya la batería de tests de la aplicación.
La promesa se derrumba si los tests existen pero nadie los ejecuta automáticamente. El fallo es de lo más corriente: alguien cambia una regla Rego, la ejecuta una vez a mano con el caso que tenía en la cabeza y la fusiona, y otra combinación de condiciones, sin probar ni revisar, llega rota a producción. Conectar opa test, el validador de Cedar o la batería de tests de tu lenguaje al pipeline de CI que bloquea una compilación rota no es un acabado opcional; es lo que hace verdad lo de “políticas como código”. Y es, al final, la respuesta al cambio del viernes.
Resumen
Las políticas como código responden a la parte que la arquitectura XACML dejaba en abstracto: cómo se escribe, se prueba y se despliega el libro de normas. Son un proceso y no un lenguaje, control de versiones, revisión de código, tests automáticos, artefactos versionados y despliegue por pipeline aplicados a los papeles de PAP y PRP, con el PDP decidiendo igual por debajo. OPA y Rego son de propósito general: un motor y un lenguaje, input más data para producir un documento de decisión, reutilizados en la admisión de Kubernetes con Gatekeeper, en las comprobaciones de configuración en CI con Conftest, en las service mesh con opa-envoy-plugin y en la autorización de aplicaciones, y probados con opa test. Cedar cambia generalidad por demostrabilidad: una gramática restringida de permit y forbid sobre una petición PARC permite analizar formalmente las políticas de formas que la flexibilidad de Rego hace inabordables, y se distribuye como crate integrado o como el servicio gestionado AWS Verified Permissions. Casbin cambia un modelo fijo por una latencia casi nula: un model.conf configurable más un matcher, integrado como llamada a librería dentro del proceso, excelente para reproducir RBAC, ABAC y ACL dentro de un servicio e independiente del almacenamiento gracias a los adaptadores. Y los sistemas reales mezclan motores por punto de decisión y no por decreto de empresa.
Tres preguntas para ponerte a prueba
- Explica con tus palabras por qué Cedar puede ofrecer verificación formal de las políticas y Rego en general no. ¿Qué decisión de diseño marca la diferencia y qué gana o pierde cada lenguaje por ella?
- Un equipo quiere añadir autorización a una única herramienta interna de administración, sin tolerancia a latencia extra y sin ganas de mantener un servicio aparte. ¿Cuál de los tres motores encaja mejor y qué dos propiedades estructurales de ese motor, más allá de “es más sencillo”, lo hacen adecuado?
- Con la regla de soporte de la fintech, explica por qué la búsqueda del ticket va dentro de la política en Rego pero fuera, en el contexto o en un callback, en Cedar y Casbin. ¿Qué cuesta y qué aporta cada elección?
Ejercicios prácticos
- Escribe y prueba una política Rego. Instala OPA, escribe la regla de soporte de la fintech, o una versión simplificada, en Rego y escribe al menos tres casos de
opa test: uno que debería permitir, uno denegado por el rol equivocado y uno denegado por estar fuera de horario. Ejecutaopa testy comprueba que pasan; después rompe una condición a propósito y comprueba que la detecta el test correcto. - Modela una regla en los tres lenguajes. Coge una regla de acceso de tu propio trabajo, o inventa una con al menos dos condiciones, y escríbela en Rego, en Cedar y como un
model.confde Casbin más una línea de política. ¿Cuál fue más fácil de escribir, cuál sería más fácil de revisar para alguien que no conoce la herramienta y en cuál confiarías más para detectar un error automáticamente? - Diseña una barrera de CI. Esboza en un README, no hace falta que sea código que funcione, cómo debería ser la comprobación de una pull request en un repositorio de políticas Rego: qué se ejecuta, en qué orden y qué bloquea la fusión. Incluye al menos un paso de tests y otro que detecte un
default allow := trueaccidental. Compara tu diseño con cómo integra la documentación de OPAopa testen CI.
Preguntas frecuentes
¿Qué significa de verdad 'políticas como código'?
Significa tratar las reglas de autorización como tratas el código de la aplicación: escritas en un lenguaje de verdad, guardadas en control de versiones, revisadas en pull requests, probadas con tests unitarios y desplegadas por el mismo pipeline de CI/CD que todo lo demás, en lugar de vivir como filas en la base de datos de una pantalla de administración o como un XML que nadie compara. Las reglas siguen cumpliendo los papeles de PAP y PDP de la arquitectura XACML; las políticas como código cambian cómo se escriben, se prueban y se despliegan.
¿Es OPA/Rego un sustituto de XACML?
Sustituye el lenguaje XML de XACML, no su arquitectura. OPA es un punto de decisión de políticas de propósito general: le envías una petición en JSON, evalúa reglas Rego sobre esa entrada más los datos que tenga cargados y devuelve un documento de decisión. La forma de PEP, PDP, PIP y PAP del artículo de XACML sigue intacta por debajo; OPA es una forma moderna y cómoda para los desarrolladores de construir el PDP y el PAP.
¿Qué diferencia real hay entre OPA/Rego y AWS Cedar?
Los dos son motores de autorización de propósito general con una petición formada por principal, acción, recurso y contexto, pero optimizan cosas distintas. Rego es un lenguaje de consulta flexible capaz de expresar casi cualquier lógica de política y de dar forma a salidas de decisión arbitrarias, a cambio de algo de capacidad de análisis. Cedar restringe a propósito su gramática a sentencias permit y forbid para que las políticas se puedan analizar formalmente, demostrar que no se solapan, detectar reglas inalcanzables y verificar frente a un esquema, a cambio de ser menos expresivo que Rego para lógicas poco habituales. Elige Rego para la máxima flexibilidad en muchos dominios (Kubernetes, CI, infraestructura); elige Cedar cuando quieras en concreto políticas comprobables por máquina para el modelo de principal, acción y recurso de una aplicación.
¿En qué se diferencia Casbin de OPA y Cedar?
OPA y Cedar traen un modelo de decisión fijo al que se llega cruzando una frontera de petición y respuesta, sea un sidecar, un servicio o una librería a la que se llama con una entrada. Casbin trae en cambio un modelo configurable: escribes un pequeño model.conf con tu propia forma de petición, tu forma de política y tu expresión de coincidencia, y se integra directamente en tu aplicación como una llamada a librería, normalmente sin ningún salto de red. Eso lo hace muy bueno para reproducir con poco coste patrones RBAC, ACL y ABAC sencillos dentro de un servicio, pero por defecto no es un servidor de políticas independiente y no tiene nada equivalente a la lógica general de Rego ni a la verificación formal de Cedar.
¿Se pueden probar automáticamente estos lenguajes de políticas?
Sí, y esa es toda la gracia de llamarlo políticas como código. OPA trae opa test, un ejecutor de tests unitarios para Rego que corre en CI en cada cambio. Cedar trae un validador que comprueba las políticas frente a un esquema declarado, además de unas herramientas de análisis (Cedar Analysis) capaces de demostrar propiedades como 'estas dos políticas nunca se aplican a la vez'. El enforcer de Casbin es una llamada a función normal, así que se prueba con el framework que ya use el lenguaje anfitrión, como el paquete testing de Go o pytest en Python. En todos los casos el objetivo es el que XACML nunca resolvió bien: detectar una regla de autorización rota en una pull request y no en producción.
¿Tengo que elegir uno solo para todo mi sistema?
No, y la mayoría de las organizaciones no lo hacen. Es habitual ver OPA Gatekeeper aplicando políticas en la admisión de Kubernetes, Cedar o un equivalente alojado protegiendo los permisos de grano fino de una aplicación y Casbin integrado en un servicio interno que solo necesita un RBAC sencillo sin dependencias de red. La lección de XACML se aplica un nivel más arriba: elige la herramienta por punto de decisión, según lo que ese PDP tenga que decidir de verdad, en lugar de dar por hecho que un solo motor debe encargarse de todas las decisiones de autorización de la empresa.