Políticas como Código: OPA/Rego, Cedar y Casbin

Cómo los motores de autorización modernos escriben, prueban y despliegan la política que XACML dejó abstracta. Un recorrido profundo y cargado de diagramas por Open Policy Agent y Rego, AWS Cedar y Casbin — sus lenguajes, modelos de decisión, patrones de despliegue, ecosistemas (Gatekeeper, Conftest, Verified Permissions) y cómo probarlos y elegir entre ellos, con el mismo ejemplo práctico implementado en los tres.

De la arquitectura al lenguaje

El artículo de XACML terminó con una promesa: ya tienes la arquitectura — PEP, PDP, PIP, PAP, el flujo de solicitud/decisión, los algoritmos de combinación — pero no el lenguaje. Los equipos reales ya no escriben XML crudo de XACML. Escriben políticas como código: reglas de autorización expresadas en un lenguaje real, versionadas en un repositorio real, probadas en un pipeline real y desplegadas como cualquier otro artefacto. Este artículo es esa pieza que faltaba. Tomamos la misma arquitectura de cuatro roles y llenamos tres de los lenguajes que realmente ejecutan autorización en producción hoy — Rego de Open Policy Agent, AWS Cedar y Casbin — más el ecosistema de herramientas que creció alrededor de ellos.


Qué significa realmente “políticas como código”

Antes de comparar motores, vale la pena ser precisos sobre el paradigma en sí, porque “políticas como código” no es simplemente “la política resulta ser un archivo de texto”. Es un conjunto específico de prácticas tomadas prestadas por completo de la ingeniería de software y aplicadas a los roles de PAP y PRP de la arquitectura XACML — la autoría y el almacenamiento de las reglas:


  • Versionadas. Las políticas viven en Git (o equivalente), no en una fila de base de datos editada por una pantalla de administración. Cada cambio tiene autor, fecha y diff.
  • Revisadas como código. Los cambios pasan por pull requests. Un segundo ingeniero — a menudo de un equipo de seguridad o plataforma — revisa el diff antes de fusionarlo, igual que un cambio de código de aplicación.
  • Probadas automáticamente. Las políticas tienen tests unitarios que afirman que ciertos inputs producen ciertas decisiones, y esos tests corren en CI en cada cambio, no solo cuando alguien se acuerda de pulsar “probar” en un panel de administración.
  • Empaquetadas y versionadas como artefacto. Las políticas se empaquetan (un bundle, un binario compilado, una capa de contenedor) con un identificador de versión, igual que se empaqueta el código de aplicación, de modo que una decisión siempre pueda rastrearse hasta la versión exacta de política que la produjo.
  • Desplegadas por un pipeline, no por un formulario. Promover una política de staging a producción sigue el mismo camino de CI/CD que un despliegue de aplicación — con las mismas aprobaciones, el mismo mecanismo de rollback y el mismo rastro de auditoría.

El Pipeline de Políticas como Código De una regla local a una decisión del PDP en vivo y rastreable El Pipeline de Políticas como Código De una regla local a una decisión del PDP en vivo y rastreable tipos de línea paso del pipeline enlace de trazabilidad Autor de la Política (escribe y prueba) redacta una regla y la prueba en local Pull Request (revisión de par) un segundo ingeniero revisa el diff Pipeline de CI (build + test) corre los tests, arma un bundle Registro (distribución) el bundle se publica y versiona v2 PDP (punto de decisión) consulta el registro y lo carga en caliente abre un PR fusiona publica consulta y descarga se rastrea hasta bundle v2.3.1 El autor escribe y prueba · PR revisado · CI arma un bundle · el Registro lo distribuye · el PDP lo carga en caliente — las decisiones se rastrean
El ciclo de vida de políticas como código como un pipeline: un autor redacta y prueba una regla en local, un Pull Request se revisa entre pares, CI arma y prueba un bundle versionado, un Registro lo distribuye, y el PDP lo consulta, descarga y carga en caliente sin redesplegarse. Cada decisión en vivo se rastrea hasta la versión exacta del bundle que la produjo.

Cada práctica de esa lista corresponde a algo que el artículo de XACML ya nombró: esto es simplemente el PAP y el PRP hechos con la disciplina de una cadena de suministro de software en vez de una GUI de administración. Nada aquí es exclusivo de un motor — OPA, Cedar y Casbin admiten todos este ciclo de vida, aunque (como verás) lo admiten con distintas cantidades de herramientas incorporadas.



Tres motores, una misma forma — un primer vistazo

Antes de las inmersiones profundas, aquí está la tabla de orientación. Cada motor responde la misma pregunta subyacente — “¿puede este principal hacer esta acción sobre este recurso?” — con un lenguaje, un modelo de evaluación y una forma de despliegue por defecto distintos.


OPA / RegoAWS CedarCasbin
Qué esMotor de políticas de propósito general (graduado en CNCF)Lenguaje y motor de autorización diseñado a medida, liberado por AWSLibrería de autorización configurable
Estilo de lenguajeLenguaje de consulta declarativo, inspirado en DatalogGramática restringida: declaraciones permit/forbidArchivo de modelo (tu propia forma de solicitud/política) + expresión de coincidencia
Forma de la solicitudinput + data en JSON arbitrarioPrincipal, Acción, Recurso, Contexto (PARC)Lo que definas en model.conf
Despliegue típicoSidecar/demonio sobre HTTP, o librería incrustada, o WASMCrate Rust incrustado, o AWS Verified Permissions (gestionado)Llamada a librería incrustada, en el mismo proceso, sin salto de red
Rasgo distintivoPropósito general: el mismo motor para Kubernetes, CI, infraestructura, appsAnálisis formal y verificable por máquina de las políticasEl modelo es totalmente configurable — reproduce RBAC/ABAC/ACL de forma barata
EcosistemaGatekeeper, Conftest, opa-envoy-pluginAWS Verified Permissions, Cedar AnalysisAdaptadores para SQL/Mongo/Redis/etcd, SDKs en más de 10 lenguajes
Pruebas incorporadasopa test — runner de tests unitarios de primera claseValidador de esquema + conjunto de herramientas de análisisTests unitarios comunes en el lenguaje anfitrión

Nota que cada columna sigue respondiendo a la forma PARC/sujeto-recurso-acción-entorno que establecieron los dos artículos anteriores — las diferencias están en el diseño del lenguaje y el despliegue, no en de qué está hecha fundamentalmente una decisión de autorización. Con el mapa en mano, recorramos cada motor por turno.


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 modificación, autorizar una llamada de API o filtrar un pipeline de CI. Logra esa generalidad manteniéndose deliberadamente sin opinión sobre qué estás decidiendo — OPA solo sabe cómo evaluar Rego, su lenguaje de consulta diseñado a medida, contra dos documentos JSON que le das.


Cómo evalúa OPA una decisión

Cada evaluación de OPA combina exactamente dos entradas. input es la solicitud que se está decidiendo ahora mismo — el documento JSON que describe esta acción específica, entregado fresco en cada consulta (la solicitud del PEP, en términos de XACML). data es todo lo que OPA ya tiene cargado — datos de referencia como asignaciones de roles, registros de tickets o jerarquías organizacionales, típicamente sincronizados de antemano (el material del PIP, precargado en vez de buscado en vivo). Las reglas Rego leen ambos y producen un documento de decisión — no necesariamente un booleano simple; Rego puede devolver JSON estructurado, útil para devolver obligaciones o una explicación completa junto al veredicto.


flowchart LR
  accTitle: Cómo se compone una sola evaluación de OPA
  accDescr: Dos nodos de origen, Input y Data, convergen en un nodo de evaluación de política Rego, que produce un documento de decisión con permitir/denegar más detalle opcional.
  Input["Input<br/>(esta solicitud, JSON)"] --> Eval["Evaluación de<br/>política Rego"]
  Data["Data<br/>(datos de referencia precargados:<br/>roles, tickets, datos organizacionales)"] --> Eval
  Eval --> Decision["Documento de decisión<br/>(permitir/denegar + detalle opcional)"]
Cómo se compone una sola evaluación de OPA. La solicitud llega como el documento input; los datos de referencia ya cargados en OPA son el documento data; las reglas Rego se evalúan contra ambos, y OPA devuelve un documento de decisión que puede ser un booleano o un resultado estructurado más rico.

Una política Rego mínima para la regla del agente de soporte de la app de cashback — un agente de soporte puede ver una cuenta solo si tiene un ticket abierto y asignado, durante horario laboral — se ve así:


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 pseudo-regla de XACML: default allow := false es la postura de denegación por defecto — el mismo instinto que la sección de algoritmos de combinación del artículo anterior defendió como lo seguro. La regla allow es una conjunción de condiciones que deben cumplirse todas (el AND implícito de Rego entre líneas dentro del cuerpo de una regla); si alguna falla, la evaluación cae al valor por defecto. ticket_open_for_resource busca en data.tickets — el material del PIP precargado — un ticket abierto que coincida, exactamente como el PDP de XACML le pedía hechos a su PIP. Nada aquí es exótico una vez que tienes el vocabulario de XACML; es la misma regla, en un lenguaje que puedes lintear, formatear y probar con tests unitarios.


Despliegue: tres formas, un mismo motor

OPA admite las mismas tres formas de despliegue que el artículo de XACML introdujo en abstracto, y aquí es donde se vuelven concretas. Como sidecar o demonio, OPA corre como su propio proceso junto a tu servicio y se consulta por HTTP o gRPC local — el patrón dominante en el mundo cloud-native, y la forma que usa opa-envoy-plugin para agregar autorización a un servicio detrás de Envoy. Como librería incrustada, OPA se compila directamente en un binario de Go (o corre como WebAssembly dentro de otros runtimes), eliminando el salto de red por completo al costo de un ciclo de actualizar-y-redesplegar por binario. Como servicio centralizado, un clúster de OPA es consultado por muchos llamadores sobre la red — el más simple de mantener consistente, al costo de una dependencia de red en el camino crítico de cada decisión.


Las políticas y los datos llegan a una instancia de OPA en ejecución como bundles — archivos .tar.gz versionados que contienen archivos Rego y datos JSON, obtenidos de un servidor de bundles (a menudo solo un bucket de almacenamiento de objetos o un registro OCI) en un intervalo de sondeo, y cargados en caliente sin reiniciar. Este es el mecanismo concreto detrás del diagrama del pipeline de antes: “publicar en un punto de distribución” significa publicar un nuevo bundle, y “cargar en caliente sin redesplegar” es OPA notando que la revisión del bundle cambió e intercambiando la política de forma atómica.


El ecosistema: dónde aparece OPA más allá de una app

Como OPA es de propósito general, acumuló un ecosistema de envoltorios diseñados a medida en vez de quedarse como una sola herramienta:


  • OPA Gatekeeper — un controlador de admisión de Kubernetes que envuelve a OPA para validar o mutar recursos a medida que se crean o actualizan (rechazando un Deployment que solicita hostNetwork, o un Pod sin las etiquetas requeridas). Esto es políticas como código aplicado a la infraestructura misma, no a solicitudes de aplicación.
  • Conftest — una CLI que corre políticas Rego contra archivos de configuración (manifiestos de Kubernetes, planes de Terraform, Dockerfiles) como un paso de CI, de modo que un recurso mal configurado falla el pull request antes de llegar siquiera a un clúster.
  • opa-envoy-plugin — corre OPA como un filtro de autorización externo para Envoy, dejando que un service mesh autorice cada solicitud sin que cada servicio incruste su propia lógica — un PEP construido una sola vez, en la capa del mesh, sirviendo a cada servicio detrás de él.

Probando Rego: opa test

OPA incluye un runner de tests unitarios integrado. Los archivos de test viven junto a los archivos de política y usan la misma sintaxis Rego, afirmando que ciertos inputs producen ciertos resultados:


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 corre cada test del árbol y falla el build ante cualquier discrepancia de aserción — el mecanismo exacto que convierte “políticas como código” de un eslogan en una práctica exigida: una regla de autorización rota falla en CI de la misma manera que fallaría un test unitario roto, antes de llegar a producción.


AWS Cedar

Cedar es un lenguaje de políticas y motor de evaluación que AWS diseñó desde cero y liberó como código abierto en 2023, construido específicamente para autorización a nivel de aplicación y, algo inusual entre estos tres, con la verificación formal como objetivo de diseño de primera clase en vez de una idea posterior.


El modelo de solicitud PARC

Cada decisión de Cedar evalúa un principal, una acción, un recurso y un contexto — el modelo PARC, un primo directo del sujeto/acción/recurso/entorno de XACML. Las políticas de Cedar no son código de propósito general; son declaraciones permit o forbid declarativas, cada una nombrando a qué combinaciones de principal/acción/recurso aplica, con una cláusula opcional when/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
};

Compara esto línea por línea con la pseudo-regla de XACML del artículo anterior: principal in Role::"support-agent" y action == Action::"view" y resource in ResourceType::"Account" juntos son el target — el filtro barato que decide si esta política siquiera es relevante; el bloque when es la condición — la prueba más fina contra atributos; permit es el efecto. Cedar no inventó una forma nueva, le dio a la forma de XACML un lenguaje que de verdad querrías escribir.


Entidades, esquema y el flujo de decisión

Cedar representa a principales y recursos como entidades — registros JSON con atributos y, crucialmente, relaciones de parentesco que modelan pertenencia a grupos y jerarquía (una entidad de usuario puede listar Role::"support-agent" como padre, que es lo que hace que principal in Role::"support-agent" coincida). Un esquema opcional pero muy recomendado declara qué tipos de entidad, acciones y atributos son válidos, que es lo que habilita la capacidad distintiva de Cedar: el análisis estático de políticas antes de que evalúen siquiera una solicitud real.


sequenceDiagram
  accTitle: Cómo evalúa Cedar una solicitud 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 solicitud contra el conjunto de políticas permit/forbid autoradas de antemano por el PAP, donde cualquier política forbid que coincida gana, 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 ConjuntoPolíticas (PAP)
  PolicySet-->>Cedar: políticas permit/forbid (configuración, antes de la solicitud)
  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 (forbid gana)
  Cedar-->>App: Allow o Deny
Una solicitud de autorización en Cedar. La aplicación entrega principal, acción, recurso y contexto; Cedar carga las entidades relevantes (con sus relaciones de parentesco/grupo) desde el almacén de entidades y evalúa el conjunto de políticas permit/forbid contra la solicitud, las entidades y un esquema opcional, devolviendo Allow o Deny — con forbid siempre teniendo precedencia sobre permit.

La regla de evaluación de Cedar es simple y deliberadamente conservadora: cualquier política forbid que coincida gana, sin importar cuántas políticas permit también coincidan — el mismo instinto de deny-overrides que el artículo de XACML llamó la opción segura por defecto, solo que aquí está incorporado en el lenguaje en vez de dejarse como una opción de configuración.


Por qué la verificación formal es el verdadero diferenciador de Cedar

Esta es la característica que más separa a Cedar de Rego. Como la gramática de Cedar está deliberadamente restringida — sin bucles ni recursión arbitrarios, un conjunto acotado de operadores — el conjunto de políticas completo puede entregarse a un solucionador SMT y probar que tiene (o carece de) ciertas propiedades, no solo probarse contra ejemplos de input. El conjunto de herramientas de código abierto Cedar Analysis puede responder preguntas como “¿estas dos políticas alguna vez aplican a la misma solicitud a la vez?”, “¿esta política está totalmente implicada por aquella (haciéndola redundante)?” o “¿esta política nueva restringe el acceso comparada con la vieja, o podría ampliarlo por accidente?” — preguntas que los tests unitarios solo pueden muestrear, nunca responder de forma exhaustiva, porque un conjunto de tests revisa los inputs que se te ocurrió escribir, mientras que el análisis formal revisa todos los inputs a la vez. La generalidad de Rego es precisamente lo que hace intratable el análisis equivalente para Rego arbitrario — no puedes probar propiedades en general sobre un lenguaje de consulta casi Turing-completo de la manera en que puedes hacerlo sobre la gramática restringida de Cedar. Ese compromiso — expresividad contra demostrabilidad — es la decisión de ingeniería más importante a entender al elegir entre los dos.


Despliegue: crate incrustado o servicio gestionado

Cedar se distribuye como un crate de Rust (cedar-policy) que las aplicaciones incrustan directamente, con bindings para otros lenguajes, lo que hace de la librería incrustada la forma de despliegue habitual. Para equipos que prefieren no operar su propio PDP en absoluto, AWS Verified Permissions es un servicio de evaluación Cedar totalmente gestionado — un PDP alojado que llamas por red, configuras con un esquema y políticas a través de la consola o API de AWS, y que AWS opera y escala. Verified Permissions es, en los términos del artículo de XACML, un PDP de servicio centralizado gestionado que resulta hablar Cedar.


Casbin

Casbin toma un enfoque estructuralmente distinto al de OPA y Cedar: en vez de distribuir un modelo de decisión fijo, distribuye un modelo configurable y te pide que describas el tuyo propio. Esto hace que Casbin sea menos un “motor de autorización” en el sentido de OPA/Cedar y más un kit de herramientas de autorización — extremadamente bueno para reproducir de forma barata patrones familiares (ACL, RBAC, ABAC) directamente dentro de una aplicación, con SDKs en más de diez lenguajes (Go, Java, Node.js, Python, PHP, .NET, Rust y otros).


El modelo: solicitud, política, matcher

Todo despliegue de Casbin empieza con un pequeño archivo de modelo (model.conf) que define cuatro cosas: la forma de una solicitud entrante, la forma de una regla de política almacenada, el efecto (cómo se combinan varias reglas coincidentes en una sola decisión — el propio algoritmo de combinación de Casbin, en el vocabulario de XACML), y un matcher — una expresión booleana que decide si una solicitud dada coincide con una línea de política dada.


[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.act


Los datos de política viven aparte, típicamente en un archivo CSV o 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, view

En el momento de la solicitud, la aplicación llama al enforcerenforcer.Enforce(sub, obj, act) — con el sujeto, objeto y acción actuales; Casbin evalúa el matcher de cada línea de política contra esa solicitud, combina los resultados usando el efecto de política, y devuelve un booleano. HasOpenTicketFor en el ejemplo es una función ordinaria registrada con Casbin desde la aplicación anfitriona — el soporte ABAC de Casbin funciona dejando que las expresiones matcher llamen de vuelta a código real, que es como llega a datos de tickets o atributos sin necesitar su propia abstracción de PIP.


flowchart LR
  accTitle: Cómo resuelve un enforcer de Casbin una solicitud
  accDescr: La app llama a Enforce con sujeto, objeto y acción. El enforcer se apoya en un archivo de modelo con la forma de solicitud y 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 política combina los resultados coincidentes, 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 solicitud+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 de política<br/>combina coincidencias"]
  Effect --> Result["permitir / denegar<br/>(devuelto a la app)"]
Cómo resuelve un enforcer de Casbin una solicitud. La aplicación llama a Enforce con sujeto, objeto y acción; Casbin evalúa cada línea de la política cargada contra la expresión matcher del modelo, combina los resultados coincidentes usando el efecto de política configurado, y devuelve permitir o denegar al código de aplicación que lo llamó — todo en el mismo proceso, sin salto de red.

Adaptadores: almacenamiento de políticas sin tocar el modelo

Casbin separa el modelo de dónde viven realmente las filas de política mediante adaptadores — backends de almacenamiento conectables para bases de datos SQL, MongoDB, Redis, etcd, o un simple archivo, todos cumpliendo la misma interfaz. Cambiar una política basada en archivo por una respaldada en base de datos que una herramienta de administración separada escribe es un cambio de adaptador de una línea, no una reescritura del modelo o de las llamadas de aplicación de la app — la misma propiedad de “cambiar fuentes de datos sin tocar la lógica de política” que el artículo de XACML atribuyó a un PIP bien separado, lograda aquí a través de la abstracción de adaptador en su lugar.


Por qué Casbin no es un reemplazo directo de OPA o Cedar

Casbin deliberadamente no compite en los mismos ejes que los otros dos. No tiene lenguaje de consulta de propósito general — el matcher es una expresión booleana, no un programa, así que la lógica multi-paso al estilo Rego con reglas auxiliares es incómoda de expresar directamente en un matcher (aunque la vía de escape de funciones callback cubre la mayor parte de la brecha en la práctica). No tiene una historia de verificación formal comparable a Cedar Analysis. Y, de forma crítica, no es un servidor independiente por defecto — el enforcer es un objeto de librería que vive dentro de tu proceso, así que no hay un único PDP natural compartido de forma consistente entre muchas aplicaciones como sí lo es un clúster de OPA o un endpoint de Verified Permissions, a menos que levantes deliberadamente el Casbin Server opcional basado en gRPC. Lo que Casbin sacrifica en generalidad y demostrabilidad, lo recupera en latencia casi nula (sin llamada de red, a menudo sin serialización) y en la rapidez con que un equipo ya familiarizado con el pensamiento RBAC o ACL puede montar exactamente ese patrón con unas pocas líneas de configuración.


Otros miembros de la familia de políticas como código

Los tres motores anteriores son los que vale la pena conocer a fondo, pero el patrón es más grande que cualquier trío de herramientas, y un mapa profesional de este espacio debería incluir a los vecinos cercanos:


  • Kyverno resuelve el mismo problema que OPA Gatekeeper — política de admisión en Kubernetes — pero con YAML plano en vez de Rego, cambiando la generalidad de Rego por una barrera de entrada más baja para equipos que ya viven en manifiestos de Kubernetes. Vale la pena conocerlo como la alternativa “sin lenguaje nuevo” a Gatekeeper específicamente, no como un competidor de propósito general de OPA.
  • Styra DAS y plataformas comerciales similares envuelven a OPA con un plano de gestión — UI de autoría de políticas, distribución, registro de decisiones, análisis de impacto — para organizaciones que quieren el motor de OPA sin construir la plataforma alrededor ellas mismas.
  • Los motores al estilo Zanzibar y OpenFGA resuelven un problema distinto que este artículo deliberadamente no cubrió: autorización basada en relaciones, con forma de grafo, a la escala de compartir en Google Drive. Merecen — y tendrán — su propio artículo a continuación, porque responden “¿cómo revisas el acceso cuando la respuesta depende de un grafo de relaciones demasiado grande para evaluarse como una regla plana?”, que es una pregunta distinta de “¿en qué lenguaje escribo mis reglas?”.


Cómo elegir entre ellos

No hay una elección universalmente correcta — solo una elección correcta para lo que un PDP dado realmente necesita decidir, la misma lección con la que cerró el artículo de XACML. Usa esto como una heurística de partida, no como un recetario:


Si necesitas…Recurre a…Porque…
Autorizar de la misma manera en Kubernetes, CI, infraestructura y appsOPA / RegoUn motor y lenguaje de propósito general en dominios muy distintos, con herramientas de ecosistema maduras por dominio (Gatekeeper, Conftest)
Probar que las políticas nunca se solapan ni amplían el acceso por accidenteCedarEl análisis formal solo es posible porque la gramática está deliberadamente restringida — ningún otro motor aquí lo ofrece
Enviar autorización a nivel de aplicación que AWS operará por tiCedar vía AWS Verified PermissionsUn PDP centralizado gestionado, sin infraestructura que operar
Agregar RBAC/ABAC a un servicio con latencia casi nula y sin infraestructura nuevaCasbinLlamada de librería en el mismo proceso, sin sidecar ni salto de red, modelo hecho a medida de tu forma de solicitud exacta
Rechazar un plan de Terraform o manifiesto de Kubernetes mal configurado en CIConftest (OPA/Rego)Diseñado a medida para revisiones de políticas como código contra configuración, no solo solicitudes en vivo
Modelar el acceso como un grafo de relaciones (compartir, anidar, delegar)Ninguno — ver el siguiente artículoEs una forma de problema distinta; los sistemas de la familia Zanzibar encajan directamente

Los sistemas reales frecuentemente usan más de una fila de esta tabla a la vez — OPA/Gatekeeper protegiendo el clúster, Cedar o Verified Permissions protegiendo los permisos de recursos de la aplicación, Casbin incrustado en una herramienta de administración interna que nunca necesitó un salto de red en primer lugar. Esa pluralidad no es indecisión; es el mismo principio de “elegir la herramienta por cada PDP” que introdujo la sección de despliegue del artículo anterior, ahora con tres opciones concretas entre las cuales elegir.


Ejemplo práctico: una regla, tres lenguajes

Vuelve una última vez a la regla del agente de soporte de la app de cashback, y observa la misma política expresada en los tres motores. Mantener una sola regla fija mientras cambia el lenguaje es la forma más rápida de ver qué cuesta y qué compra realmente cada lenguaje.


La regla, en prosa: un agente de soporte puede ver la cuenta de un cliente solo si tiene un ticket abierto asignado a él para esa cuenta, y solo durante horario laboral (9:00–18:00).


En Rego (mostrado completo arriba): un default allow := false más una conjunción de condiciones, con una regla auxiliar (ticket_open_for_resource) que busca en datos de tickets precargados — el más largo de código de los tres, y el más explícito sobre cómo ocurre la búsqueda del ticket.


En Cedar (mostrado completo arriba): una sola declaración permit con una cláusula when que referencia campos de context.ticket que el llamador ya entregó — más corto que Rego porque Cedar asume que el llamador ya ensambló el contexto relevante, en vez de buscar él mismo en un documento de datos masivo.


En Casbin, la misma regla se convierte en una expresión matcher más una fila de política, delegando la comprobación del ticket a una función callback del lenguaje anfitrión (HasOpenTicketFor) en vez de expresar la búsqueda en el lenguaje de política en absoluto — el texto de política más corto de los tres, porque Casbin empuja más de la lógica de vuelta al código de aplicación ordinario.


Las diferencias no son accidentales — cada una es la filosofía de su lenguaje hecha visible en un solo ejemplo. Rego quiere la lógica de búsqueda dentro de la política, comprobable y revisable junto a la regla. Cedar quiere la búsqueda hecha antes de que corra la política, manteniendo la política misma demostrable. Casbin quiere la búsqueda delegada afuera, a código en el que ya confías, manteniendo delgada la capa de política. Ninguno de los tres está equivocado; están optimizando cosas distintas, y ahora puedes ver exactamente cuáles.


Probando políticas como código: hacer que “como código” sea cierto en la práctica

Un lenguaje de políticas sin una historia de pruebas no es realmente política como código — es solo política en un formato de archivo distinto. Cada motor se toma esto en serio, con distinta madurez de herramientas:


OPA tiene la historia incorporada más completa: opa test corre tests unitarios estilo tabla escritos en el propio Rego, admite reportes de cobertura (opa test --coverage), y se integra directamente en CI como un solo comando con un código de salida de éxito/fallo — la misma forma que el runner de tests de cualquier otro lenguaje.


Cedar prueba en dos capas. Los tests unitarios ordinarios (afirmar que isAuthorized(request) devuelve la decisión esperada para una tabla de inputs) cubren escenarios específicos igual que los tests de Rego, mientras que el validador de esquema y el conjunto de herramientas Cedar Analysis agregan algo que los tests unitarios estructuralmente no pueden: garantías exhaustivas sobre todos los inputs, no solo los que a un autor de tests se le ocurrió escribir. Un pipeline maduro de Cedar corre ambos — tests unitarios para el comportamiento, análisis para las garantías estructurales.


Casbin no tiene un runner de tests específico de políticas porque el enforcer es una función ordinaria en el lenguaje anfitrión — se prueba igual que se prueba cualquier otra cosa, llamando a Enforce() con solicitudes de prueba dentro de tu framework de tests existente (go test, pytest, jest). Esto es menos ceremonia que los otros dos, pero también significa que Casbin no tiene equivalente a las herramientas de cobertura dedicadas de opa test ni a las garantías formales de Cedar; el rigor es enteramente función de qué tan disciplinado sea ya el conjunto de tests de la aplicación que lo rodea.



Recapitulación

Políticas como código es la respuesta a la pieza que la arquitectura XACML dejó abstracta — cómo se escribe, prueba y despliega realmente el recetario:


  1. Políticas como código es un proceso, no un lenguaje. Control de versiones, revisión de código, tests automatizados, artefactos versionados y despliegue por pipeline — aplicados a los roles de PAP/PRP de la arquitectura XACML. El PDP sigue decidiendo igual por debajo.
  2. OPA/Rego es de propósito general. Un motor, un lenguaje (input + data → documento de decisión), reutilizado tanto en admisión de Kubernetes (Gatekeeper) como en revisiones de configuración de CI (Conftest), mallas de servicio (opa-envoy-plugin) y autorización de aplicaciones, probado con el opa test incorporado.
  3. Cedar cambia generalidad por demostrabilidad. Una gramática restringida permit/forbid sobre una solicitud PARC permite que las políticas se analicen formalmente — probar que no se solapan, verificar contra un esquema — algo que la flexibilidad de Rego vuelve intratable; se distribuye como un crate incrustado o como el servicio gestionado AWS Verified Permissions.
  4. Casbin cambia un modelo fijo por latencia casi nula. Un model.conf configurable más una expresión matcher, incrustado como una llamada a librería en el mismo proceso sin salto de red, excelente para reproducir RBAC/ABAC/ACL de forma barata dentro de un servicio, agnóstico de almacenamiento vía adaptadores.
  5. Los sistemas reales mezclan motores por PDP, no por mandato de toda la empresa — el mismo principio de “elegir por cada punto de decisión” con el que cerró el artículo de XACML, ahora con tres opciones concretas y probadas en producción.

Tres preguntas para autoevaluarte

  1. Explica, con tus propias palabras, por qué Cedar puede ofrecer verificación formal de políticas mientras que Rego generalmente no puede — ¿qué elección de diseño específica marca la diferencia, y qué gana o pierde cada lenguaje por ello?
  2. Un equipo quiere agregar autorización a una única herramienta interna de administración, sin tolerancia para latencia añadida y sin interés en operar un servicio separado. ¿Cuál de los tres motores encaja mejor, y qué dos propiedades estructurales de ese motor (no solo “es más simple”) lo hacen el más adecuado?
  3. Usando la regla del agente de soporte de cashback, explica por qué la misma política tiene la búsqueda del ticket dentro de la política en Rego, pero afuera de la política (en el contexto o un callback) en Cedar y Casbin. ¿Qué cuesta cada elección, y qué compra?

Ejercicios prácticos

  1. Escribe y prueba una política Rego. Instala OPA localmente, escribe la regla del agente de soporte de cashback (o una versión simplificada) como una política Rego, y escribe al menos tres casos de opa test: uno que debería permitir, uno denegado por rol incorrecto, y uno denegado por estar fuera del horario laboral. Corre opa test y confirma que los tres pasan, luego rompe una condición a propósito y confirma que el test correcto lo detecta.
  2. Modela una regla en los tres lenguajes. Elige cualquier regla de acceso de tu propio trabajo (o inventa una con al menos dos condiciones), y escríbela en Rego, Cedar, y un model.conf + línea de política de Casbin. Compara los tres: ¿cuál fue más fácil de escribir, cuál sería más fácil de revisar para alguien no familiarizado con la herramienta, y cuál confiarías más en que detecte un error automáticamente?
  3. Diseña una compuerta de CI. Esboza (en un README, no necesariamente código funcional) cómo debería verse una revisión de pull request para un repositorio de políticas Rego: qué corre, en qué orden, y qué bloquea la fusión. Incluye como mínimo un paso de tests y un paso que detectaría un default allow := true accidental. Compara tu diseño contra la integración real de CI de opa test en la documentación de OPA.

Preguntas frecuentes

¿Qué significa realmente 'políticas como código'?

Significa tratar las reglas de autorización igual que se trata el código de una aplicación: escritas en un lenguaje real, 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 vez de vivir como filas en la base de datos de un panel de administración o como XML irrevisable que nadie diferencia. Las reglas siguen cumpliendo los roles de PAP/PDP de la arquitectura XACML; políticas como código solo cambia cómo se autoran, prueban y despliegan esas reglas.

¿OPA/Rego reemplaza a XACML?

Reemplaza 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 solicitud como JSON, evalúa reglas Rego contra ese input más cualquier dato cargado, y devuelve un documento de decisión. La forma PEP/PDP/PIP/PAP del artículo anterior sigue intacta por debajo — OPA es solo una manera moderna y amigable para el desarrollador de construir el PDP y el PAP.

¿Cuál es la diferencia real entre OPA/Rego y AWS Cedar?

Ambos son motores de autorización de propósito general con una solicitud con forma PARC (principal/acción/recurso más contexto), pero optimizan cosas distintas. Rego es un lenguaje de consulta flexible que puede expresar casi cualquier lógica de política e incluso dar forma a un resultado de decisión arbitrario, lo cual cuesta algo de capacidad de análisis. Cedar restringe deliberadamente su gramática a declaraciones permit/forbid para que el conjunto de políticas pueda analizarse formalmente — probar que no se solapan, detectar reglas inalcanzables, verificar contra un esquema — al costo de ser menos expresivo que Rego para lógica exótica. Elige Rego para máxima flexibilidad en muchos dominios (Kubernetes, CI, infraestructura); elige Cedar cuando quieres específicamente políticas de autorización verificables por máquina para el modelo principal/acción/recurso de una aplicación.

¿Por qué Casbin es distinto de OPA y Cedar?

OPA y Cedar envían un modelo de decisión fijo, alcanzado a través de un límite de solicitud/respuesta (un sidecar, un servicio o una librería invocada con un input). Casbin, en cambio, envía un modelo configurable — escribes un pequeño model.conf que describe tu propia forma de solicitud, forma de política y expresión de coincidencia — y se incrusta directamente en tu aplicación como una llamada a librería, casi siempre sin ningún salto de red. Eso hace a Casbin excepcionalmente bueno para reproducir patrones RBAC, ACL y ABAC simple de forma barata dentro de un solo servicio, pero no es un servidor de políticas independiente por defecto y no tiene equivalente a la lógica de propósito general de Rego ni a la verificación formal de Cedar.

¿Se pueden probar automáticamente estos lenguajes de políticas?

Sí, y hacerlo es exactamente el sentido de llamar a esto 'políticas como código'. OPA incluye `opa test`, un runner de tests unitarios de primera clase para reglas Rego que corre en CI en cada cambio. Cedar incluye un validador que revisa las políticas contra un esquema declarado más un conjunto de herramientas de análisis (Cedar Analysis) que puede probar propiedades como 'estas dos políticas nunca aplican a la vez'. El enforcer de Casbin es una simple llamada a función, así que se prueba con el framework de tests que el lenguaje anfitrión ya usa — `testing` de Go, `pytest` de Python, etc. En todos los casos, el objetivo es el mismo que XACML nunca resolvió bien: atrapar una regla de autorización rota en un pull request en vez de en producción.

¿Tengo que elegir solo uno de estos para todo mi sistema?

No, y en la práctica la mayoría de las organizaciones no lo hace. Es común ver OPA/Gatekeeper aplicando política en la capa de admisión de Kubernetes, Cedar o un equivalente gestionado protegiendo los permisos de recursos de grano fino de una aplicación, y Casbin incrustado dentro de un servicio interno que solo necesita RBAC simple sin dependencia de red. La lección de XACML del artículo anterior sigue aplicando un nivel más arriba: elige la herramienta por cada PDP, según lo que ese PDP realmente necesita decidir, en vez de asumir que un solo motor debe poseer cada decisión de autorización de la empresa.