Kyverno is a Kubernetes-native policy engine that allows you to define policies as Kubernetes resources. This guide covers how to integrate Kyverno policy enforcement directly into Java applications for validation, mutation, and generation capabilities.
Architecture Overview
Java Application â Kyverno Java SDK â Kubernetes API â Kyverno Controller â (Policy Validation / Resource Mutation)
Step 1: Dependencies Setup
Maven Dependencies
<!-- pom.xml --> <dependencies> <!-- Kubernetes Client --> <dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> <version>18.0.0</version> </dependency> <!-- Kyverno Java SDK --> <dependency> <groupId>io.kyverno</groupId> <artifactId>kyverno-java-client</artifactId> <version>1.9.0</version> </dependency> <!-- YAML Processing --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>2.0</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Validation --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> </dependencies>
Step 2: Configuration Classes
Kubernetes Configuration
// src/main/java/com/company/kyverno/config/KubernetesConfig.java
package com.company.kyverno.config;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.util.Config;
import io.kubernetes.client.util.credentials.AccessTokenAuthentication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
@Slf4j
public class KubernetesConfig {
@Value("${kubernetes.master.url:https://kubernetes.default.svc}")
private String masterUrl;
@Value("${kubernetes.token:#{null}}")
private String serviceAccountToken;
@Value("${kubernetes.namespace:default}")
private String namespace;
@Bean
public ApiClient apiClient() throws IOException {
ApiClient client;
if (serviceAccountToken != null) {
// Use service account token
client = new ApiClient();
client.setBasePath(masterUrl);
client.setAuthentication(new AccessTokenAuthentication(serviceAccountToken));
client.setVerifyingSsl(false); // For testing only
} else {
// Auto-configure from cluster
client = Config.defaultClient();
}
Configuration.setDefaultApiClient(client);
log.info("Kubernetes API client configured for namespace: {}", namespace);
return client;
}
@Bean
public String kubernetesNamespace() {
return namespace;
}
}
Kyverno Configuration
// src/main/java/com/company/kyverno/config/KyvernoConfig.java
package com.company.kyverno.config;
import com.company.kyverno.client.KyvernoPolicyClient;
import com.company.kyverno.engine.PolicyEngine;
import com.company.kyverno.engine.PolicyValidator;
import io.kubernetes.client.openapi.ApiClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KyvernoConfig {
@Bean
public KyvernoPolicyClient kyvernoPolicyClient(ApiClient apiClient) {
return new KyvernoPolicyClient(apiClient);
}
@Bean
public PolicyEngine policyEngine(KyvernoPolicyClient policyClient) {
return new PolicyEngine(policyClient);
}
@Bean
public PolicyValidator policyValidator(PolicyEngine policyEngine) {
return new PolicyValidator(policyEngine);
}
}
Step 3: Kyverno Client Implementation
Policy Client
// src/main/java/com/company/kyverno/client/KyvernoPolicyClient.java
package com.company.kyverno.client;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Status;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Slf4j
public class KyvernoPolicyClient {
private static final String GROUP = "kyverno.io";
private static final String VERSION = "v1";
private static final String PLURAL_CLUSTER_POLICIES = "clusterpolicies";
private static final String PLURAL_POLICIES = "policies";
private final CustomObjectsApi customObjectsApi;
private final String namespace;
public KyvernoPolicyClient(ApiClient apiClient, String namespace) {
this.customObjectsApi = new CustomObjectsApi(apiClient);
this.namespace = namespace;
}
public KyvernoPolicyClient(ApiClient apiClient) {
this(apiClient, "default");
}
/**
* Apply a ClusterPolicy to the cluster
*/
public Map<String, Object> createClusterPolicy(Map<String, Object> policy) throws ApiException {
log.info("Creating ClusterPolicy: {}", getPolicyName(policy));
return customObjectsApi.createClusterCustomObject(
GROUP, VERSION, PLURAL_CLUSTER_POLICIES, policy, null, null, null
);
}
/**
* Apply a namespaced Policy
*/
public Map<String, Object> createPolicy(String namespace, Map<String, Object> policy) throws ApiException {
log.info("Creating Policy in namespace {}: {}", namespace, getPolicyName(policy));
return customObjectsApi.createNamespacedCustomObject(
GROUP, VERSION, namespace, PLURAL_POLICIES, policy, null, null, null
);
}
/**
* Get a ClusterPolicy by name
*/
public Map<String, Object> getClusterPolicy(String policyName) throws ApiException {
return customObjectsApi.getClusterCustomObject(
GROUP, VERSION, PLURAL_CLUSTER_POLICIES, policyName
);
}
/**
* Get a namespaced Policy by name
*/
public Map<String, Object> getPolicy(String namespace, String policyName) throws ApiException {
return customObjectsApi.getNamespacedCustomObject(
GROUP, VERSION, namespace, PLURAL_POLICIES, policyName
);
}
/**
* List all ClusterPolicies
*/
public Map<String, Object> listClusterPolicies() throws ApiException {
return customObjectsApi.listClusterCustomObject(
GROUP, VERSION, PLURAL_CLUSTER_POLICIES, null, null, null, null, null, null, null, null
);
}
/**
* List Policies in a namespace
*/
public Map<String, Object> listPolicies(String namespace) throws ApiException {
return customObjectsApi.listNamespacedCustomObject(
GROUP, VERSION, namespace, PLURAL_POLICIES, null, null, null, null, null, null, null, null
);
}
/**
* Delete a ClusterPolicy
*/
public V1Status deleteClusterPolicy(String policyName) throws ApiException {
log.info("Deleting ClusterPolicy: {}", policyName);
return customObjectsApi.deleteClusterCustomObject(
GROUP, VERSION, PLURAL_CLUSTER_POLICIES, policyName, null, null, null, null, null, null
);
}
/**
* Delete a namespaced Policy
*/
public V1Status deletePolicy(String namespace, String policyName) throws ApiException {
log.info("Deleting Policy in namespace {}: {}", namespace, policyName);
return customObjectsApi.deleteNamespacedCustomObject(
GROUP, VERSION, namespace, PLURAL_POLICIES, policyName, null, null, null, null, null, null
);
}
/**
* Validate a resource against policies
*/
public PolicyValidationResult validateResource(Map<String, Object> resource) throws ApiException {
// This would typically call Kyverno's validation webhook
// For now, we'll implement client-side validation
return new PolicyValidationResult(true, "Validation completed");
}
private String getPolicyName(Map<String, Object> policy) {
try {
Map<String, Object> metadata = (Map<String, Object>) policy.get("metadata");
return (String) metadata.get("name");
} catch (Exception e) {
return "unknown";
}
}
@Data
@AllArgsConstructor
public static class PolicyValidationResult {
private boolean valid;
private String message;
private Map<String, String> violations;
public PolicyValidationResult(boolean valid, String message) {
this.valid = valid;
this.message = message;
this.violations = Map.of();
}
}
}
Step 4: Policy Engine Implementation
Policy Engine
// src/main/java/com/company/kyverno/engine/PolicyEngine.java
package com.company.kyverno.engine;
import com.company.kyverno.client.KyvernoPolicyClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.regex.Pattern;
@Component
@Slf4j
public class PolicyEngine {
private final KyvernoPolicyClient policyClient;
private final ObjectMapper yamlMapper;
private final ObjectMapper jsonMapper;
public PolicyEngine(KyvernoPolicyClient policyClient) {
this.policyClient = policyClient;
this.yamlMapper = new ObjectMapper(new YAMLFactory());
this.jsonMapper = new ObjectMapper();
}
/**
* Validate Kubernetes resource against policies
*/
public ValidationResult validateResource(Map<String, Object> resource) {
List<PolicyViolation> violations = new ArrayList<>();
try {
// Extract resource metadata
Map<String, Object> metadata = (Map<String, Object>) resource.get("metadata");
String kind = (String) resource.get("kind");
String name = (String) metadata.get("name");
String namespace = (String) metadata.get("namespace");
log.info("Validating resource: {}/{} in namespace {}", kind, name, namespace);
// Apply validation rules
violations.addAll(validateLabels(metadata));
violations.addAll(validateAnnotations(metadata));
violations.addAll(validateResourceLimits(resource));
violations.addAll(validateSecurityContext(resource));
violations.addAll(validateNetworkPolicies(resource));
return new ValidationResult(violations.isEmpty(), violations);
} catch (Exception e) {
log.error("Error validating resource", e);
violations.add(new PolicyViolation("SYSTEM_ERROR", "Validation error: " + e.getMessage()));
return new ValidationResult(false, violations);
}
}
/**
* Mutate resource based on policies
*/
public Map<String, Object> mutateResource(Map<String, Object> resource) {
Map<String, Object> mutatedResource = deepCopy(resource);
try {
// Apply mutation rules
mutateLabels(mutatedResource);
mutateAnnotations(mutatedResource);
mutateResourceLimits(mutatedResource);
mutateSecurityContext(mutatedResource);
addDefaultFields(mutatedResource);
log.info("Resource mutated successfully");
return mutatedResource;
} catch (Exception e) {
log.error("Error mutating resource", e);
return resource; // Return original resource on error
}
}
/**
* Generate additional resources based on policies
*/
public List<Map<String, Object>> generateResources(Map<String, Object> resource) {
List<Map<String, Object>> generatedResources = new ArrayList<>();
try {
// Generate NetworkPolicy if needed
Map<String, Object> networkPolicy = generateNetworkPolicy(resource);
if (networkPolicy != null) {
generatedResources.add(networkPolicy);
}
// Generate ConfigMap if needed
Map<String, Object> configMap = generateConfigMap(resource);
if (configMap != null) {
generatedResources.add(configMap);
}
log.info("Generated {} additional resources", generatedResources.size());
} catch (Exception e) {
log.error("Error generating resources", e);
}
return generatedResources;
}
/**
* Validate labels against policies
*/
private List<PolicyViolation> validateLabels(Map<String, Object> metadata) {
List<PolicyViolation> violations = new ArrayList<>();
Map<String, String> labels = (Map<String, String>) metadata.get("labels");
if (labels == null) {
violations.add(new PolicyViolation("LABELS_REQUIRED", "Resource must have labels"));
return violations;
}
// Check required labels
if (!labels.containsKey("app")) {
violations.add(new PolicyViolation("APP_LABEL_REQUIRED", "Label 'app' is required"));
}
if (!labels.containsKey("version")) {
violations.add(new PolicyViolation("VERSION_LABEL_REQUIRED", "Label 'version' is required"));
}
// Validate label format
Pattern validLabelPattern = Pattern.compile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$");
labels.forEach((key, value) -> {
if (!validLabelPattern.matcher(key).matches()) {
violations.add(new PolicyViolation("INVALID_LABEL_KEY",
String.format("Label key '%s' is invalid", key)));
}
if (!validLabelPattern.matcher(value).matches()) {
violations.add(new PolicyViolation("INVALID_LABEL_VALUE",
String.format("Label value '%s' for key '%s' is invalid", value, key)));
}
});
return violations;
}
/**
* Validate annotations
*/
private List<PolicyViolation> validateAnnotations(Map<String, Object> metadata) {
List<PolicyViolation> violations = new ArrayList<>();
Map<String, String> annotations = (Map<String, String>) metadata.get("annotations");
if (annotations != null) {
// Check for required annotations
if (!annotations.containsKey("description")) {
violations.add(new PolicyViolation("DESCRIPTION_ANNOTATION_REQUIRED",
"Annotation 'description' is required"));
}
}
return violations;
}
/**
* Validate resource limits
*/
private List<PolicyViolation> validateResourceLimits(Map<String, Object> resource) {
List<PolicyViolation> violations = new ArrayList<>();
if ("Pod".equals(resource.get("kind")) ||
"Deployment".equals(resource.get("kind")) ||
"StatefulSet".equals(resource.get("kind"))) {
Map<String, Object> spec = (Map<String, Object>) resource.get("spec");
Map<String, Object> template = (Map<String, Object>) spec.get("template");
Map<String, Object> podSpec = (Map<String, Object>) template.get("spec");
List<Map<String, Object>> containers = (List<Map<String, Object>>) podSpec.get("containers");
for (Map<String, Object> container : containers) {
violations.addAll(validateContainerResources(container));
}
}
return violations;
}
/**
* Validate container resource requirements
*/
private List<PolicyViolation> validateContainerResources(Map<String, Object> container) {
List<PolicyViolation> violations = new ArrayList<>();
String containerName = (String) container.get("name");
Map<String, Object> resources = (Map<String, Object>) container.get("resources");
if (resources == null) {
violations.add(new PolicyViolation("RESOURCES_REQUIRED",
String.format("Container '%s' must have resource limits", containerName)));
return violations;
}
Map<String, Object> limits = (Map<String, Object>) resources.get("limits");
if (limits == null) {
violations.add(new PolicyViolation("RESOURCE_LIMITS_REQUIRED",
String.format("Container '%s' must have resource limits", containerName)));
return violations;
}
// Check memory limits
if (!limits.containsKey("memory")) {
violations.add(new PolicyViolation("MEMORY_LIMIT_REQUIRED",
String.format("Container '%s' must have memory limit", containerName)));
}
// Check CPU limits
if (!limits.containsKey("cpu")) {
violations.add(new PolicyViolation("CPU_LIMIT_REQUIRED",
String.format("Container '%s' must have CPU limit", containerName)));
}
return violations;
}
/**
* Validate security context
*/
private List<PolicyViolation> validateSecurityContext(Map<String, Object> resource) {
List<PolicyViolation> violations = new ArrayList<>();
if ("Pod".equals(resource.get("kind"))) {
Map<String, Object> spec = (Map<String, Object>) resource.get("spec");
Map<String, Object> securityContext = (Map<String, Object>) spec.get("securityContext");
if (securityContext == null) {
violations.add(new PolicyViolation("SECURITY_CONTEXT_REQUIRED",
"Pod must have security context"));
return violations;
}
// Check runAsNonRoot
Boolean runAsNonRoot = (Boolean) securityContext.get("runAsNonRoot");
if (runAsNonRoot == null || !runAsNonRoot) {
violations.add(new PolicyViolation("RUN_AS_NON_ROOT_REQUIRED",
"Pod must run as non-root user"));
}
}
return violations;
}
/**
* Validate network policies
*/
private List<PolicyViolation> validateNetworkPolicies(Map<String, Object> resource) {
List<PolicyViolation> violations = new ArrayList<>();
if ("Service".equals(resource.get("kind"))) {
Map<String, Object> spec = (Map<String, Object>) resource.get("spec");
String type = (String) spec.get("type");
if ("LoadBalancer".equals(type)) {
violations.add(new PolicyViolation("LOADBALANCER_RESTRICTED",
"LoadBalancer services are restricted"));
}
}
return violations;
}
/**
* Mutate labels
*/
private void mutateLabels(Map<String, Object> resource) {
Map<String, Object> metadata = (Map<String, Object>) resource.get("metadata");
Map<String, String> labels = (Map<String, String>) metadata.get("labels");
if (labels == null) {
labels = new HashMap<>();
metadata.put("labels", labels);
}
// Add default labels
labels.putIfAbsent("managed-by", "kyverno-java-client");
labels.putIfAbsent("created-at", String.valueOf(System.currentTimeMillis()));
}
/**
* Mutate annotations
*/
private void mutateAnnotations(Map<String, Object> resource) {
Map<String, Object> metadata = (Map<String, Object>) resource.get("metadata");
Map<String, String> annotations = (Map<String, String>) metadata.get("annotations");
if (annotations == null) {
annotations = new HashMap<>();
metadata.put("annotations", annotations);
}
// Add default annotations
annotations.putIfAbsent("kyverno-mutated", "true");
annotations.putIfAbsent("last-validated", String.valueOf(System.currentTimeMillis()));
}
/**
* Mutate resource limits
*/
private void mutateResourceLimits(Map<String, Object> resource) {
if ("Pod".equals(resource.get("kind")) ||
"Deployment".equals(resource.get("kind"))) {
Map<String, Object> spec = (Map<String, Object>) resource.get("spec");
Map<String, Object> template = (Map<String, Object>) spec.get("template");
Map<String, Object> podSpec = (Map<String, Object>) template.get("spec");
List<Map<String, Object>> containers = (List<Map<String, Object>>) podSpec.get("containers");
for (Map<String, Object> container : containers) {
mutateContainerResources(container);
}
}
}
/**
* Mutate container resources
*/
private void mutateContainerResources(Map<String, Object> container) {
Map<String, Object> resources = (Map<String, Object>) container.get("resources");
if (resources == null) {
resources = new HashMap<>();
container.put("resources", resources);
}
Map<String, Object> limits = (Map<String, Object>) resources.get("limits");
if (limits == null) {
limits = new HashMap<>();
resources.put("limits", limits);
}
// Set default limits
limits.putIfAbsent("memory", "128Mi");
limits.putIfAbsent("cpu", "100m");
Map<String, Object> requests = (Map<String, Object>) resources.get("requests");
if (requests == null) {
requests = new HashMap<>();
resources.put("requests", requests);
}
requests.putIfAbsent("memory", "64Mi");
requests.putIfAbsent("cpu", "50m");
}
/**
* Mutate security context
*/
private void mutateSecurityContext(Map<String, Object> resource) {
if ("Pod".equals(resource.get("kind"))) {
Map<String, Object> spec = (Map<String, Object>) resource.get("spec");
Map<String, Object> securityContext = (Map<String, Object>) spec.get("securityContext");
if (securityContext == null) {
securityContext = new HashMap<>();
spec.put("securityContext", securityContext);
}
securityContext.putIfAbsent("runAsNonRoot", true);
securityContext.putIfAbsent("runAsUser", 1000);
}
}
/**
* Add default fields
*/
private void addDefaultFields(Map<String, Object> resource) {
Map<String, Object> metadata = (Map<String, Object>) resource.get("metadata");
// Add namespace if not present
if (metadata.get("namespace") == null) {
metadata.put("namespace", "default");
}
}
/**
* Generate NetworkPolicy
*/
private Map<String, Object> generateNetworkPolicy(Map<String, Object> resource) {
if ("Deployment".equals(resource.get("kind"))) {
Map<String, Object> metadata = (Map<String, Object>) resource.get("metadata");
String name = (String) metadata.get("name");
String namespace = (String) metadata.get("namespace");
Map<String, Object> networkPolicy = new HashMap<>();
Map<String, Object> npMetadata = new HashMap<>();
npMetadata.put("name", name + "-network-policy");
npMetadata.put("namespace", namespace);
networkPolicy.put("apiVersion", "networking.k8s.io/v1");
networkPolicy.put("kind", "NetworkPolicy");
networkPolicy.put("metadata", npMetadata);
// Add NetworkPolicy spec
Map<String, Object> spec = new HashMap<>();
Map<String, Object> podSelector = new HashMap<>();
Map<String, Object> matchLabels = new HashMap<>();
matchLabels.put("app", ((Map<String, String>) metadata.get("labels")).get("app"));
podSelector.put("matchLabels", matchLabels);
spec.put("podSelector", podSelector);
List<Map<String, Object>> policyTypes = Arrays.asList(
Map.of("", "Ingress"),
Map.of("", "Egress")
);
spec.put("policyTypes", policyTypes);
networkPolicy.put("spec", spec);
return networkPolicy;
}
return null;
}
/**
* Generate ConfigMap
*/
private Map<String, Object> generateConfigMap(Map<String, Object> resource) {
// Implementation for ConfigMap generation
return null;
}
/**
* Deep copy a map
*/
@SuppressWarnings("unchecked")
private Map<String, Object> deepCopy(Map<String, Object> original) {
try {
String json = jsonMapper.writeValueAsString(original);
return jsonMapper.readValue(json, Map.class);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to deep copy resource", e);
}
}
@Data
@AllArgsConstructor
public static class ValidationResult {
private boolean valid;
private List<PolicyViolation> violations;
public String getSummary() {
if (valid) {
return "Validation passed";
} else {
return String.format("Validation failed with %d violations", violations.size());
}
}
}
@Data
@AllArgsConstructor
public static class PolicyViolation {
private String rule;
private String message;
private String severity; // INFO, WARNING, ERROR
public PolicyViolation(String rule, String message) {
this(rule, message, "ERROR");
}
}
}
Step 5: Policy Validator
// src/main/java/com/company/kyverno/engine/PolicyValidator.java
package com.company.kyverno.engine;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@Slf4j
public class PolicyValidator {
private final PolicyEngine policyEngine;
private final ObjectMapper yamlMapper;
private final ObjectMapper jsonMapper;
public PolicyValidator(PolicyEngine policyEngine) {
this.policyEngine = policyEngine;
this.yamlMapper = new ObjectMapper(new YAMLFactory());
this.jsonMapper = new ObjectMapper();
}
/**
* Validate YAML resource
*/
public PolicyEngine.ValidationResult validateYaml(String yamlContent) {
try {
Map<String, Object> resource = yamlMapper.readValue(yamlContent, Map.class);
return policyEngine.validateResource(resource);
} catch (JsonProcessingException e) {
log.error("Failed to parse YAML content", e);
return new PolicyEngine.ValidationResult(false,
java.util.List.of(new PolicyEngine.PolicyViolation("INVALID_YAML", "Failed to parse YAML")));
}
}
/**
* Validate JSON resource
*/
public PolicyEngine.ValidationResult validateJson(String jsonContent) {
try {
Map<String, Object> resource = jsonMapper.readValue(jsonContent, Map.class);
return policyEngine.validateResource(resource);
} catch (JsonProcessingException e) {
log.error("Failed to parse JSON content", e);
return new PolicyEngine.ValidationResult(false,
java.util.List.of(new PolicyEngine.PolicyViolation("INVALID_JSON", "Failed to parse JSON")));
}
}
/**
* Mutate YAML resource
*/
public String mutateYaml(String yamlContent) {
try {
Map<String, Object> resource = yamlMapper.readValue(yamlContent, Map.class);
Map<String, Object> mutatedResource = policyEngine.mutateResource(resource);
return yamlMapper.writeValueAsString(mutatedResource);
} catch (JsonProcessingException e) {
log.error("Failed to process YAML content", e);
return yamlContent; // Return original on error
}
}
/**
* Mutate JSON resource
*/
public String mutateJson(String jsonContent) {
try {
Map<String, Object> resource = jsonMapper.readValue(jsonContent, Map.class);
Map<String, Object> mutatedResource = policyEngine.mutateResource(resource);
return jsonMapper.writeValueAsString(mutatedResource);
} catch (JsonProcessingException e) {
log.error("Failed to process JSON content", e);
return jsonContent; // Return original on error
}
}
/**
* Generate resources from YAML
*/
public String generateFromYaml(String yamlContent) {
try {
Map<String, Object> resource = yamlMapper.readValue(yamlContent, Map.class);
java.util.List<Map<String, Object>> generatedResources = policyEngine.generateResources(resource);
if (generatedResources.isEmpty()) {
return "No resources generated";
}
StringBuilder result = new StringBuilder();
for (Map<String, Object> genResource : generatedResources) {
result.append("---\n")
.append(yamlMapper.writeValueAsString(genResource))
.append("\n");
}
return result.toString();
} catch (JsonProcessingException e) {
log.error("Failed to generate resources from YAML", e);
return "Error generating resources";
}
}
/**
* Batch validate multiple resources
*/
public BatchValidationResult validateMultiple(String multiYamlContent) {
BatchValidationResult result = new BatchValidationResult();
try {
String[] yamlDocuments = multiYamlContent.split("---\n");
for (String yamlDoc : yamlDocuments) {
if (yamlDoc.trim().isEmpty()) continue;
PolicyEngine.ValidationResult validationResult = validateYaml(yamlDoc);
result.addResult(yamlDoc, validationResult);
}
} catch (Exception e) {
log.error("Failed to validate multiple resources", e);
result.setOverallValid(false);
result.setErrorMessage("Batch validation failed: " + e.getMessage());
}
return result;
}
@Data
public static class BatchValidationResult {
private boolean overallValid = true;
private String errorMessage;
private java.util.Map<String, PolicyEngine.ValidationResult> results = new java.util.HashMap<>();
public void addResult(String resource, PolicyEngine.ValidationResult result) {
results.put(resource, result);
if (!result.isValid()) {
overallValid = false;
}
}
public int getTotalResources() {
return results.size();
}
public int getValidResources() {
return (int) results.values().stream()
.filter(PolicyEngine.ValidationResult::isValid)
.count();
}
public int getViolationCount() {
return results.values().stream()
.mapToInt(result -> result.getViolations().size())
.sum();
}
}
}
Step 6: Policy Management Service
// src/main/java/com/company/kyverno/service/PolicyManagementService.java
package com.company.kyverno.service;
import com.company.kyverno.client.KyvernoPolicyClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.kubernetes.client.openapi.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
@Slf4j
public class PolicyManagementService {
private final KyvernoPolicyClient policyClient;
private final ObjectMapper yamlMapper;
public PolicyManagementService(KyvernoPolicyClient policyClient) {
this.policyClient = policyClient;
this.yamlMapper = new ObjectMapper(new YAMLFactory());
}
/**
* Create a ClusterPolicy from YAML
*/
public Map<String, Object> createClusterPolicyFromYaml(String policyYaml) throws ApiException {
try {
Map<String, Object> policy = yamlMapper.readValue(policyYaml, Map.class);
return policyClient.createClusterPolicy(policy);
} catch (JsonProcessingException e) {
log.error("Failed to parse policy YAML", e);
throw new IllegalArgumentException("Invalid policy YAML format", e);
}
}
/**
* Create a namespaced Policy from YAML
*/
public Map<String, Object> createPolicyFromYaml(String namespace, String policyYaml) throws ApiException {
try {
Map<String, Object> policy = yamlMapper.readValue(policyYaml, Map.class);
return policyClient.createPolicy(namespace, policy);
} catch (JsonProcessingException e) {
log.error("Failed to parse policy YAML", e);
throw new IllegalArgumentException("Invalid policy YAML format", e);
}
}
/**
* Create standard security policies
*/
public void createStandardSecurityPolicies() throws ApiException {
// Require resource limits
createClusterPolicyFromYaml("""
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: enforce
background: false
rules:
- name: validate-resource-limits
match:
resources:
kinds:
- Pod
- Deployment
- StatefulSet
validate:
message: "CPU and memory limits are required"
pattern:
spec:
template:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
""");
// Require security context
createClusterPolicyFromYaml("""
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-security-context
spec:
validationFailureAction: enforce
background: false
rules:
- name: require-run-as-non-root
match:
resources:
kinds:
- Pod
validate:
message: "Pods must run as non-root"
pattern:
spec:
securityContext:
runAsNonRoot: true
""");
// Block latest tags
createClusterPolicyFromYaml("""
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: block-latest-tag
spec:
validationFailureAction: enforce
background: false
rules:
- name: block-latest-tag
match:
resources:
kinds:
- Pod
validate:
message: "Using latest tag is not allowed"
pattern:
spec:
containers:
- image: "!*:latest"
""");
log.info("Standard security policies created successfully");
}
/**
* Create network policies
*/
public void createNetworkPolicies() throws ApiException {
createClusterPolicyFromYaml("""
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-network-policy
spec:
validationFailureAction: audit
background: true
rules:
- name: generate-network-policy
match:
resources:
kinds:
- Deployment
generate:
kind: NetworkPolicy
name: "{{request.object.metadata.name}}-network-policy"
namespace: "{{request.object.metadata.namespace}}"
synchronize: true
data:
spec:
podSelector:
matchLabels:
app: "{{request.object.metadata.labels.app}}"
policyTypes:
- Ingress
- Egress
""");
}
/**
* Get all policies with status
*/
public PolicySummary getPolicySummary() throws ApiException {
PolicySummary summary = new PolicySummary();
Map<String, Object> clusterPolicies = policyClient.listClusterPolicies();
List<Map<String, Object>> items = (List<Map<String, Object>>) clusterPolicies.get("items");
if (items != null) {
summary.setClusterPolicyCount(items.size());
for (Map<String, Object> policy : items) {
Map<String, Object> metadata = (Map<String, Object>) policy.get("metadata");
String name = (String) metadata.get("name");
summary.addClusterPolicy(name, "Active");
}
}
return summary;
}
/**
* Validate policy syntax
*/
public PolicyValidationResult validatePolicySyntax(String policyYaml) {
try {
Map<String, Object> policy = yamlMapper.readValue(policyYaml, Map.class);
// Check required fields
if (!policy.containsKey("apiVersion") || !policy.containsKey("kind") || !policy.containsKey("metadata")) {
return new PolicyValidationResult(false, "Policy missing required fields");
}
Map<String, Object> metadata = (Map<String, Object>) policy.get("metadata");
if (metadata.get("name") == null) {
return new PolicyValidationResult(false, "Policy name is required");
}
Map<String, Object> spec = (Map<String, Object>) policy.get("spec");
if (spec == null) {
return new PolicyValidationResult(false, "Policy spec is required");
}
return new PolicyValidationResult(true, "Policy syntax is valid");
} catch (JsonProcessingException e) {
return new PolicyValidationResult(false, "Invalid YAML format: " + e.getMessage());
}
}
@Data
public static class PolicySummary {
private int clusterPolicyCount;
private int namespacedPolicyCount;
private Map<String, String> clusterPolicies = new HashMap<>();
private Map<String, String> namespacedPolicies = new HashMap<>();
public void addClusterPolicy(String name, String status) {
clusterPolicies.put(name, status);
}
public void addNamespacedPolicy(String namespace, String name, String status) {
namespacedPolicies.put(namespace + "/" + name, status);
}
}
@Data
@AllArgsConstructor
public static class PolicyValidationResult {
private boolean valid;
private String message;
private List<String> warnings;
public PolicyValidationResult(boolean valid, String message) {
this.valid = valid;
this.message = message;
this.warnings = new ArrayList<>();
}
}
}
Step 7: REST API Controllers
// src/main/java/com/company/kyverno/controller/PolicyController.java
package com.company.kyverno.controller;
import com.company.kyverno.engine.PolicyEngine;
import com.company.kyverno.engine.PolicyValidator;
import com.company.kyverno.service.PolicyManagementService;
import io.kubernetes.client.openapi.ApiException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/kyverno")
@RequiredArgsConstructor
@Slf4j
public class PolicyController {
private final PolicyValidator policyValidator;
private final PolicyManagementService policyManagementService;
private final PolicyEngine policyEngine;
@PostMapping("/validate/yaml")
public ResponseEntity<Map<String, Object>> validateYaml(@RequestBody ValidationRequest request) {
try {
PolicyValidator.BatchValidationResult result = policyValidator.validateMultiple(request.getYamlContent());
return ResponseEntity.ok(Map.of(
"valid", result.isOverallValid(),
"totalResources", result.getTotalResources(),
"validResources", result.getValidResources(),
"violationCount", result.getViolationCount(),
"results", result.getResults()
));
} catch (Exception e) {
log.error("Validation failed", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Validation failed",
"message", e.getMessage()
));
}
}
@PostMapping("/mutate/yaml")
public ResponseEntity<Map<String, Object>> mutateYaml(@RequestBody ValidationRequest request) {
try {
String mutatedYaml = policyValidator.mutateYaml(request.getYamlContent());
return ResponseEntity.ok(Map.of(
"success", true,
"mutatedYaml", mutatedYaml,
"originalLength", request.getYamlContent().length(),
"mutatedLength", mutatedYaml.length()
));
} catch (Exception e) {
log.error("Mutation failed", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Mutation failed",
"message", e.getMessage()
));
}
}
@PostMapping("/generate/yaml")
public ResponseEntity<Map<String, Object>> generateFromYaml(@RequestBody ValidationRequest request) {
try {
String generatedResources = policyValidator.generateFromYaml(request.getYamlContent());
return ResponseEntity.ok(Map.of(
"success", true,
"generatedResources", generatedResources
));
} catch (Exception e) {
log.error("Generation failed", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Generation failed",
"message", e.getMessage()
));
}
}
@PostMapping("/policies/cluster")
public ResponseEntity<Map<String, Object>> createClusterPolicy(@RequestBody PolicyRequest request) {
try {
var validationResult = policyManagementService.validatePolicySyntax(request.getPolicyYaml());
if (!validationResult.isValid()) {
return ResponseEntity.badRequest().body(Map.of(
"error", "Invalid policy syntax",
"message", validationResult.getMessage()
));
}
Map<String, Object> result = policyManagementService.createClusterPolicyFromYaml(request.getPolicyYaml());
return ResponseEntity.ok(Map.of(
"success", true,
"policy", result
));
} catch (ApiException e) {
log.error("Failed to create cluster policy", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Failed to create policy",
"message", e.getResponseBody()
));
} catch (Exception e) {
log.error("Unexpected error", e);
return ResponseEntity.internalServerError().body(Map.of(
"error", "Internal server error",
"message", e.getMessage()
));
}
}
@PostMapping("/policies/standard/security")
public ResponseEntity<Map<String, Object>> createStandardSecurityPolicies() {
try {
policyManagementService.createStandardSecurityPolicies();
return ResponseEntity.ok(Map.of(
"success", true,
"message", "Standard security policies created successfully"
));
} catch (Exception e) {
log.error("Failed to create standard policies", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Failed to create standard policies",
"message", e.getMessage()
));
}
}
@GetMapping("/policies/summary")
public ResponseEntity<Map<String, Object>> getPolicySummary() {
try {
var summary = policyManagementService.getPolicySummary();
return ResponseEntity.ok(Map.of(
"success", true,
"summary", summary
));
} catch (Exception e) {
log.error("Failed to get policy summary", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Failed to get policy summary",
"message", e.getMessage()
));
}
}
@PostMapping("/validate/syntax")
public ResponseEntity<Map<String, Object>> validatePolicySyntax(@RequestBody PolicyRequest request) {
try {
var result = policyManagementService.validatePolicySyntax(request.getPolicyYaml());
return ResponseEntity.ok(Map.of(
"valid", result.isValid(),
"message", result.getMessage(),
"warnings", result.getWarnings()
));
} catch (Exception e) {
log.error("Syntax validation failed", e);
return ResponseEntity.badRequest().body(Map.of(
"error", "Syntax validation failed",
"message", e.getMessage()
));
}
}
@Data
public static class ValidationRequest {
private String yamlContent;
}
@Data
public static class PolicyRequest {
private String policyYaml;
private String namespace;
}
}
Step 8: Application Configuration
application.yml
# application.yml
kubernetes:
master:
url: https://kubernetes.default.svc
token: ${KUBERNETES_TOKEN:}
namespace: ${KUBERNETES_NAMESPACE:default}
kyverno:
policy:
enforcement:
enabled: true
default-action: audit # audit, enforce
mutation:
enabled: true
generation:
enabled: true
server:
port: 8080
logging:
level:
com.company.kyverno: DEBUG
io.kubernetes: INFO
Best Practices
- Security
- Use RBAC to restrict policy management
- Validate all inputs thoroughly
- Implement proper error handling
- Use secure communication with Kubernetes API
- Performance
- Cache policy evaluations when possible
- Use background validation for non-critical resources
- Implement rate limiting for API calls
- Reliability
- Implement retry mechanisms for Kubernetes API calls
- Use proper logging and monitoring
- Implement health checks
- Maintainability
- Use consistent policy naming conventions
- Document policy rules and purposes
- Implement policy versioning
Conclusion
This Kyverno Java implementation provides:
- Policy Validation: Comprehensive resource validation
- Resource Mutation: Automatic resource modification
- Policy Generation: Dynamic resource creation
- Policy Management: Full lifecycle management of Kyverno policies
Key features:
- Multi-format Support: YAML and JSON validation
- Batch Operations: Validate multiple resources at once
- Standard Policies: Pre-built security policies
- Extensible Architecture: Easy to add custom validation rules
- Kubernetes Integration: Direct API integration
This solution enables Java applications to enforce Kubernetes policies consistently across development, CI/CD, and runtime environments.