Try it free

Ingest Kyverno compliance findings

  • Latest Dynatrace
  • How-to guide
  • Published May 18, 2026

Ingest Kyverno compliance findings into Dynatrace as security events. With runtime context from Dynatrace, you can analyze Kubernetes policy results together with the services and workloads they affect.

Get started

Overview

The Dynatrace integration with Kyverno allows you to unify and contextualize Kubernetes policy and compliance findings with other security data on the Dynatrace platform.

Kyverno is a Kubernetes-native policy engine that validates, mutates, and generates resource configurations. When validating policies are deployed in a cluster, Kyverno evaluates targeted resources and produces standardized OpenReports reports.

This integration deploys a custom OpenTelemetry Collector distribution that reads Kyverno OpenReports, transforms policy results into Dynatrace security events, and pushes them to the OpenPipeline security events ingest endpoint.

Dynatrace maps the ingested data to runtime entities that are part of your monitored environment for unified analysis, prioritization, and orchestration.

Use cases

With the ingested data, you can accomplish various use cases, such as

  • Visualize and analyze security findings
  • Discover coverage gaps in security findings
  • Automate and orchestrate security findings

Requirements

See below for the Kyverno and Kubernetes and Dynatrace requirements.

Kyverno and Kubernetes requirements

  • A running Kubernetes cluster version 1.26 or later.
  • kubectl configured with permissions to create ClusterRole and ClusterRoleBinding resources.
  • Helm 3 or 4.
  • Kyverno installed with OpenReports enabled.
  • Validating Kyverno policies deployed in the cluster. For policy types that produce reports, see Which Kyverno policy types produce reports?.
Enable OpenReports and configure Kyverno metrics

Enable OpenReports and configure Kyverno controllers to send metrics over OTLP gRPC with the following minimum Helm values:

openreports:
enabled: true
installCrds: true
admissionController:
metering:
disabled: false
config: grpc
port: 4317
collector: otel-collector.dynatrace.svc.cluster.local
backgroundController:
metering:
disabled: false
config: grpc
port: 4317
collector: otel-collector.dynatrace.svc.cluster.local
cleanupController:
metering:
disabled: false
config: grpc
port: 4317
collector: otel-collector.dynatrace.svc.cluster.local
reportsController:
metering:
disabled: false
config: grpc
port: 4317
collector: otel-collector.dynatrace.svc.cluster.local

For the full list of metrics emitted by Kyverno, see Kyverno metrics. To decide which policy types to deploy, see Kyverno policy types.

Dynatrace requirements

Generate a Dynatrace access token and save it for later. For details, see Dynatrace API - Tokens and authentication.

Token scopePurpose

openpipeline.events_security

Ingest security events through /platform/ingest/v1/security.events.

metrics.ingest

Send OTLP metrics to Dynatrace.

logs.ingest

Send OTLP logs to Dynatrace.

openTelemetryTrace.ingest

Send OTLP traces to Dynatrace.

Activation and setup

To integrate Kyverno OpenReports with Dynatrace, deploy the custom Dynatrace Security Events Collector in your Kubernetes cluster.

The required collector image is available at

ghcr.io/dynatrace-oss/dynatrace-security-events-collector:latest

This collector image can ingest, parse, and transform Kyverno OpenReports into Dynatrace security events that follow the Dynatrace Semantic Dictionary.

Use one of the following deployment approaches.

Recommended

Use this option if you already have the OpenTelemetry Operator installed or if you prefer CRD-based lifecycle management.

1. Prepare the environment
  1. Create the namespace.

    kubectl create namespace dynatrace
  2. Create a Kubernetes Secret with your Dynatrace endpoint, API token, and cluster name.

    kubectl -n dynatrace create secret generic dynatrace-secrets \
    --from-literal=endpoint=https://<your-environment>.dynatrace.com \
    --from-literal=api-token=dt0c01.XXXXXXXX \
    --from-literal=cluster-name=<your-cluster-name>

    Replace:

    • <your-environment> with your Dynatrace environment.
    • dt0c01.XXXXXXXX with the token generated for this integration.
    • <your-cluster-name> with a name that identifies this cluster in Dynatrace (for example, prod-eu-1). The cluster name is optional but recommended for multi-cluster setups.
  3. Apply RBAC permissions.

    The collector needs read access to OpenReports resources and core Kubernetes objects to enrich security events with workload context. The rbac.yaml manifest creates a ServiceAccount, a ClusterRole, and a ClusterRoleBinding with the required read-only permissions.

    curl -O https://raw.githubusercontent.com/dynatrace-oss/dynatrace-security-events-collector/refs/heads/master/manifests/rbac.yaml
    kubectl apply -f rbac.yaml
2. Install the OpenTelemetry Operator

Skip this step if the OpenTelemetry Operator is already installed in your cluster.

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm install opentelemetry-operator open-telemetry/opentelemetry-operator \
--namespace opentelemetry-operator-system --create-namespace
3. Deploy the collector

The openTelemetry-manifest_statefulset.yaml file defines an OpenTelemetryCollector custom resource that the operator manages.

curl -O https://raw.githubusercontent.com/dynatrace-oss/dynatrace-security-events-collector/refs/heads/master/manifests/openTelemetry-manifest_statefulset.yaml
kubectl apply -f openTelemetry-manifest_statefulset.yaml

Use the custom collector image from ghcr.io/dynatrace-oss/dynatrace-security-events-collector:latest. The default upstream OpenTelemetry Collector image doesn't include the required Security Event Processor and Exporter plugins.

4. Verify the deployment
  1. Check pod status.

    kubectl -n dynatrace get pods

    You should see a pod named security-events-collector-* with the status Running.

  2. Check the logs.

    kubectl -n dynatrace logs -l app.kubernetes.io/name=security-events-collector --tail=50

    Look for log lines that mention OpenReports being processed and events being exported.

  3. Verify events in Dynatrace.

    After the first findings are sent, run the following query in Notebooks Notebooks:

    fetch security.events
    | filter dt.system.bucket == "default_securityevents"
    | filter event.kind == "SECURITY_EVENT"
    AND event.provider == "Kyverno"
    AND event.type == "COMPLIANCE_FINDING"
5. Uninstall the integration

Removing the collector stops new security events from being ingested. It doesn't delete events that have already been stored in Grail. These events are retained according to your environment's data retention policy.

  1. Delete the OpenTelemetryCollector resource.

    kubectl -n dynatrace delete opentelemetrycollector otel
  2. Optional Uninstall the OpenTelemetry Operator if it was installed only for this integration.

    helm uninstall opentelemetry-operator --namespace opentelemetry-operator-system
  3. Delete the shared resources.

    kubectl delete clusterrolebinding otelcontribcol
    kubectl delete clusterrole otelcontribcol
    kubectl -n dynatrace delete serviceaccount security-events-collector
    kubectl -n dynatrace delete secret dynatrace-secrets

Use this option if you can't install the OpenTelemetry Operator or if you prefer plain Kubernetes manifests.

1. Prepare the environment
  1. Create the namespace.

    kubectl create namespace dynatrace
  2. Create a Kubernetes Secret with your Dynatrace endpoint, API token, and cluster name.

    kubectl -n dynatrace create secret generic dynatrace-secrets \
    --from-literal=endpoint=https://<your-environment>.dynatrace.com \
    --from-literal=api-token=dt0c01.XXXXXXXX \
    --from-literal=cluster-name=<your-cluster-name>

    Replace:

    • <your-environment> with your Dynatrace environment.
    • dt0c01.XXXXXXXX with the token generated for this integration.
    • <your-cluster-name> with a name that identifies this cluster in Dynatrace (for example, prod-eu-1). The cluster name is optional but recommended for multi-cluster setups.
  3. Apply RBAC permissions.

    curl -O https://raw.githubusercontent.com/dynatrace-oss/dynatrace-security-events-collector/refs/heads/master/manifests/rbac.yaml
    kubectl apply -f rbac.yaml
2. Apply the collector ConfigMap

The configmap.yaml file stores the collector pipeline configuration and mounts it into the collector pod at startup.

curl -O https://raw.githubusercontent.com/dynatrace-oss/dynatrace-security-events-collector/refs/heads/master/manifests/configmap.yaml
kubectl apply -f configmap.yaml
3. Deploy the collector

The collector-deployment.yaml file defines the Deployment and a ClusterIP Service that exposes the collector within the cluster.

curl -O https://raw.githubusercontent.com/dynatrace-oss/dynatrace-security-events-collector/refs/heads/master/manifests/collector-deployment.yaml
kubectl apply -f collector-deployment.yaml

Use the custom collector image from ghcr.io/dynatrace-oss/dynatrace-security-events-collector:latest. The default upstream OpenTelemetry Collector image doesn't include the required Security Event Processor and Exporter plugins.

4. Verify the deployment
  1. Check pod status.

    kubectl -n dynatrace get pods

    You should see a pod named dt-security-events-collector-* with the status Running.

  2. Check the logs.

    kubectl -n dynatrace logs -l app=dt-security-events-collector --tail=50

    Look for log lines that mention OpenReports being processed and events being exported.

  3. Check the metrics endpoint.

    kubectl -n dynatrace port-forward deployment/dt-security-events-collector 8888:8888
    curl -s http://localhost:8888/metrics | grep processor_securityevent

    You should see processor_securityevent_incoming_logs_total and processor_securityevent_outgoing_logs_total counters.

  4. Verify events in Dynatrace.

    After the first findings are sent, run the following query in Notebooks Notebooks:

    fetch security.events
    | filter dt.system.bucket == "default_securityevents"
    | filter event.kind == "SECURITY_EVENT"
    AND event.provider == "Kyverno"
    AND event.type == "COMPLIANCE_FINDING"
5. Uninstall the integration

Removing the collector stops new security events from being ingested. It doesn't delete events that have already been stored in Grail. These events are retained according to your environment's data retention policy.

  1. Delete the Service and Deployment.

    kubectl -n dynatrace delete service otel-collector
    kubectl -n dynatrace delete deployment dt-security-events-collector
  2. Delete the ConfigMap.

    kubectl -n dynatrace delete configmap dt-security-collector-config
  3. Delete the shared resources.

    kubectl delete clusterrolebinding otelcontribcol
    kubectl delete clusterrole otelcontribcol
    kubectl -n dynatrace delete serviceaccount security-events-collector
    kubectl -n dynatrace delete secret dynatrace-secrets
  4. Optional Delete the namespace if it was created only for this integration and contains no other resources.

    kubectl delete namespace dynatrace

    Deleting the namespace removes all resources inside it, including other workloads or secrets. Run kubectl -n dynatrace get all before deleting the namespace to confirm it doesn't contain resources that you need.

Details

How it works

Kyverno integration architecture showing Kyverno producing OpenReports, the Dynatrace Security Events Collector transforming reports into security events, and OpenPipeline storing them in Grail
Kyverno integration architecture showing Kyverno producing OpenReports, the Dynatrace Security Events Collector transforming reports into security events, and OpenPipeline storing them in Grail

Kyverno compliance findings flow from OpenReports through the Dynatrace Security Events Collector to the OpenPipeline security events endpoint, where Dynatrace stores them in Grail as security events.

  1. Kyverno validates Kubernetes resources against deployed validating policies. For each policy evaluation, Kyverno produces OpenReports containing per-resource results with severity and compliance details.
  2. The k8sobjects receiver in the OpenTelemetry Collector reads reports and clusterreports resources from the openreports.io API group on a configured interval.
  3. The custom securityevent processor parses each OpenReport, extracts individual policy results, and maps them to the Dynatrace security event semantic data model. The mapping adds severity, risk score, compliance status, finding title, product vendor, and Kubernetes context.
  4. The k8sattributes processor adds Kubernetes metadata, such as cluster UID and namespace. The resource processor adds the cluster name.
  5. The custom securityevent exporter sends batched HTTP POST requests to /platform/ingest/v1/security.events, authenticated with a Dynatrace API token.
  6. Dynatrace stores the security events in Grail in the default_securityevents bucket. The events become queryable in Notebooks Notebooks, Investigations Investigations, and Dashboards Dashboards, and you can use them in workflows.

Advanced configuration

The Dynatrace Security Events Collector is a custom OpenTelemetry Collector distribution that chains three components in a logs pipeline to transform Kubernetes policy compliance data into Dynatrace security events.

k8sobjects receiver securityevent processor securityevent exporter
(pulls OpenReports) -> (transforms to events) -> (posts to Dynatrace)

The pipeline type is logs because the k8sobjects receiver emits Kubernetes objects as log records. The processor and exporter operate on these log records.

Configure OpenReports collection

The k8sobjects receiver watches or pulls Kubernetes custom resources and emits them as log records. This integration uses it to pull OpenReports from Kyverno and other policy engines that implement the OpenReports specification.

OpenReports receiver configuration
receivers:
k8sobjects:
auth_type: serviceAccount
objects:
- name: clusterreports
group: openreports.io
mode: pull
interval: 10m
- name: reports
group: openreports.io
mode: pull
interval: 10m
ParameterDescription

auth_type

Authentication method. Use serviceAccount when running inside Kubernetes.

objects[].name

Kubernetes resource name to collect. Use clusterreports for cluster-scoped reports and reports for namespaced reports.

objects[].group

API group. OpenReports use openreports.io.

objects[].mode

Collection mode. pull fetches all objects on a schedule. watch streams changes in real time.

objects[].interval

How often to pull reports. The default is 10m. Lower values increase API server load.

Filter by status

By default, the processor handles all Kyverno result statuses: pass, fail, error, and skip. To reduce event volume and ingest cost, you can process only specific statuses.

Status filter configuration
processors:
securityevent:
processors:
openreports:
enabled: true
status_filter:
- "fail"
- "error"

Licensing and cost

For billing information, see Events powered by Grail.

FAQ

What type of data is ingested from Kyverno?

The collector ingests compliance finding events: one security event per policy result and Kubernetes resource evaluated by Kyverno. Each event represents a single pass, fail, error, or skip verdict from a Kyverno policy rule applied to a specific resource.

Dynatrace categorizes these events as COMPLIANCE_FINDING with category COMPLIANCE in the security event schema.

How are Kyverno compliance findings imported into Dynatrace?

The integration is push-based from the Kubernetes cluster to Dynatrace. The custom collector periodically reads OpenReports from the Kubernetes API and pushes transformed security events to the Dynatrace security events ingest endpoint.

The k8sobjects receiver queries reports and clusterreports resources at a fixed interval. Each pull fetches existing reports. The processor uses SHA256-based deduplication to prevent previously sent results from creating duplicate events in Dynatrace.

Kyverno's background scanner runs on its own schedule, which is 1 hour by default. The collector interval and Kyverno scan schedule are independent.

How many events does one OpenReport generate?

One OpenReport typically contains many individual policy results, with one result per evaluated resource. For example, if a policy targets 50 Deployments, the report contains 50 results, and the processor expands this into 50 individual security events.

You can monitor this expansion with the processor metrics:

  • processor_securityevent_incoming_logs_total: reports received
  • processor_securityevent_outgoing_logs_total: events emitted
Which Kyverno policy types produce reports?

Only validating policy types produce OpenReports that can be ingested. Mutating, generating, deleting, and cleanup policies don't produce reports and don't appear in Dynatrace through this integration.

Policy typeScopeReport type

ClusterPolicy

Cluster-wide

clusterreports

Policy

Namespaced

reports

ClusterValidatingPolicy

Cluster-wide

clusterreports

ValidatingPolicy

Namespaced

reports

ImageValidatingPolicy

Cluster-wide

clusterreports

ClusterImageValidatingPolicy

Cluster-wide

clusterreports

NamespacedImageValidatingPolicy

Namespaced

reports

How are severity and risk score mapped?

Dynatrace maps risk levels and scores from the original Kyverno policy severity. If no severity is specified in the policy, Dynatrace uses MEDIUM.

dt.security.risk.level (mapped from finding.severity)dt.security.risk.score

critical → CRITICAL

10.0

high → HIGH

8.9

medium → MEDIUM

6.9

low → LOW

3.9

missing or unknown → NONE

0.0

Can I use the default upstream OpenTelemetry Collector image?

No. This integration requires the Security Event Processor and Security Event Exporter plugins. These plugins aren't part of the upstream OpenTelemetry Collector or Collector Contrib distributions. Use the pre-built image from ghcr.io/dynatrace-oss/dynatrace-security-events-collector:latest or build a custom collector that includes these plugins.

What happens if the collector restarts?

The collector reads all existing OpenReports on the next interval. The processor's SHA256-based deduplication prevents duplicate events from being sent to Dynatrace. No data is lost.

Can I use this integration with multiple clusters?

Yes. Each cluster's collector should set a unique k8s.cluster.name value through the CLUSTERNAME environment variable or the cluster-name key in the Kubernetes Secret.

You can then filter events per cluster in DQL:

fetch security.events
| filter event.type == "COMPLIANCE_FINDING"
| filter k8s.cluster.name == "production-eu-west-1"

Related topics

  • OpenPipeline
  • Dynatrace Query Language
  • Security events
Related tags
Threat Observability