Skip to content

All posts

KubernetesKyverno

How to collect Kubernetes compliance evidence for auditors

Ritesh Patel

Kubernetes: How to collect Kubernetes compliance evidence for auditors

Collect Kubernetes compliance evidence from two records. Kyverno policy reports show the current pass or fail results for each rule. The kube-apiserver audit log shows which requests were made and by whom. Current-state policy reports are not an audit trail by themselves, so export both on a schedule and keep the files with your control mapping.

Why you need two records, not one

An auditor asks two questions. What did the control look like, and did it operate over the period under review? A policy report answers the first. It is a snapshot of the cluster’s rule results. The audit log answers the second, because the API server writes an event for each request you choose to record.

Kyverno policy reports kube-apiserver audit log
What it holds A result (pass, skip, fail, warn or error) for each rule applied to each resource Requests to the API server, at the audit level you set
History None. Reports “always represent the current state of the cluster” Events are written to the file at --audit-log-path
How it gets created Kyverno generates it for validate, mutate, generate and verifyImages rules You enable it with --audit-policy-file. Without that flag, no events are logged
Answers What is the control state now? Who changed what, and when?

Use the report for state and the log for activity. Neither one proves the control was adequate. That judgement comes from the mapping you write, which the rest of this post covers.

Turn on API server audit logging

Checked against the Kubernetes audit documentation (fetched 2026-11-03). The page’s version selector lists v1.37 as current, but the page does not state which version its content describes.

  1. Write an audit policy file. The page says the rules field must be provided, and that a policy with no rules is illegal.
# /etc/kubernetes/audit/policy.yaml (example path)
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: Metadata
  1. Pass the policy file and a log path to kube-apiserver. The page says --audit-log-path set to - writes to standard out.
kube-apiserver \
  --audit-policy-file=/etc/kubernetes/audit/policy.yaml \
  --audit-log-path=/var/log/kubernetes/audit/audit.log
  1. If the control plane runs kube-apiserver as a static Pod, mount the host paths for both the policy file and the log file. The page says this is needed “so that audit records are persisted.”

  2. Budget memory. The page notes that audit logging “increases the memory consumption of the API server” because context is stored for each request.

Rules are checked in order, and the first matching rule sets the audit level for an event. Put narrow rules above broad ones, or a broad Metadata rule will hide the detail you wanted to capture.

Export Kyverno policy reports

Checked against the Kyverno policy reports documentation (fetched 2026-11-03). The Kyverno version is not stated on source page.

  1. List namespaced reports across the cluster.
  2. List cluster-scoped reports.
  3. Export every report kind as YAML, and save the output to a file.
kubectl get policyreport -A
kubectl get clusterpolicyreport
kubectl get reports -A -o yaml

Each report holds a top-level scope (the resource the report covers), a results list and a summary. A result names the policy and rule, the result value and whether the policy is scored. The page’s example uses wgpolicyk8s.io/v1alpha2 as the API version.

apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
results:
  - policy: disallow-host-namespaces
    rule: host-namespaces
    result: fail
    scored: true
    source: kyverno
scope:
  apiVersion: v1
  kind: Pod
  name: kube-apiserver-kind-control-plane
  namespace: kube-system

Read the five result values

The docs define each value, and auditors will read them literally, so use the definitions as written.

Result Kyverno definition
pass The resource was applicable to a rule and the pattern passed evaluation
skip Preconditions were not satisfied, or an applicable PolicyException exists
fail The resource failed the pattern evaluation
warn A fail converted to a warning because the policy sets policies.kyverno.io/scored to "false"
error Variable substitution failed outside of preconditions, so the rule could not be fully evaluated

A warn is not a pass. It is a failing result that the policy author chose not to score, so the auditor should see the annotation in the policy source.

Keep dated snapshots

Because reports keep no history, the export time is part of the evidence. The docs say reports “do not record historical information,” so a fail that was fixed before your next export leaves no trace in the report.

  1. Run the export from the previous section on a schedule you set, such as nightly or at each release.
  2. Name each file with the run date and time in UTC, so an auditor can match it to a period.
  3. Store the files where they cannot be edited after the run. Keep the policy source in version control at the same time, so the rule that produced each result can be identified.

Snapshots show what the cluster reported at a moment in time. They do not show what happened between two snapshots, which is why the audit log is the second record.

Record exceptions as skip results

The Kyverno docs say exceptions can change a fail result to skip. Two flags control them. The guide says PolicyExceptions are disabled by default, and that you enable them by setting enablePolicyException to true. You must also set exceptionNamespace to name the namespaces where exceptions may be used.

  1. Enable exceptions with enablePolicyException, and list the allowed namespaces in exceptionNamespace. The guide does not show how to set these flags, so check the flag reference for your install method.
  2. Create each exception in an allowed namespace, and scope it to the resources it covers.
  3. Confirm that the matching resources now show skip in the report, and keep the exception manifest with its approval record.

The legacy kyverno.io PolicyException is deprecated as of Kyverno v1.19 and will be removed in v1.20 together with ClusterPolicy. Use the policies.kyverno.io PolicyException instead.

What a cluster scan cannot prove

A scan shows the present, and an auditor often needs the past. Three gaps matter most.

First, a report cannot show a control that was broken and then fixed. If a deployment ran with a host namespace for two weeks and was corrected yesterday, the report shows only the corrected state, which is why dated snapshots and the audit log matter.

Second, a skip does not explain itself. The result does not say whether a precondition failed or an exception applied, so record the reason next to the exception.

Third, neither record holds the approval. The reports and the audit log do not contain who approved an exception or why. Keep those in your ticketing system or pull request history, and link each exception to them by name.

Map each control to the evidence it produces

Write this mapping before the audit, not during it. Each row names the cluster setting, the artifact you will export and the record that links them.

Control objective Cluster setting Evidence to export
Privileged or host-access workloads are restricted A Kyverno validate rule Dated policy report results for that rule, and the policy file from version control
Changes to API resources are attributable An audit policy rule at Metadata or higher Audit log events with the requesting user, verb and resource
Exceptions are approved and bounded A PolicyException in an allowed namespace skip results for the matching resources, and the approval in your ticket system

For a framework-specific view, see the SOC 2 controls post, which shows where policy evidence stops and process evidence begins.

Enforcing this with Kyverno

Kyverno creates reports automatically for validate, mutate, generate and verifyImages rules, so the evidence comes from the policies you already run. Keep those policies in version control, export the reports on a schedule, and keep the exception manifests with their approvals. The steps above need only Kyverno, kubectl and the kube-apiserver flags.

For the broader approach to continuous compliance, start with the continuous compliance hub. For a regulated payments team’s worked example on Kyverno, read how Razorpay automates RBI compliance on Kubernetes with Kyverno. The product page for Nirmata Control Hub is here.

Sources checked

Frequently asked questions

Is a Kyverno policy report enough to pass an audit?

Not on its own. The Kyverno docs say reports always represent the current state of the cluster and do not record historical information. A control that failed last month and passes today shows only a pass. Keep dated exports of the reports, and pair them with the kube-apiserver audit log, which records requests as they happen.

What does a skip result mean in a policy report?

The Kyverno docs define skip in two ways: the rule's preconditions were not satisfied, or an applicable PolicyException exists. The result value alone does not tell you which. Check the rule's preconditions and the exceptions in the namespace before you explain a skip to an auditor.

Why does my audit log show no events?

Check two flags. The Kubernetes docs say that if --audit-policy-file is omitted, no events are logged, and that if --audit-log-path is not set, the log backend is disabled. The policy file must also contain a rules field, because a policy with zero rules is treated as illegal.

Does the audit log record request and response bodies?

Only at the higher levels. Metadata records the requesting user, timestamp, resource and verb, but not the request or response body. Request adds the request body. RequestResponse adds both the request and response bodies. Choose the level per rule, and remember that the first matching rule sets the level for an event.

Should I still use the kyverno.io PolicyException?

Use the policies.kyverno.io PolicyException instead, as the Kyverno guide says. The legacy kyverno.io version is deprecated as of Kyverno v1.19 and will be removed in v1.20, together with ClusterPolicy. Plan to migrate existing exceptions before the upgrade that removes them.

Ritesh Patel, Founder & VP Product, Nirmata

Ritesh Patel is a founder of Nirmata and leads its products. He writes about governing AI coding agents, MCP security and Kubernetes policy as code.

LinkedIn

Nirmata Control HubOne control plane for Kyverno policies.
ShareXLinkedIn

Stay up-to-date with the latest in AI, governance, and infrastructure

Subscribe to our free monthly newsletter to get industry news, DevOps tips, and articles right to your inbox on Kyverno, containerization, Kubernetes deployment & management and more.