
Kyverno Overview: The "Kubernetes Native" Policy Engine
If you are preparing for the KCA (Kyverno Certified Associate) exam or just getting started with policy-as-code, you need to grasp the core concepts before diving into the syntax. Here is the high-level overview.
1. What is Kyverno? (Keyword: Kubernetes Native)
Kyverno is the engine (Controller), and ClusterPolicy/Policy are the configuration formats (CRDs) that it uses.
Unlike OPA Gatekeeper (which requires learning the complex Rego language), Kyverno is Kubernetes Native.
- The Key Differentiator: Policies are written in standard YAML. If you can read Kubernetes manifests (Deployment, Service, etc.), you can understand Kyverno policies immediately.
- Role: It acts as a "Gatekeeper" (Admission Controller) sitting right at the API Server door.
2. Core Logic (How it works)
When you execute kubectl apply -f pod.yaml, the flow is as follows:
- The request hits the API Server.
- Kyverno (acting as a Webhook) intercepts the request.
- It checks if the request matches any defined Policy.
- If matched, it executes one of the 4 main actions (this is the backbone of the KCA exam):
3. Key Capabilities (The 4 Pillars)
Validation (Most Common)
- Goal: "Allowed" or "Denied".
- Example: A Pod must have the label team: dev. If missing -> Block (Enforce) or Warn (Audit).
Mutation (Modify)
- Goal: Modify the YAML content before it is persisted to the database (etcd).
- Example: A user creates a Pod but forgets the imagePullPolicy. Kyverno automatically injects imagePullPolicy: Always.
Generation (Create New) - The "Killer Feature"
- Goal: Automatically generate additional resources based on a trigger.
- Example: When a new Namespace is created -> Kyverno automatically generates a default NetworkPolicy and ResourceQuota inside that Namespace.
Verify Images (Supply Chain Security)
- Goal: Check container image signatures.
- Example: Only allow images signed by the company's private key (integrates with Cosign/Sigstore)
4. Anatomy of a Policy
A basic Policy file follows this nested structure:
- Policy / ClusterPolicy:
Policy: Scoped to a specific Namespace.
ClusterPolicy: Applied to the entire cluster (Global).
- Rules: A Policy can contain multiple Rules
- Match / Exclude: Selects the resources to target (e.g., match all Pods, exclude Pods in kube-system).
- Action: What to do (validate, mutate, generate, verifyImages).
Quick YAML Structure Example:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
spec:
validationFailureAction: Enforce # Enforce (Block) or Audit (Log report)
rules:
- name: check-team-label
match:
resources:
kinds:
- Pod # Apply to Pods
validate: # Validation Action
message: "The 'team' label is required!"
pattern:
metadata:
labels:
team: "?*" # Check for existence
5. Exam Tips (Must Know)
Models:
- Audit: Violations are allowed but logged in a PolicyReport. This is the safe default for Production to avoid breaking existing workflows.
- Enforce: Violations are blocked immediately (Deny).
JMESPath: Kyverno uses JMESPath for logic processing (handling if/else, contains, sum, etc.). Expect questions on filtering JSON data using JMESPath.
Auto-gen Rules: If you write a rule for Pod, Kyverno is smart enough to automatically generate rules for Deployment, StatefulSet, DaemonSet, etc. (unless you explicitly disable this feature).
Summary: Kyverno is an Admission Controller that uses YAML to Validate, Mutate, Generate resources, and Verify Images.
Sections need to be understood
Kyverno Match/Exclude
Purpose: Identifying and filtering resources for rule evaluation.
Basic Structure:
match:
any: # OR logic - satisfy at least one condition
- resources:
kinds: [...] # REQUIRED
names: [...] # Optional
namespaces: [...] # Optional
operations: [...] # Optional (default: CREATE, UPDATE)
selector: {...} # Optional (label selector)
match:
all: # AND logic - must satisfy ALL conditions
- resources:
kinds: [...]
Example Match Pod or Deployment with specific label: Applies to Deployment OR StatefulSet with label app=critical
match:
any:
- resources:
kinds:
- Deployment
- StatefulSet
operations:
- CREATE
- UPDATE
selector:
matchLabels:
app: critical
Exclude specific namespace: Applies to ALL Pods EXCEPT those in prod-alpha namespace
match:
any:
- resources:
kinds:
- Pod
exclude:
any:
- resources:
namespaces:
- prod-alpha
Match by namespace label: Only applies to Deployments in namespaces with label type=connector or type=compute
match:
any:
- resources:
kinds:
- Deployment
namespaceSelector:
matchExpressions:
- key: type
operator: In
values:
- connector
- compute
Exclude user/role: Applies to Pods with label app=critical EXCEPT those created by cluster-admin or user John
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
app: critical
exclude:
any:
- clusterRoles:
- cluster-admin
- subjects:
- kind: User
name: John
Important Logic:
Within resources block:
- AND across types (kinds AND namespaces)
- OR within lists (Pod OR Deployment)
Between match and exclude: AND logic (must satisfy match AND not satisfy exclude)
Tips:
- operations is optional, defaults to [CREATE, UPDATE]
- Wildcards * supported in kinds, names, namespaces
- Use any: for OR logic, all: for AND logic
- exclude must be a subset of match
request.object Variable
Documentation: https://kyverno.io/docs/policy-types/cluster-policy/variables/
What is request.object?
request.object is the incoming resource configuration (the new state) that is being submitted to Kubernetes API server.
Practical Example 1 - Access resource name: If you create namespace
prod -> {{request.object.metadata.name}} = "prod"
validate:
message: "Creating namespace: {{request.object.metadata.name}}"
pattern:
metadata:
labels:
team: "?*"
Practical Example 2 - Generate Secret in new namespace: When creating namespace staging -> Secret is generated in staging namespace
generate:
kind: Secret
name: regcred
namespace: "{{request.object.metadata.name}}" # Uses incoming namespace name
clone:
namespace: default
name: regcred
Practical Example 3 - Validate based on labels: Only runs if the incoming resource has label app=critical
preconditions:
any:
- key: "{{request.object.metadata.labels.app}}"
operator: Equals
value: "critical"
Quick Comparison:
# User submits this Pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.20
{{request.object.metadata.name}}->"nginx"{{request.object.metadata.labels.app}}->"web"{{request.object.spec.containers[0].image}}->"nginx:1.20"
What is JMESPath used for in Kyverno?
In Kyverno, JMESPath (pronounced "James Path") is a JSON query language used to select, filter, and transform data from Kubernetes resources.
Think of it as the logic layer that allows your policies to "read" and "process" YAML manifests.
It is primarily used for:
- Variable Substitution: Extracting values from a request to use elsewhere (e.g., getting a namespace name:
{{ request.object.metadata.namespace }}). - Condition Logic: Writing complex
preconditionsto decide if a rule should run (e.g., "only run this if the image tag is NOT 'latest'"). - Filtering: Selecting specific items from arrays (e.g., "find all containers that have port 80 open").
- Data Transformation: Modifying data formats before validation or mutation.
Example:
To check if a label exists, you might use a JMESPath expression like:
contains(request.object.metadata.labels, 'production')
Let's go for scenario:
- We want to enforce a rule that says: "If a Pod has the label env set to production, it must also include a contact-email annotation."
- We use the JMESPath contains function in the preconditions block to target only specific resources.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-annotation-for-production
spec:
validationFailureAction: Enforce
background: false
rules:
- name: check-production-annotation
match:
any:
- resources:
kinds:
- Pod
# 1. PRECONDITIONS: The JMESPath Logic
# This block ensures the rule ONLY runs if the label 'env' contains 'production'
preconditions:
all:
- key: true
operator: Equals
# Note: In JMESPath, `contains` returns true/false.
# We check if the 'env' label value contains the string 'production'.
# || '' is the fucking fallback, because contains(null, 'production') will error
# But contains('', 'production') will not!!!
value: "{{ contains(request.object.metadata.labels.env || '', 'production') }}"
# 2. VALIDATION: The actual check to enforce
validate:
message: "Production pods must have the 'contact-email' annotation."
pattern:
metadata:
annotations:
contact-email: "?*"
This will be blocked duo to missing contact-email annotation.
apiVersion: v1
kind: Pod
metadata:
name: bad-prod-pod
labels:
env: production
spec:
containers:
- name: nginx
image: nginx
This will be allowed because env is not production haha
apiVersion: v1
kind: Pod
metadata:
name: dev-pod
labels:
env: development
spec:
containers:
- name: nginx
image: nginx
What is PolicyReport?
The PolicyReport is essentially a "Health Check Report Card" for your Kubernetes resources.
While ClusterPolicy defines the rules, the PolicyReport stores the results of those rules.
Purpose of PolicyReport: Its main goal is Observability & Auditing.
Without PolicyReports, if a policy is set to Audit mode (non-blocking), you would have no easy way to know which resources are violating rules—you would have to dig through Kyverno controller logs.
- Audit Results: It lists resources that exist in your cluster but violate a policy.
- Standardization: It is not unique to Kyverno! It is a standard CRD defined by the Kubernetes Policy Working Group (WG-Policy). This means other tools (like Falco, Trivy, or UI Dashboards) can also read/write these reports.
- Scoping: It keeps results close to the application. Developers can check
kubectl get policyreport -n my-appto see if their app is compliant, without needing cluster-admin access.
Example output of command get policyreport:
NAME PASS FAIL WARN ERROR SKIP AGE
polr-ns-default 0 1 0 0 0 5m
The PolicyReport YAML output of command: kubectl get policyreport -n my-app polr-ns-default -oyaml
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:
name: polr-ns-default
namespace: default # It lives next to the Pod, not at the cluster level
labels:
app.kubernetes.io/managed-by: kyverno
summary:
pass: 0
fail: 1
warn: 0
error: 0
skip: 0
results:
- policy: require-labels # The name of the ClusterPolicy responsible
rule: check-for-team-label # The specific rule name
category: Best Practices
severity: medium
result: fail # The outcome (fail, pass, warn, error, skip)
message: "Validation error: label 'team' is required"
source: kyverno
resources: # The specific object that failed
- apiVersion: v1
kind: Pod
name: nginx
namespace: default
uid: a1b2c3d4-e5f6...
Validate Rules
This field sits directly under the spec section of your Policy.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
# ---------------------------------------------------------
# THIS IS THE FIELD
# Options:
# - Enforce (BLOCK the request)
# - Audit (ALLOW the request, but report it as a fail)
# ---------------------------------------------------------
validationFailureAction: Enforce
background: true
rules:
- name: require-image-tag
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Using the ':latest' tag is not allowed."
pattern:
spec:
containers:
- image: "!*:latest"
Important Note on Scoping: You can also set this field per rule if you want mixed behaviors in a single policy (available in newer Kyverno versions):
spec:
validationFailureAction: Audit # Default for the whole policy
rules:
- name: critical-rule
validationFailureAction: Enforce # This specific rule will BLOCK
...
- name: minor-rule
# Inherits Audit (ALLOW)
...
Example: What happens in "Audit" mode
You apply a "bad" Pod.
$ kubectl apply -f bad-pod.yaml
pod/bad-pod created <-- Look! It says created.
Kubernetes API Audit Log (file):
{
"verb": "create",
"user": "kubernetes-admin",
"objectRef": { "resource": "pods", "name": "bad-pod" },
"responseStatus": { "code": 201 } <-- Success! No mention of error.
}
Kyverno Policy Report (Where the truth is):
$ kubectl get policyreports
NAME FAIL
polr-ns-default 1 <-- Here is your violation log.
So, simple summary:
Enforce: Stops the bad thing from entering.Audit: Lets the bad thing in, but writes a ticket (PolicyReport) so you can fix it later. It does not rely on the standard API Server audit log for reporting violations.
Background Scans
Document: https://kyverno.io/docs/policy-reports/background/
It's purpose: Periodically checking existing resources in the cluster against policies to detect violations. It will just create a ticket (PolicyReport).
By default, Kyverno never deletes an existing resource during a Background Scan, even if your policy is set to Enforce. This is a safety mechanism to prevent accidentally killing production workloads.
What if I want to delete pods that violated policies? You will need Cleanup policy: https://kyverno.io/docs/policy-types/cleanup-policy/
Example: "Delete any Pod older than 7 days"
apiVersion: kyverno.io/v2beta1
kind: ClusterCleanupPolicy
metadata:
name: cleanup-old-pods
spec:
match:
any:
- resources:
kinds:
- Pod
conditions:
all:
- key: "{{ time_since('', request.object.metadata.creationTimestamp, '') }}"
operator: GreaterThan
value: "168h" # 7 days
schedule: "*/5 * * * *" # Run check every 5 mins
Example: ClusterCleanupPolicy that will automatically DELETE any Pod that is missing the team label.
apiVersion: kyverno.io/v2beta1
kind: ClusterCleanupPolicy
metadata:
name: delete-pods-missing-team-label
spec:
# 1. SCHEDULE
# Run this check every 5 minutes
schedule: "*/5 * * * *"
# 2. TARGET RESOURCES
match:
any:
- resources:
kinds:
- Pod
namespaces:
- default # SAFEGUARD: Only delete pods in 'default' for now
# 3. DELETE LOGIC (JMESPath)
# "If this condition is TRUE, then DELETE the resource."
conditions:
all:
- key: "{{ request.object.metadata.labels.team || '' }}"
operator: Equals
value: ""
# Logic: If the 'team' label is missing (empty), it matches this rule -> DELETE.
When not to use Background Scan or background: false. When policy use following variables
request.userInfo.*request.operationrequest.dryRunserviceAccountNamein admission context
Because background scan doesn't has admission request context, only resource data!
What is failurePolicy?
This setting defines how Kubernetes should react if the Kyverno controller is DOWN (crashed, timed out, or unreachable).
It determines whether the cluster should "Fail Closed" (Block everything) or "Fail Open" (Let everything pass) during a system outage.
Fail (Default):
- Behavior: If Kyverno doesn't respond -> BLOCK the API request.
- Pro: Maximum Security (nothing bypasses policy).
- Con: If Kyverno crashes, your cluster might stop accepting changes (no new Pods).
Ignore:
- Behavior: If Kyverno doesn't respond -> ALLOW the API request.
- Pro: High Availability (cluster keeps working even if Kyverno dies).
- Con: Security Risk (policies are temporarily skipped).
Example:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: critical-policy-with-fail-open
spec:
# -------------------------------------------------------------
# ACTION: If Kyverno is dead, just let the request go through.
# -------------------------------------------------------------
failurePolicy: Ignore
# This is the logic rule (Enforce/Audit)
validationFailureAction: Enforce
rules:
- ...
Relationship between Kyverno and Admission Controller
Think of Kyverno as a specialized, programmable Admission Controller.
- Admission Controller: The "Police Station" built into Kubernetes.
- Kyverno: A specific "Officer" you hire working at that station, who follows the instructions (Policies) you write in YAML.
You use Kyverno TO implement Admission Control. Instead of writing a custom Go webhook server to validate your labels, you just apply a Kyverno YAML.
Kyverno CLI
- Checks if resources pass or fail a policy.
kyverno apply policy.yaml --resource pod.yaml
- Runs structured tests defined in a
kyverno-test.yamlfile.
kyverno test ./tests
- Tests a specific JMESPath expression against JSON input.
kyverno jp query -i object.json 'metadata.labels'
- Checks if your policy YAML is written correctly.
kyverno validate policy.yaml
- Shows the installed CLI version.
kyverno version
What is External Data Source?
Purpose: It allows you to load data from outside into a variable before the rule logic (validate/mutate) runs.
Document: https://main.kyverno.io/docs/policy-types/cluster-policy/external-data-sources/
In order to consume data from a ConfigMap in a rule, a context is required... The context data can then be referenced in the policy rule using JMESPath notation.
Kyverno supports 3 main data sources in context:
- Kubernetes Resources (via API Call): Look up existing data in the cluster (e.g., ConfigMaps, Secrets, Services).
- External APIs: Make an HTTP call to a service outside the cluster.
- Image Registry: Fetch metadata about a container image (e.g., image size, architecture).
Scenario: You want to enforce a rule that "Pods can only use roles defined in a shared ConfigMap".
apiVersion: v1
kind: ConfigMap
metadata:
name: allowed-roles-cm
namespace: default
data:
roles: '["frontend", "backend", "cache"]'
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: validate-role-from-configmap
spec:
validationFailureAction: Enforce
rules:
- name: check-role
match:
any:
- resources:
kinds:
- Pod
# ---------------------------------------------------------
# CONTEXT: Load data from ConfigMap into variable "allowed_list"
# ---------------------------------------------------------
context:
- name: allowed_list
apiCall:
# We call the K8s API to fetch the ConfigMap
urlPath: "/api/v1/namespaces/default/configmaps/allowed-roles-cm"
jmesPath: "data.roles" # Extract just the roles list
validate:
message: "Invalid Role! It must be in the allowed list."
# Use the variable {{ allowed_list }} we just loaded
deny:
conditions:
all:
- key: "{{ request.object.metadata.labels.role }}"
operator: AnyNotIn
value: "{{ allowed_list }}"
- We apply manifest:
apiVersion: v1
kind: Pod
metadata:
name: database-pod
labels:
# This is the label being checked by the policy
role: database
spec:
containers:
- name: db-container
image: postgres:15
ports:
- containerPort: 5432
- Kyverno Logic: Checks
metadata.labels.role("database"). - Comparison: "database" is NOT in the allowed list
["frontend", "backend", "cache"] - Result:
Error from server: error when creating "pod-request.yaml": admission webhook "validate.kyverno.svc-fail" denied the request:
validate-role-from-configmap:
check-role: Role is not allowed!
Extra example for API Call in external data source
context:
- name: podCount
apiCall: # <--- Generic field for ANY Kubernetes resource
urlPath: "/api/v1/namespaces/{{request.namespace}}/pods"
jmesPath: "items | length(@)"
Default Port in Kyverno
Here is the list of default ports used by Kyverno:
Webhook Server Port 9443 (HTTPS/TCP):
- Purpose: This is the secure port where the Kubernetes API Server sends Admission Requests to Kyverno.
- Usage: It handles policy validation and mutation logic.
Metrics Port 8000 (HTTP/TCP):
- Purpose: Exposes Prometheus metrics.
- Usage: Scraped by Prometheus at
/metricsto monitor policy execution counts, latency, and rule results.
Probes / Health Check Port 8080 (HTTP/TCP):
- Purpose: Used for Liveness and Readiness probes.
- Usage: The Kubelet checks
/health/livenessand/health/readinesson this port to ensure the Pod is healthy.
Pprof (Profiler) Port 6060 (HTTP/TCP): (Optional):
- Purpose: Used for debugging and profiling Go performance.
- Usage: Disabled by default. Only open if you specifically enable profiling flags for debugging.
Summary: The most critical port is 9443, as that is the main communication line between Kubernetes and Kyverno.
Kyverno Mutate - JSON Patch
patchesJson6902 is a mutation method used when you need surgical precision to modify a resource.
Unlike the standard patchStrategicMerge (which merges YAMLs together), this uses specific operations (RFC 6902 standard) to tell Kyverno exactly how to change the data.
Example:
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
resources:
limits:
memory: "256Mi"
When to use it?
Use it when patchStrategicMerge cannot do the job, specifically for:
- Removing a field (impossible with standard merge).
- Adding an item to a specific position in a list (arrays).
- Replacing a value entirely without merging.
It follows the JSON Patch format:
- op: The action (
add,remove,replace). - path: The location of the field (e.g.,
/metadata/labels/mytag). - value: The data to put there.
Example: "Add a sidecar container"
This adds a new container to the start of the containers list (index 0).
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-sidecar
spec:
rules:
- name: inject-sidecar
match:
any:
- resources:
kinds:
- Pod
mutate:
# Note the |- block scalar, this expects a JSON/YAML list string
patchesJson6902: |-
- op: add
path: /spec/containers/0
value:
name: my-sidecar
image: alpine:latest
- Official Docs: Kyverno Mutate - JSON Patch
Generate Rules Example
generate:
synchronize: true # Auto-update when source changed
kind: NetworkPolicy
name: default-deny
namespace: "{{request.object.metadata.name}}"
data:
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Explain for flows:
- User create Namespace "kienlt-vip-pro"
- Kyverno trigger match and generate NetworkPolicy
- name: default-deny
- namespace: dev-team-a ← get from request.object.metadata.name
- Block all Ingress/Egress
- Result: New namespace will have NetworkPolicy
deny-all. Actual resource created
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: kienlt-vip-pro # from request
spec:
podSelector: {} # all pods
policyTypes:
- Ingress # block incoming
- Egress # block outgoing
Explain for synchronize:
synchronize: true: Kyverno complete managed lifecyclesynchronize: false: Kyverno created for the first time then user can edit it manually without getting revert back likesynchronize: true
This is example for pattern zero-trust by default!
VerifyImages Example
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
Foreach - Advanced Mutation
mutate:
foreach:
- list: "request.object.spec.containers"
patchStrategicMerge:
spec:
containers:
- name: "{{ element.name }}"
securityContext:
readOnlyRootFilesystem: true
More for JMESPath which will appear a lot in exam xD
Filtering (Critical)
The exam often asks: "Block the Pod if ANY container uses an image with the 'latest' tag" or "Find containers that do not have memory limits set".
You must use the [?expression] syntax.
Syntax: request.object.spec.containers[? filter_condition ]
Example: Find all containers where the image starts with "nginx":
# Returns a list of container objects that match the condition
{{ request.object.spec.containers[?starts_with(image, 'nginx')] }}
Kyverno Application (Deny if found):
validate:
message: "Nginx images are not allowed!"
deny:
conditions:
all:
# Filter the list. Use length() to count.
# If count > 0, it means a violation exists -> Block.
- key: "{{ request.object.spec.containers[?starts_with(image, 'nginx')] | length(@) }}"
operator: GreaterThan
value: 0
Pipe Operator | (Projection)
Used to extract a specific field from a complex object into a flat list.
Example: You don't want the whole container object; you just want a list of image names.
# Input: List of container objects
# Output: ["nginx:latest", "redis:alpine", "busybox"]
key: "{{ request.object.spec.containers[].image }}"
Exam Combo (Filter + Pipe): "Get the containerPort of the container named 'app'" request.object.spec.containers[?name == 'app'].ports[].containerPort
Null Handling (The Exam Trap)
This is where many people fail. If a field does not exist in the YAML, JMESPath returns null, causing the Policy to crash or behave unexpectedly.
Use the || (OR) operator to set a default value.
Example: Check the team label. What if the Pod has no labels at all?
- Risk:
{{ request.object.metadata.labels.team }}(If null -> Error). - Safe:
{{ request.object.metadata.labels.team || '' }}(If null -> treat as empty string).
Kyverno Application:
preconditions:
all:
- key: "{{ request.object.metadata.labels.team || 'no-team' }}"
operator: Equals
value: "production"
The length() function
Used to limit the quantity of a resource.
Example: "A Pod must not have more than 3 containers."
validate:
deny:
conditions:
all:
- key: "{{ request.object.spec.containers | length(@) }}"
operator: GreaterThan
value: 3
Sample JMESPath questions
Scenario: You are writing a policy against the following Pod JSON. Analyze this JSON and answer the 3 questions below.
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "backend-app",
"labels": {
"app": "backend",
"env": "production"
}
},
"spec": {
"containers": [
{
"name": "main-app",
"image": "my-org/app:v1",
"ports": [
{ "containerPort": 8080, "protocol": "TCP" }
]
},
{
"name": "sidecar-logger",
"image": "fluentd:latest",
"ports": []
}
],
"volumes": [
{ "name": "config", "configMap": { "name": "app-conf" } },
{ "name": "data", "emptyDir": {} }
]
}
}
Question Basic Selection: You need to extract the name of the first container in the list
- Answer:
spec.containers[0].name
- Explanation: In JMESPath, you access array items by index using brackets [0].
Question Filtering & Counting: You want to check how many containers are using the latest tag. Which JMESPath expression returns the count (integer)?
- Answer:
spec.containers[?contains(image, 'latest')] | length(@)
- Explanation:
- spec.containers[...]: Access the list
- [?contains(image, 'latest')]: Filter the list to keep only objects where the image string contains "latest".
- | length(@): Pipe the result to the length function to count them.
Complex Projection: You want to find the names of all volumes that are of type emptyDir. Which expression returns ["data"]?
- Answer:
spec.volumes[?emptyDir != null].name
- Explanation:
1. We need to check if the field emptyDir exists.
2. In the first volume ("config"), emptyDir is missing (null).
3. In the second volume ("data"), emptyDir exists (it is an object {}).
4. [?emptyDir != null] filters the list to keep only volumes that have this field.
5. .name extracts the name from the remaining items.
6. Some JMESPath implementations allow [?emptyDir] as a shorthand for "not null", but != null is the strict standard used in many exams.
Kyverno JP CLI examples xD
# object.json
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "web-app",
"namespace": "production",
"labels": {
"app": "nginx",
"env": "production",
"team": "platform"
},
"annotations": {
"contact-email": "team@example.com"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:1.25",
"ports": [
{ "containerPort": 80, "protocol": "TCP" },
{ "containerPort": 443, "protocol": "TCP" }
],
"resources": {
"limits": { "memory": "256Mi", "cpu": "500m" }
}
},
{
"name": "sidecar",
"image": "fluentd:latest",
"ports": []
}
],
"volumes": [
{ "name": "config", "configMap": { "name": "app-config" } },
{ "name": "data", "emptyDir": {} },
{ "name": "secret", "secret": { "secretName": "app-secret" } }
]
}
}
Basic Selection:
# Get Pod Name
kyverno jp query -i object.json 'metadata.name'
# Output: "web-app"
# Get namespace
kyverno jp query -i object.json 'metadata.namespace'
# Output: "production"
# Get all labels
kyverno jp query -i object.json 'metadata.labels'
# Output: {"app":"nginx","env":"production","team":"platform"}
# Get specific label
kyverno jp query -i object.json 'metadata.labels.env'
# Output: "production"
Array Access:
# Get first container
kyverno jp query -i object.json 'spec.containers[0]'
# Output: {"name":"nginx","image":"nginx:1.25",...}
# get first container name
kyverno jp query -i object.json 'spec.containers[0].name'
# Output: "nginx"
# Get all containers name
kyverno jp query -i object.json 'spec.containers[].name'
# Output: ["nginx","sidecar"]
# Get all images
kyverno jp query -i object.json 'spec.containers[].image'
# Output: ["nginx:1.25","fluentd:latest"]
Filtering with [? ]:
# Find container image contains "latest"
kyverno jp query -i object.json "spec.containers[?contains(image, 'latest')]"
# Output: [{"name":"sidecar","image":"fluentd:latest",...}]
# Find container named "nginx"
kyverno jp query -i object.json "spec.containers[?name == 'nginx']"
# Output: [{"name":"nginx","image":"nginx:1.25",...}]
# find volumes has emptyDir
kyverno jp query -i object.json "spec.volumes[?emptyDir != null]"
# Output: [{"name":"data","emptyDir":{}}]
# find volumes has configMap
kyverno jp query -i object.json "spec.volumes[?configMap != null].name"
# Output: ["config"]
Counting with length()
# count containers
kyverno jp query -i object.json 'spec.containers | length(@)'
# Output: 2
# count volumes
kyverno jp query -i object.json 'spec.volumes | length(@)'
# Output: 3
# count containers using "latest" tag
kyverno jp query -i object.json "spec.containers[?contains(image, 'latest')] | length(@)"
# Output: 1
Null Handling with ||
# Label exists
kyverno jp query -i object.json "metadata.labels.env || 'not-set'"
# Output: "production"
# Label not exists - fallback to default
kyverno jp query -i object.json "metadata.labels.tier || 'not-set'"
# Output: "not-set"
# Check annotation exists or not!
kyverno jp query -i object.json "metadata.annotations.description || 'no-description'"
# Output: "no-description"
Combined Examples (Exam-style)
# Get all containerPort of container "nginx"
kyverno jp query -i object.json "spec.containers[?name == 'nginx'].ports[].containerPort"
# Output: [80,443]
# Check if container missing resources.limits!
kyverno jp query -i object.json "spec.containers[?resources.limits == null].name"
# Output: ["sidecar"]
# Get memory limit of the first container
kyverno jp query -i object.json "spec.containers[0].resources.limits.memory || 'not-set'"
# Output: "256Mi"
# Check label "env" ccontains "prod"
kyverno jp query -i object.json "contains(metadata.labels.env || '', 'prod')"
# Output: true
My secret weapons - Mock Exam
Here is the link Udemy Practice Test:
https://www.udemy.com/course/complete-certified-kyverno-associate-kca-exam-prep/
Exam Note
- Exam duration: 90 minutes
- Exam questions: 60 questions
- The exam required 75/100 to pass, and I scored 80/100!
Some extra section that appeared in exam!
- Need to remember correct flow:
Authn/Authz --> Mutating --> Schema Validation -> Validate --> ETCD
- Auto Gen feature, remember annotation xD:
https://release-1-8-0.kyverno.io/docs/writing-policies/autogen/
- TTL Default of Certification in Kyverno:
https://pkg.go.dev/github.com/kyverno/kyverno/pkg/tls
const (
CAValidityDuration = 365 * 24 * time.Hour // 365 days
TLSValidityDuration = 150 * 24 * time.Hour // 150 days
CertRenewalInterval = 12 * time.Hour // 12 hours
)
- Concurrent Policies Generation Number Default!
- PolicyException CRD
- CEL
- CLI Commands with parameter, yes the fucking parameter you don't heard it wrong!
It is pretty hard to get perfect number even in scenario I take another test for Kyverno since some question is really advanced for me! I only scored 82/100!
Conclusion
- Understand everything in this article
- Take exam notes
- Scored >80 in Mock exams. 4 Exams in totals
Good luck!!!