# Check compliance and apply the rules that close it

Score a cluster against published frameworks, see which workloads fail each control, and apply the rule that closes it with Kubernetes built-in admission or with Kyverno.

Once a cluster is connected, AuthFI scores it against published compliance frameworks. Every failing control names the workloads that fail it and the rule that would close it. Each rule comes in two forms, so you can apply it with what your cluster already has:

| Apply with                                           | What it needs                                        |
| ---------------------------------------------------- | ---------------------------------------------------- |
| **Kubernetes built-in admission (no extra install)** | Kubernetes 1.30 or later. Nothing else to install.   |
| **Kyverno**                                          | [Kyverno](https://kyverno.io) running in the cluster |

Use this guide to read the score, and to turn a failing control into a rule in your cluster.

## Before you begin

You need:

- A cluster with Zero-Code installed. See [Install Zero-Code on a cluster](/zero-code/install).
- `kubectl`, with permission to create the rule: a `ValidatingAdmissionPolicy` and a `ValidatingAdmissionPolicyBinding` for built-in admission, or a `ClusterPolicy` for Kyverno.

<Steps>

### Open the compliance score

In the AuthFI console, open **Security > Compliance**.

Choose the cluster in **Cluster** and the framework in **Compliance framework**. To score the cluster again, choose **Score again**.

The tiles **Passing**, **Failing** and **We cannot decide** count the controls. The table lists each control with the columns **Control**, **Requires**, **Status**, **Objects scanned** and **Would refuse**.

### Find the failing controls

Choose the **Fail** chip above the table to show only failing controls. Each control has one of [five statuses](#statuses), and only **Pass** counts as a pass.

**Would refuse** shows how many workloads the rule would deny, before you apply it.

### Read a failing control

Select a control's row. A drawer opens with four tabs.

| Tab                         | Shows                                          |
| --------------------------- | ---------------------------------------------- |
| **What failed**             | The workloads that fail, by namespace and name |
| **The rule that closes it** | The rule that closes the control               |
| **Already guarded**         | What already guards the control                |
| **Why this matters**        | Why the control matters                        |

A control you already guard with your own Kyverno policy gets no suggestion. See [How your own Kyverno policies are counted](#how-your-own-kyverno-policies-are-counted).

### Choose how you will apply it

On **The rule that closes it**, under **Apply with**, choose **Kubernetes built-in admission (no extra install)** or **Kyverno**. The rule shown below changes to that form. The same choice sits above the table, beside the button that copies every rule at once.

### Copy the rule and apply it

Choose **Copy for built-in admission** or **Copy as Kyverno**. The rule is copied to the clipboard as JSON, and the console confirms how many rules were copied.

Save it to a file and apply it to the cluster yourself:

```bash
kubectl apply -f authfi-privileged-container.json
```

A built-in admission rule is two objects: a `ValidatingAdmissionPolicy` that holds the check, and a `ValidatingAdmissionPolicyBinding` that puts it in force. They are copied together as one `List`. This is the rule for the privileged containers check:

```json
{
  "apiVersion": "v1",
  "kind": "List",
  "items": [
    {
      "apiVersion": "admissionregistration.k8s.io/v1",
      "kind": "ValidatingAdmissionPolicy",
      "metadata": { "name": "authfi-privileged-container" },
      "spec": {
        "failurePolicy": "Fail",
        "matchConstraints": {
          "resourceRules": [
            { "apiGroups": [""], "apiVersions": ["*"], "operations": ["CREATE", "UPDATE"], "resources": ["pods"] },
            {
              "apiGroups": ["apps"],
              "apiVersions": ["*"],
              "operations": ["CREATE", "UPDATE"],
              "resources": ["deployments", "statefulsets", "daemonsets"]
            },
            {
              "apiGroups": ["batch"],
              "apiVersions": ["*"],
              "operations": ["CREATE", "UPDATE"],
              "resources": ["jobs", "cronjobs"]
            }
          ]
        },
        "variables": [
          {
            "name": "spec",
            "expression": "has(object.spec.jobTemplate) ? object.spec.jobTemplate.spec.template.spec : (has(object.spec.template) ? object.spec.template.spec : object.spec)"
          },
          {
            "name": "containers",
            "expression": "variables.spec.containers + (has(variables.spec.initContainers) ? variables.spec.initContainers : []) + (has(variables.spec.ephemeralContainers) ? variables.spec.ephemeralContainers : [])"
          }
        ],
        "validations": [
          {
            "expression": "variables.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || !c.securityContext.privileged)",
            "message": "EADMIT001: Containers must not be privileged",
            "reason": "Forbidden"
          }
        ]
      }
    },
    {
      "apiVersion": "admissionregistration.k8s.io/v1",
      "kind": "ValidatingAdmissionPolicyBinding",
      "metadata": { "name": "authfi-privileged-container" },
      "spec": { "policyName": "authfi-privileged-container", "validationActions": ["Warn", "Audit"] }
    }
  ]
}
```

The Kyverno form of the same rule is one `ClusterPolicy`:

```json
{
  "apiVersion": "kyverno.io/v1",
  "kind": "ClusterPolicy",
  "metadata": { "name": "authfi-privileged-container" },
  "spec": {
    "validationFailureAction": "Audit",
    "background": true,
    "rules": [
      {
        "name": "privileged_container",
        "match": { "any": [{ "resources": { "kinds": ["Pod"] } }] },
        "validate": {
          "message": "EADMIT001: Containers must not be privileged",
          "pattern": {
            "spec": {
              "=(containers)": [{ "=(securityContext)": { "=(privileged)": false } }],
              "=(initContainers)": [{ "=(securityContext)": { "=(privileged)": false } }],
              "=(ephemeralContainers)": [{ "=(securityContext)": { "=(privileged)": false } }]
            }
          }
        }
      }
    ]
  }
}
```

Every rule's message starts with a stable code, such as `EADMIT001`, so you can search your logs for it whatever the wording.

Every exported rule only watches. A built-in admission binding is `["Warn", "Audit"]`: whoever applies a failing object sees a warning, and the API server's audit log records it. A Kyverno policy is in `Audit`. Neither denies anything, so applying a rule cannot break a deployment.

To copy the rules for every control shown in the table at once, choose **Copy the rules for built-in admission** or **Copy the rules as Kyverno** above the table. One rule copies as one object, and several copy as one `List`.

### Switch the rule to deny

When the warnings and reports are clean, switch the rule to deny yourself and apply it again:

- Built-in admission: set the binding's `validationActions` to `["Deny"]`.
- Kyverno: set `validationFailureAction` to `Enforce`.

</Steps>

AuthFI reads, scores and writes the rule. You apply it, and you decide what is admitted to your cluster.

## Check that it works

For a built-in admission rule, list the rule, then ask the API server to admit a failing object without creating it:

```bash
kubectl get validatingadmissionpolicy authfi-privileged-container
kubectl run probe --image=busybox --privileged --dry-run=server
```

While the binding is `["Warn", "Audit"]`, the second command prints a warning carrying `EADMIT001`. Once the binding is `["Deny"]`, the same command is refused with that code.

For a Kyverno policy, open **Security > Policy** and choose the **Admission** chip. Your policy is listed beside every other rule in force, marked with whether it denies or only monitors. A policy in `Audit` is counted as monitoring. Once it is in `Enforce`, it is counted as a guard and the control is covered by you.

## Frameworks

| Framework                         | Version |
| --------------------------------- | ------- |
| CIS Kubernetes Benchmark          | 1.9     |
| SOC 2                             |         |
| ISO/IEC 27001                     | 2022    |
| NIST SP 800-207, Zero Trust       |         |
| PCI DSS                           | 4.0     |
| HIPAA Security Rule               |         |
| OWASP Top 10                      | 2021    |
| OWASP API Security Top 10         | 2023    |
| OWASP Top 10 for LLM Applications | 2025    |

## Checks

| Check                   | Fails when                                                |
| ----------------------- | --------------------------------------------------------- |
| Privileged containers   | A container runs privileged                               |
| Privilege escalation    | A container may gain more privileges than its parent      |
| Running as root         | A container is not required to run as a non-root user     |
| Host namespaces         | A pod shares the host's network, process or IPC namespace |
| Host paths              | A pod mounts a path from the node                         |
| Wildcard RBAC           | A role grants `*` on verbs or resources                   |
| Cluster-admin bindings  | A subject is bound to `cluster-admin`                     |
| Service account tokens  | A token is mounted into a pod that does not need it       |
| Calls across namespaces | A workload was seen calling into another namespace        |

The last check comes from traffic the nodes observed, not from configuration. It tells you what is happening. The other checks tell you what is permitted.

## Statuses

| Status                    | Value          | Meaning                                                                      |
| ------------------------- | -------------- | ---------------------------------------------------------------------------- |
| **Pass**                  | `pass`         | Objects were checked and none fails                                          |
| **Fail**                  | `fail`         | At least one object fails. The failing objects are listed.                   |
| **Nothing of this kind**  | `no_objects`   | Nothing in the cluster is in scope for this control                          |
| **We cannot decide**      | `unanswerable` | The control is about something a cluster cannot show, such as a staff policy |
| **Cluster not reporting** | `not_synced`   | The cluster has not reported recently enough to answer                       |

A control that cannot be answered from the cluster is shown as **We cannot decide**. It is not counted as passing, so the score reflects only what was checked.

## How your own Kyverno policies are counted

If the cluster runs Kyverno, AuthFI reads your `ClusterPolicy` and `Policy` objects.

| Your policy                            | Counted as                                     |
| -------------------------------------- | ---------------------------------------------- |
| Validates the same thing, in `Enforce` | A guard. The control is covered by you.        |
| Validates the same thing, in `Audit`   | Monitoring. It reports and denies nothing.     |
| Something else                         | Listed on **Security > Policy**, with its mode |

Your policies appear on **Security > Policy** beside every other rule in force. Each is marked with whether it denies or only monitors.

## How current the score is

The score is computed from what the cluster last reported. The **Store** tile shows **current** or **stale**.

If a cluster has not reported for an hour, the page says the result is stale and shows when it was last complete.

## Next steps

<Cards>
  <Card href="/zero-code/install" title="Install Zero-Code on a cluster">
    Connect the cluster that the score is computed from.
  </Card>
  <Card href="/zero-code/enforcement" title="How enforcement works">
    What the nodes decide, and how monitor mode and enforce mode fit together.
  </Card>
  <Card href="/security/audit" title="The audit record">
    Search every decision, and send it to your log tools.
  </Card>
</Cards>
