5 consejos para usar el lenguaje Rego con Open Policy Agent (OPA)
18 de septiembre de 2020
0 minutos de lecturaNota del editor
Este blog se publicó originalmente en fugue.co. Fugue se unió a Snyk en 2022 y es un componente clave de Snyk IaC.
En Fugue, nos encanta Open Policy Agent (OPA), y hemos escrito mucho código Rego para proteger los recursos en la nube. Por eso, reunimos las lecciones más valiosas que aprendimos en el proceso. También puedes usar OPA y el lenguaje Rego para habilitar políticas como código y aplicar automáticamente políticas codificadas.
1. Las leyes de De Morgan serán tus mejores aliadas
Si tuviera que limitar esta lista a un solo punto, sería este. Las leyes de De Morgan son dos reglas de transformación. Aplicadas a Rego, te permiten transformar una regla y mantener exactamente el mismo comportamiento.
Suelen escribirse así:
O, en código:
Pero quizá un ejemplo lo aclare mejor. Las dos afirmaciones siguientes son equivalentes y ejemplifican la aplicación de la primera ley:
Esta no es una pizza con jamón y champiñones.
Esta pizza no tiene jamón o no tiene champiñones.
Te permiten transformar cláusulas AND en cláusulas OR y viceversa, sin cambiar el comportamiento del código. Un momento —podrías preguntar—, si estas transformaciones no cambian el comportamiento de mi código, ¿para qué sirven?
La idea clave es que Rego, como lenguaje de consulta, se basa principalmente en disyunciones (o afirmaciones OR). Por ejemplo, comprobar si alguien del grupo está capacitado para cortar una pizza se puede escribir así:
Pero ¿cómo comprobamos si todas las personas del grupo están capacitadas para cortar pizzas? ¿Podemos, mmm, crear primero un grupo con las personas capacitadas y luego comprobar su tamaño?
Esto no es muy bueno. Además de ser difícil de leer, una afirmación como esta es difícil de optimizar para un planificador de consultas, ya que tenemos que contar estos conjuntos; incluso si podríamos detenernos en cuanto encontramos a una persona menor de edad en el grupo.
Aquí es donde entran en juego las leyes de De Morgan: nos indican que siempre escribimos las consultas como una disyunción al negar algunas partes. Solo tenemos que agregar un enunciado auxiliar para negar el enunciado original:
Y luego escribir la política es sencillo:
¡Listo! Es eficiente y fácil de leer.
Conclusión: si parece difícil escribir una política Rego, considera siempre si es más fácil escribir la política negada y luego volver a negarla. ¡Viva la lógica!
2. ¿or o any?
Como acabamos de ver, Rego es más fácil de leer y escribir si colocas las afirmaciones OR al principio y las afirmaciones AND, una tras otra, dentro de los cuerpos de las reglas. Sin embargo, como programadores, no suele pasar mucho tiempo antes de encontrar ese caso en el que lo idiomático no funciona bien.
En Rego, esto suele ocurrir cuando el cuerpo de una regla ya tiene una buena cantidad de consultas. Por ejemplo:
¿Y si también queremos permitir porciones de pizza? Podemos duplicar todo el cuerpo:
Bueno, eso no es muy bueno. En la mayoría de los casos podemos introducir una regla auxiliar:
En este caso funciona bien. Sin embargo, a veces tenemos tantas reglas auxiliares que hasta cuesta encontrarles un buen nombre. En esos casos poco frecuentes, podemos fingir que en Rego existe una afirmación OR llamada any:
Esta es una forma perfectamente legible de escribirlo. Si usas any con moderación, puede facilitarte mucho la vida.
3. Comprensiones any y all
Siguiendo por el mismo camino, any y all son extremadamente útiles en más casos, especialmente con las comprensiones de listas. Estas dos funciones juntas son mucho más que la suma de sus partes.
Si volvemos al primer ejemplo, así podemos usar all junto con una comprensión de lista para expresar la misma política:
Creo que esta es tan fácil de leer como la versión que construimos siguiendo las leyes de De Morgan. Cuál es más idiomática suele depender del contexto.
¿Es más fácil razonar sobre la política negada? Entonces seguiremos la transformación de De Morgan.
¿O es más fácil pensar en los elementos que vamos a controlar como una lista o colección? En ese caso, una comprensión de lista es adecuada.
4. Usa walk para recorrer árboles de documentos completos
Las consultas Rego siempre terminan, lo cual es excelente, pero tiene un costo: por lo general, no es posible escribir reglas recursivas.
¿Por qué son necesarias las reglas recursivas? Son muy útiles para trabajar con documentos de entrada recursivos, como árboles profundamente anidados. Por suerte, OPA proporciona algunas funciones integradas para ayudarte.
Por ejemplo, podríamos estar escribiendo una política para monitorear hilos de discusión:
¿Cómo obtenemos los mensajes en Rego? Podemos probar algo como esto:
Esto funciona bien con niveles de anidamiento finitos, pero no siempre podemos dar eso por sentado y, además, no es muy fácil de leer. En su lugar, puedes usar walk para recorrer recursivamente el árbol y obtener todos los campos message:
Una vez que tenemos esos mensajes en una regla Rego común, podemos escribir una política idiomática para denegar los hilos de discusión que contengan malas palabras.
Un ejemplo del mundo real en el que esto ocurre son los módulos secundarios de Terraform: aquí usamos walk en Regula para aplanar los módulos secundarios de una forma muy similar a como se aplanan los mensajes de un hilo de discusión.
5. Carga dinámica de paquetes y reglas
Uno de los aspectos interesantes del diseño de Rego es que todo el «universo» de reglas y datos está anidado en el mismo documento. Ya sea que accedas a la entrada del usuario, a datos de archivos JSON o YAML, o a reglas de tus paquetes, todo son referencias:
Como podemos enumerar referencias en Rego mediante variables o comodines, podemos inspeccionar dinámicamente el conjunto de paquetes que se cargó. Esto te permite crear una arquitectura en la que solo tienes que agregar nuevos archivos de políticas y resumirlos todos en un informe.
Colocar las políticas en paquetes separados también tiene la ventaja de que se pueden depurar por separado y no hay posibilidad de que los nombres de las reglas entren en conflicto. Las colocaremos todas en el espacio de nombres data.policies. La primera usa las leyes de De Morgan para comprobar si hay queso en una pizza:
La segunda política verifica que dos personas no empiecen a pelearse por la última porción:
Una vez que acordamos un espacio de nombres para nuestras políticas, resumirlas es relativamente sencillo: buscamos cada deny en data.policies y generamos un informe legible para las personas.
Junto con algo de jq para extraer los valores concretos, esto puede generar un informe bien presentado directamente en tu terminal. Aquí usaremos -I para leer la entrada desde stdin.
Hay muchos otros trucos avanzados que se pueden hacer accediendo a los paquetes como datos; por ejemplo, pasar input a un formato diferente según si un paquete declara input_version = 1 o input_version = 2. ¡Todo dentro de Rego!



