How to use parameters in Rego rules? I would have something like this:
deny[reason] {
input.request.kind.kind == "Route"
not valid_route_request[label]
reason := sprintf("missing or wrong router selector label: %v", [label])
}
valid_route_request[label] {
requester := input.request.userInfo.username
some i # iterate on all users
requester == data.kubernetes.users[i].metadata.name
label := input.request.object.metadata.labels["router-selector"]
label == data.kubernetes.users[i].metadata.annotations[router_selector_key]
}
where label
is used to build the error message. I get error from OPA: var label is unsafe ...
Generally speaking, it is still not clear to me how to pass parameters in Rego.
Your example is almost correct--the problem you're facing is that label
is "unsafe".
TLDR; assign label
inside the deny
rule:
deny[reason] {
input.request.kind.kind == "Route"
label := input.request.object.metadata.labels["router-selector"]
not valid_route_request[label]
reason := sprintf("wrong 'router-selector' label: %v", [label])
}
deny[reason] {
input.request.kind.kind == "Route"
not input.request.object.metadata.labels["router-selector"]
reason := "missing 'router-selector' label"
}
Note, I've created TWO deny rules. One for the case where the path input.request.object.metadata.labels["route-selector']
is undefined and the other for an invalid value.
Why does OPA generate a safety error in the original example? Safety is a property of Rego that ensures that all variables can be assigned a finite number of values. To be considered "safe", a variable must appear as the output of at-least-one non-negated expression.
Here are some examples that are all safe:
# 'a' is assigned to the value referenced by input.foo
a := input.foo
# 'x' is assigned to the keys of the collection referenced by input.foo
input.foo[x]
# 'label' is is assigned to the keys of the collection referenced by valid_route_request
valid_route_request[label]
# 'x' is safe because it is assigned outside the negated expression
x := 7; not illegal_numbers[x]
Here are examples of unsafe expressions:
# 'x' is unsafe because it does not appear as an output of a non-negated expression
not p[x]; not q[x]
# 'y' is unsafe because it only appears as a built-in function input
count(y)
Safety errors can also occur with variables that appear in the head of the rule:
# 'msg' is unsafe because it is not assigned inside the body of the rule.
deny[msg] {
input.request.kind.kind == "BadKind"
}
Safety is important as it ensures that OPA can enumerate all of the values that could be assigned to the variable. If the variable is unsafe it means there could be an infinite number of variable assignments. In your example, the statement valid_route_request
generates a set of values (labels?). The not valid_route_request[label]
statement in the deny
rule is unsafe because label
is not assigned elsewhere in the deny
rule (and label
does not appear in the global scope presumably.) This actually becomes a bit clearer if you include 'some' in the deny rule:
deny[reason] {
some label
input.request.kind.kind == "Route"
not valid_route_request[label]
reason := ...
}
This rule says (in English):
reason is in deny if for some label, input.request.kind.kind equals Route and label is not in valid_route_request, and ...
Technically there would be an infinite number of assignments to label
that satisfy this rule (e.g., the string "12345" would NOT be contained in valid_route_request
and so would "123456" and so would ...)