Rego

What is Rego?

Rego is a high-level, declarative policy language designed to be expressive, flexible, and human-readable. It is used by OPA to define policies that determine whether certain actions should be allowed or denied based on input data. Rego policies are evaluated by OPA to produce decisions, such as "allow" or "deny," that can be enforced across different systems and applications.

Rego is particularly powerful in cloud-native environments because it allows you to express complex policy decisions in a concise and manageable way. It is designed to work with JSON data, making it a natural fit for modern applications and services.

Basic Structure of a Rego Policy

A Rego policy is composed of a set of rules, where each rule defines a condition that must be met for the policy to return a specific result. The basic components of a Rego policy include:

  • Package Declaration: Defines the namespace for the policy.

  • Rules: Logical conditions that return a boolean value (true or false) or other data types.

  • Input: The data against which the policy is evaluated.

  • Expressions: Logical and arithmetic operations used within rules.

Example of a Simple Rego Policy:

package example

default allow = false

allow {
    input.user == "alice"
    input.action == "read"
}
  • Package: The package example statement defines the namespace under which the rules are defined.

  • Default Rule: The default allow = false statement sets the default value of the allow rule to false, meaning actions are denied unless explicitly allowed.

  • Rule: The allow rule checks whether the input.user is "alice" and the input.action is "read". If both conditions are true, the rule returns true, allowing the action.

Understanding Rego Syntax and Basic Concepts

Rego is a declarative language, meaning it describes what should be done rather than how to do it. Here are some key concepts to understand when writing Rego policies:

  • Rules: Rules are the core of Rego policies. They define conditions that must be satisfied for the rule to return true or a specific value.

  • Variables: Rego uses variables to store values and results of expressions. Variables are created by assignment (=) and can be used in subsequent expressions.

  • Expressions: Expressions are logical or arithmetic statements that return a value. They can involve comparisons (==, !=, <, >, etc.), arithmetic operations (+, -, *, /), and functions.

  • Input: The input keyword represents the data that is passed to the policy for evaluation. It is typically a JSON object.

Example:

package example

allow {
    input.user == "alice"
    input.role == "admin"
}

In this example, the allow rule will return true if the input.user is "alice" and the input.role is "admin".

Working with Collections: Arrays, Objects, and Sets

Rego supports working with various data structures, including arrays, objects, and sets, which are essential for writing more complex policies.

  • Arrays: Ordered collections of values. Access elements using indexing (array[index]).

  • Objects: Unordered collections of key-value pairs. Access values using keys (object.key).

  • Sets: Unordered collections of unique values. Rego provides special syntax for working with sets.

Example:

package example

allow {
    input.user == "alice"
    input.roles[_] == "admin"  # Checks if "admin" is in the list of roles
}

In this example, input.roles is an array, and the rule checks if "admin" is one of the roles assigned to the user.

Writing Complex Policies with Logical Operators

Rego allows the use of logical operators (and, or, not) to combine multiple conditions within a rule.

Example:

package example

allow {
    input.user == "alice"
    input.action == "read"
    not input.resource == "restricted"  # Allow only if resource is not "restricted"
}

In this policy, the allow rule returns true if the user is "alice", the action is "read", and the resource is not "restricted".

Functions in Rego

Rego provides built-in functions for common tasks such as string manipulation, data aggregation, and type conversion. You can also define custom functions using Rego rules.

Example: Using Built-in Functions

package example

allow {
    input.user == "alice"
    count(input.roles) > 2  # Allow only if user has more than 2 roles
}

In this example, the count function is used to determine the number of roles assigned to the user.

Example: Custom Function

package example

is_admin(user) {
    user.role == "admin"
}

allow {
    is_admin(input.user)
}

Here, a custom function is_admin is defined to check if a user has the admin role, and it is used in the allow rule.

Debugging Rego Policies

When writing Rego policies, it’s important to test and debug them to ensure they work as expected. OPA provides several tools to assist with debugging:

  • Tracing: Use the trace function to output information during policy evaluation.

  • Explanation: The --explain flag in the OPA CLI provides detailed information on how a policy was evaluated.

  • Testing: Rego allows you to write test cases to validate your policies (covered in a later lesson).

Example: Using Trace for Debugging

package example

allow {
    trace("Evaluating if user is alice")
    input.user == "alice"
}

Running this with tracing enabled (opa eval --explain=full ...) will show the trace output, helping you understand the evaluation process.

Summary

In this lesson, you’ve been introduced to Rego, the policy language of OPA. You learned about its basic structure, how to write simple policies, and how to use Rego’s syntax and operators. You also explored working with collections, writing complex policies, and using functions in Rego.

Last updated