Skip to content

Commit

Permalink
rewrite validate kyverno policies function to better handle rendered … (
Browse files Browse the repository at this point in the history
#52)

…templates
  • Loading branch information
sachinchauhan23 authored Nov 28, 2024
1 parent 6219e6f commit 8fdae1a
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 39 deletions.
78 changes: 41 additions & 37 deletions targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func Validate() error {
}
mg.Deps(mg.F(kubeScore, templates))
mg.Deps(mg.F(kubeConform, templates, "api-platform"))
mg.Deps(mg.F(validateKyvernoPolicies, templates))
// mg.Deps(mg.F(validateKyvernoPolicies, templates))
mg.Deps(Pallets)
fmt.Println("Validation passed")
return nil
Expand Down Expand Up @@ -80,13 +80,13 @@ func ArgoCDDiff() error {
// ValidateKyverno runs render Kubernetes manifests and invokes validateKyvernoPolicies.
func ValidateKyverno() error {
// Render templates and obtain paths
renderedTemplates, err := renderTemplates()
paths, err := renderTemplates()
if err != nil {
return fmt.Errorf("failed to render templates: %w", err)
}

// Validate rendered templates with Kyverno
err = validateKyvernoPolicies(renderedTemplates)
err = validateKyvernoPolicies(paths)
if err != nil {
return fmt.Errorf("Kyverno validation failed: %w", err)
}
Expand All @@ -96,52 +96,56 @@ func ValidateKyverno() error {
}

// validateKyvernoPolicies runs Kyverno validation on rendered Kubernetes manifests.
func validateKyvernoPolicies(renderedTemplatePaths string) error {
policyDir := "kyverno-policies" // Directory where policies are stored
func validateKyvernoPolicies(paths string) error {
if paths == "" {
fmt.Println("No rendered templates provided for validation, skipping.")
return nil
}

policyDir := "kyverno-policies" // Directory where policies are stored
templatePaths := strings.Split(paths, ",") // Split the input paths into a list

// Prepare resource arguments for the Kyverno CLI
resourceArgs := []string{}
for _, templatePath := range templatePaths {
templatePath = strings.TrimSpace(templatePath)
if templatePath == "" {
continue
}
resourceArgs = append(resourceArgs, "-r", templatePath)
}

if len(resourceArgs) == 0 {
fmt.Println("No valid rendered templates found for validation.")
return nil
}

templateFiles, err := os.ReadDir(renderedTemplatePaths)
// Iterate over all policies and apply them
policyFiles, err := os.ReadDir(policyDir)
if err != nil {
return fmt.Errorf("failed to read rendered templates: %w", err)
return fmt.Errorf("failed to read Kyverno policies: %w", err)
}
for _, templateFile := range templateFiles {
// Skip if it’s a directory

if templateFile.IsDir() {
for _, policyFile := range policyFiles {
if !strings.HasSuffix(policyFile.Name(), ".yaml") {
continue
}
// Construct the full path for the current template file
templatePath := fmt.Sprintf("%s/%s", renderedTemplatePaths, templateFile.Name())

policyFiles, err := os.ReadDir(policyDir)
policyFilePath := fmt.Sprintf("%s/%s", policyDir, policyFile.Name())
cmdOptions := append([]string{"apply", policyFilePath, "--policy-report", "--output", "yaml"}, resourceArgs...)

output, err := sh.Output("kyverno", cmdOptions...)
if err != nil {
return fmt.Errorf("failed to read Kyverno policies: %w", err)
return fmt.Errorf("Kyverno validation failed for policy '%s': %w", policyFilePath, err)
}

for _, policyFile := range policyFiles {
if !strings.HasSuffix(policyFile.Name(), ".yaml") {
continue
}

policyFilePath := fmt.Sprintf("%s/%s", policyDir, policyFile.Name())
cmdOptions := []string{
"apply", policyFilePath,
"--resource", templatePath,
"--policy-report",
"--output", "yaml",
}

output, err := sh.Output("kyverno", cmdOptions...)
if err != nil {
return fmt.Errorf("Kyverno validation failed for template '%s' with policy '%s': %w", templatePath, policyFilePath, err)
}

fmt.Printf("Kyverno validation for template '%s' with policy '%s' completed.\n", templatePath, policyFilePath)

if strings.Contains(output, "violation") || strings.Contains(output, "failed") {
return fmt.Errorf("Kyverno validation issues found in template '%s' with policy '%s': %s", templatePath, policyFilePath, output)
}
fmt.Printf("Kyverno validation with policy '%s' completed.\n", policyFilePath)

if strings.Contains(output, "violation") || strings.Contains(output, "failed") {
return fmt.Errorf("Kyverno validation issues found with policy '%s': %s", policyFilePath, output)
}
}

return nil
}

Expand Down
4 changes: 2 additions & 2 deletions targets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestOKKubeConform(t *testing.T) {

// Test for manifest files expected to fail Kyverno policy validation
func TestFailedValidateKyverno(t *testing.T) {
path := "tests/templates/validate-fail/"
path := "tests/templates/validate-fail/deployment-fail.yaml,tests/templates/validate-fail/deployment-fail.yaml,"
err := validateKyvernoPolicies(path)
if err == nil {
t.Fatalf("Expected validation to fail for manifest %s, but it passed", path)
Expand All @@ -48,7 +48,7 @@ func TestFailedValidateKyverno(t *testing.T) {

// Test for manifest files expected to pass Kyverno policy validation
func TestOKValidateKyverno(t *testing.T) {
path := "tests/templates/validate/"
path := "tests/templates/validate/deployment-ok.yaml,tests/templates/validate/deployment2-ok.yaml,"
err := validateKyvernoPolicies(path)
if err != nil {
t.Fatalf("Expected validation to pass for manifest %s, but it failed with error: %v", path, err)
Expand Down
36 changes: 36 additions & 0 deletions tests/templates/validate-fail/deployment2-fail.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: noncompliant-app
labels:
app: noncompliant-app
spec:
replicas: 1
selector:
matchLabels:
app: noncompliant-app
template:
metadata:
labels:
app: noncompliant-app
spec:
containers:
- name: noncompliant-container
image: mysql:latest # Violates "no latest tag" policy
securityContext:
capabilities: # Violates "disallow capabilities" policy
add:
- NET_ADMIN
- SYS_MODULE
privileged: true # Violates "no privileged containers" policy
runAsNonRoot: false # Violates "must run as non-root" policy
readOnlyRootFilesystem: false # Violates "read-only root filesystem" policy
resources:
requests: # Violates "resource requests and limits required" policy
memory: "0"
cpu: "0"
limits:
memory: "0"
cpu: "0"
ports:
- containerPort: 3306 # Exposes a port without proper context
36 changes: 36 additions & 0 deletions tests/templates/validate/deployment2-ok.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: compliance-app
labels:
app: compliance-app
spec:
replicas: 1
selector:
matchLabels:
app: compliance-app
template:
metadata:
labels:
app: compliance-app
spec:
containers:
- name: compliance-container
image: bitnami/redis:7.0.10 # Uses a specific, secure image version
securityContext:
runAsNonRoot: true # Enforces running as a non-root user
runAsUser: 1001 # Specifies a non-root user ID
allowPrivilegeEscalation: false # Prevents privilege escalation
capabilities: # Drops unnecessary capabilities
drop:
- ALL
readOnlyRootFilesystem: true # Makes the root filesystem immutable
resources:
requests: # Defines minimum resource requests
memory: "64Mi"
cpu: "250m"
limits: # Defines maximum resource limits
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 6379 # Specifies the Redis port exposed by the container

0 comments on commit 8fdae1a

Please sign in to comment.