Rules
Rules in Sentinel are a first-class concept. Within a policy, rules serve a few purposes:
- They make complex logic more understandable by allowing said logic to be broken down.
- They allow assertion of this logic through testing of the rule's contents.
- They facilitate reporting of data as rules get published as part of the policy trace.
A rule functions in ways similar to both a variable and a function: they hold a value, but are lazily evaluated; a rule's value is not assigned until the first time it's referenced in a policy. Additionally, while the value of evaluated rules will be available within a policy's trace after it's evaluated, values of variables - and the return value of functions - are not. Finally, a rule value is memoized - further references to the rule will not change its result.
Rules can hold more than just boolean data. For more advanced rule patterns, see the language reference.
Writing Rules
Let's look at the Sentinel policy we wrote in the previous section:
hour = 4
main = rule { hour >= 0 and hour < 12 }
The logic in this policy is straightforward and easy to understand. However, it is common for policy logic to become much more complicated. Rules are the key abstraction for making complex logic understandable. We can turn the policy above into a set of rules:
hour = 4
past_midnight = rule { hour >= 0 }
before_noon = rule { hour < 12 }
main = rule {
past_midnight and
before_noon
}
This policy is easier to read. Our original policy was already quite simple, but it is easy to imagine a more complex policy becoming much more readable by extracting logical expressions into a set of rules.
Rules don't just exist to make a policy more readable. They're also a testing and debugging point. For testing, you can assert that certain rules are certain values given a specific input to verify that your policy works as you expect. Testing and debugging are covered in later sections.
Conditional Rules
In many cases, a rule is only valid when something else is also true.
Consider the human language statement "before you eat, you must wash your hands."
The rule "you must wash your hands" is only valid "before you eat". In any
other scenario, the rule is meaningless.
This is also true in a technical context. Imagine the rule "if the driver
is Docker, you must not expose any ports." For this example, assume
rules is_docker
and has_no_exposed_ports
exist. You can write this rule
like this:
main = rule when is_docker { has_no_exposed_ports }
When is_docker
is true, the rule is evaluated as normal. However,
when is_docker
is false, the rule always returns true
, because the
logic of the rule is meaningless.
Let's learn how to create more complex rules with logical expressions.