<p>I use the vanilla Open Policy Agent as a deployment on Kubernetes for handling admission webhooks.</p>
<p>The behavior of multiple policies evaluation is not clear to me, see this example:</p>
<pre><code>## policy-1.rego

package kubernetes.admission

check_namespace {
   # evaluate to true
   namespaces := {&quot;namespace1&quot;}
   namespaces[input.request.namespace]
}

check_user {
    # evaluate to false
    users := {&quot;user1&quot;}
    users[input.request.userInfo.username]
}

allow[&quot;yes - user1 and namespace1&quot;] {
  check_namespace
  check_user
}
</code></pre>
<p>.</p>
<pre><code>## policy-2.rego

package kubernetes.admission

check_namespace {
   # evaluate to false
   namespaces := {&quot;namespace2&quot;}
   namespaces[input.request.namespace]
}

check_user {
    # evaluate to true
    users := {&quot;user2&quot;}
    users[input.request.userInfo.username]
}

allow[&quot;yes - user2 and namespace12] {
  check_namespace
  check_user
}
</code></pre>
<p>.</p>
<pre><code>## main.rego

package system

import data.kubernetes.admission

main = {
  &quot;apiVersion&quot;: &quot;admission.k8s.io/v1&quot;,
  &quot;kind&quot;: &quot;AdmissionReview&quot;,
  &quot;response&quot;: response,
}

default uid = &quot;&quot;

uid = input.request.uid

response = {
    &quot;allowed&quot;: true,
    &quot;uid&quot;: uid,
} {
    reason = concat(&quot;, &quot;, admission.allow)
    reason != &quot;&quot;
}
else = {&quot;allowed&quot;: false, &quot;uid&quot;: uid}
</code></pre>
<p>.</p>
<pre><code> ## example input
{
  &quot;apiVersion&quot;: &quot;admission.k8s.io/v1beta1&quot;,
  &quot;kind&quot;: &quot;AdmissionReview&quot;,
  &quot;request&quot;: {
    &quot;namespace&quot;: &quot;namespace1&quot;,
    &quot;userInfo&quot;: {
        &quot;username&quot;: &quot;user2&quot;
    }
  }
}
</code></pre>
<p>.</p>
<pre><code>## Results

&quot;allow&quot;: [
    &quot;yes - user1 and namespace1&quot;,
    &quot;yes - user2 and namespace2&quot;
]
</code></pre>
<p>It seems that all of my policies are being evaluated as just one flat file, but i would expect that each policy will be evaluated independently from the others</p>
<p>What am I missing here?</p>

<p><em>Files</em> don't really mean anything to OPA, but packages do. Since both of your policies are defined in the <code>kubernetes.admission</code> module, they'll essentially be appended together as one. This works in your case only due to one of the <code>check_user</code> and <code>check_namespace</code> respectively evaluating to undefined given your input. If they hadn't you would see an error message about conflict, since complete rules can't evalutate to different results (i.e. <code>allow</code> can't be both <code>true</code> <em>and</em> <code>false</code>).</p>
<p>If you rather use a separate package per policy, like, say <code>kubernetes.admission.policy1</code> and <code>kubernetes.admission.policy2</code>, this would not be a concern. You'd need to update your main policy to collect an aggregate of the <code>allow</code> rules from all of your policies though. Something like:</p>
<pre><code>reason = concat(&quot;, &quot;, [a | a := data.kubernetes.admission[policy].allow[_]])
</code></pre>
<p>This would iterate over all the sub-packages in <code>kubernetes.admission</code> and collect the <code>allow</code> rule result from each. This pattern is called dynamic policy composition, and I wrote a longer text on the topic <a href="https://blog.styra.com/blog/dynamic-policy-composition-for-opa" rel="nofollow noreferrer">here</a>.</p>
<p>(As a side note, you probably want to aggregate <strong>deny</strong> rules rather than allow. As far as I know, clients like kubectl won't print out the reason from the response unless it's actually denied... and it's generally less useful to know why something succeeded rather than failed. You'll still have the OPA <a href="https://www.openpolicyagent.org/docs/latest/management-decision-logs/" rel="nofollow noreferrer">decision logs</a> to consult if you want to know more about why a request succeeded or failed later).</p>
