Admission Controllers and Policy Enforcement

Admission Controllers and Policy Enforcement

Admission controllers provide policy enforcement at the orchestration layer. They can modify or reject resource creation based on security policies. ValidatingAdmissionWebhooks ensure resources meet security requirements. MutatingAdmissionWebhooks can add security defaults to resources. Policy engines like Open Policy Agent enable complex policy logic.

Implementing admission control requires careful policy design to avoid blocking legitimate operations. Policies should provide clear error messages guiding users toward compliance. Dry-run modes allow policy testing before enforcement. Exemption mechanisms enable emergency overrides with proper audit trails. Regular policy reviews ensure continued relevance.

// Example: Admission webhook for security enforcement
package main

import (
    "context"
    "crypto/tls"
    "encoding/json"
    "fmt"
    "net/http"
    
    admissionv1 "k8s.io/api/admission/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type SecurityAdmissionWebhook struct {
    MinimumTLSVersion string
    RequiredLabels    []string
    ForbiddenImages   []string
}

func (s *SecurityAdmissionWebhook) Validate(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    pod := corev1.Pod{}
    if err := json.Unmarshal(ar.Request.Object.Raw, &pod); err != nil {
        return &admissionv1.AdmissionResponse{
            Result: &metav1.Status{
                Message: err.Error(),
            },
        }
    }
    
    // Check security policies
    violations := []string{}
    
    // Verify required labels
    for _, label := range s.RequiredLabels {
        if _, exists := pod.Labels[label]; !exists {
            violations = append(violations, fmt.Sprintf("Missing required label: %s", label))
        }
    }
    
    // Check container configurations
    for _, container := range pod.Spec.Containers {
        // Verify image sources
        for _, forbidden := range s.ForbiddenImages {
            if strings.Contains(container.Image, forbidden) {
                violations = append(violations, 
                    fmt.Sprintf("Forbidden image registry: %s", container.Image))
            }
        }
        
        // Ensure non-root user
        if container.SecurityContext == nil || 
           container.SecurityContext.RunAsNonRoot == nil || 
           !*container.SecurityContext.RunAsNonRoot {
            violations = append(violations, 
                fmt.Sprintf("Container %s must run as non-root", container.Name))
        }
        
        // Check for privileged mode
        if container.SecurityContext != nil && 
           container.SecurityContext.Privileged != nil && 
           *container.SecurityContext.Privileged {
            violations = append(violations, 
                fmt.Sprintf("Container %s cannot run in privileged mode", container.Name))
        }
        
        // Verify resource limits
        if container.Resources.Limits == nil {
            violations = append(violations, 
                fmt.Sprintf("Container %s must specify resource limits", container.Name))
        }
    }
    
    // Return validation result
    allowed := len(violations) == 0
    result := &admissionv1.AdmissionResponse{
        UID:     ar.Request.UID,
        Allowed: allowed,
    }
    
    if !allowed {
        result.Result = &metav1.Status{
            Message: fmt.Sprintf("Security policy violations: %s", 
                strings.Join(violations, "; ")),
        }
    }
    
    return result
}

func (s *SecurityAdmissionWebhook) Mutate(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    pod := corev1.Pod{}
    if err := json.Unmarshal(ar.Request.Object.Raw, &pod); err != nil {
        return &admissionv1.AdmissionResponse{
            Result: &metav1.Status{
                Message: err.Error(),
            },
        }
    }
    
    patches := []map[string]interface{}{}
    
    // Add security defaults
    if pod.Labels == nil {
        patches = append(patches, map[string]interface{}{
            "op":    "add",
            "path":  "/metadata/labels",
            "value": map[string]string{},
        })
    }
    
    // Add security scanning label
    patches = append(patches, map[string]interface{}{
        "op":    "add",
        "path":  "/metadata/labels/security-scan",
        "value": "required",
    })
    
    // Set security context if missing
    for i, container := range pod.Spec.Containers {
        if container.SecurityContext == nil {
            patches = append(patches, map[string]interface{}{
                "op":   "add",
                "path": fmt.Sprintf("/spec/containers/%d/securityContext", i),
                "value": map[string]interface{}{
                    "runAsNonRoot":             true,
                    "readOnlyRootFilesystem":   true,
                    "allowPrivilegeEscalation": false,
                    "capabilities": map[string]interface{}{
                        "drop": []string{"ALL"},
                    },
                },
            })
        }
    }
    
    patchBytes, _ := json.Marshal(patches)
    
    return &admissionv1.AdmissionResponse{
        UID:     ar.Request.UID,
        Allowed: true,
        Patch:   patchBytes,
        PatchType: func() *admissionv1.PatchType {
            pt := admissionv1.PatchTypeJSONPatch
            return &pt
        }(),
    }
}