package kubernetes import ( "errors" "fmt" "reflect" "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // ValidateAdmittedPod verifies the fields Kandev owns after API admission. // Unrelated labels, annotations, scheduling selectors, and server metadata are // intentionally allowed so normal admission/defaulting can still operate. func ValidateAdmittedPod(admitted, desired *corev1.Pod, mainContainer string) error { if admitted == nil || desired == nil { return errors.New("admitted Pod validation requires both objects") } if err := validateAdmittedMetadata("Pod", admitted.ObjectMeta, desired.ObjectMeta); err != nil { return err } if err := validateAdmittedPodSpec(admitted, desired); err != nil { return err } desiredMain := admittedContainerByName(desired.Spec.Containers, mainContainer) admittedMain := admittedContainerByName(admitted.Spec.Containers, mainContainer) if desiredMain == nil || admittedMain == nil { return errors.New("admitted Pod is missing the owned main container") } if !reflect.DeepEqual(admittedMain.Command, desiredMain.Command) || !reflect.DeepEqual(admittedMain.Args, desiredMain.Args) || admittedMain.WorkingDir != desiredMain.WorkingDir { return errors.New("admitted Pod mutated the main-container bootstrap") } if err := validateAdmittedWorkspaceGrants(admitted, desired, mainContainer); err != nil { return err } if err := validateAdmittedContainers(admitted, desiredMain, mainContainer); err != nil { return err } return validateAdmittedVolumes(admitted.Spec.Volumes, desired.Spec.Volumes) } // ValidateAdmittedPVC verifies the Kandev-owned shape of a managed claim while // allowing ordinary API binding fields and default storage-class selection. func ValidateAdmittedPVC(admitted, desired *corev1.PersistentVolumeClaim) error { if admitted == nil || desired == nil { return errors.New("admitted PVC validation requires both objects") } if err := validateAdmittedMetadata("PVC", admitted.ObjectMeta, desired.ObjectMeta); err != nil { return err } if !samePersistentVolumeAccessModes(admitted.Spec.AccessModes, desired.Spec.AccessModes) || !reflect.DeepEqual(admitted.Spec.VolumeMode, desired.Spec.VolumeMode) || !sameVolumeResourceRequirements(admitted.Spec.Resources, desired.Spec.Resources) { return errors.New("admitted PVC mutated owned storage requirements") } if desired.Spec.StorageClassName != nil && !reflect.DeepEqual(admitted.Spec.StorageClassName, desired.Spec.StorageClassName) { return errors.New("admitted PVC mutated the explicit storage class") } if !reflect.DeepEqual(admitted.Spec.VolumeAttributesClassName, desired.Spec.VolumeAttributesClassName) { return errors.New("admitted PVC mutated the volume attributes class") } if !reflect.DeepEqual(admitted.Spec.Selector, desired.Spec.Selector) || !reflect.DeepEqual(admitted.Spec.DataSource, desired.Spec.DataSource) || !reflect.DeepEqual(admitted.Spec.DataSourceRef, desired.Spec.DataSourceRef) { return errors.New("admitted PVC mutated selector or data-source semantics") } return nil } // ValidateOwnershipLabels verifies all Kandev-owned standard and custom // ownership labels while allowing unrelated labels added by the API server or // cluster policy. func ValidateOwnershipLabels(admitted, desired map[string]string) error { return validateAdmittedOwnedLabels("resource", admitted, desired) } func validateAdmittedMetadata(kind string, admitted, desired metav1.ObjectMeta) error { if admitted.Name != desired.Name || admitted.Namespace != desired.Namespace { return fmt.Errorf("admitted %s name or namespace changed", kind) } if len(admitted.OwnerReferences) != 0 { return fmt.Errorf("admitted %s added an owner reference", kind) } if !admittedFinalizersAllowed(kind, admitted.Finalizers) { return fmt.Errorf("admitted %s added a finalizer", kind) } if err := validateAdmittedOwnedLabels(kind, admitted.Labels, desired.Labels); err != nil { return err } return validateAdmittedOwnedAnnotations(kind, admitted.Annotations, desired.Annotations) } func validateAdmittedOwnedLabels(kind string, admitted, desired map[string]string) error { for key, desiredValue := range desired { if !isOwnedMetadataLabel(key) { continue } if admitted[key] != desiredValue { return fmt.Errorf("admitted %s ownership label %q changed", kind, key) } } for key, admittedValue := range admitted { if !isOwnedMetadataLabel(key) { continue } desiredValue, exists := desired[key] if !exists || admittedValue != desiredValue { return fmt.Errorf("admitted %s added or changed ownership label %q", kind, key) } } return nil } func validateAdmittedOwnedAnnotations(kind string, admitted, desired map[string]string) error { for key, desiredValue := range desired { if !strings.HasPrefix(key, "kandev.ai/") { continue } if admitted[key] != desiredValue { return fmt.Errorf("admitted %s owned annotation %q changed", kind, key) } } for key, admittedValue := range admitted { if !strings.HasPrefix(key, "kandev.ai/") { continue } desiredValue, exists := desired[key] if !exists || admittedValue != desiredValue { return fmt.Errorf("admitted %s added or changed owned annotation %q", kind, key) } } return nil } func isOwnedMetadataLabel(key string) bool { return reservedLabelKeys[key] || strings.HasPrefix(key, "kandev.ai/") } func admittedFinalizersAllowed(kind string, finalizers []string) bool { if len(finalizers) == 0 { return true } return kind == "PVC" && len(finalizers) == 1 && finalizers[0] == "kubernetes.io/pvc-protection" } func validateAdmittedPodSpec(admitted, desired *corev1.Pod) error { if admitted.Spec.RestartPolicy != desired.Spec.RestartPolicy || admitted.Spec.NodeName != desired.Spec.NodeName || !reflect.DeepEqual(admitted.Spec.OS, desired.Spec.OS) || !reflect.DeepEqual(admitted.Spec.AutomountServiceAccountToken, desired.Spec.AutomountServiceAccountToken) { return errors.New("admitted Pod mutated an owned Pod invariant") } for _, key := range []string{corev1.LabelOSStable, corev1.LabelArchStable} { if admitted.Spec.NodeSelector[key] != desired.Spec.NodeSelector[key] { return fmt.Errorf("admitted Pod mutated owned node selector %q", key) } } return validateAdmittedNodeAffinity(admitted.Spec.Affinity, desired.Spec.Affinity, desired.Spec.NodeSelector) } type admittedNodeRequirement struct { requirement corev1.NodeSelectorRequirement path string preferred bool matchField bool } func validateAdmittedNodeAffinity( admitted, desired *corev1.Affinity, nodeSelector map[string]string, ) error { desiredRequirements := platformNodeRequirements(desired) matchedDesired := make([]bool, len(desiredRequirements)) for _, admittedRequirement := range platformNodeRequirements(admitted) { if matchDesiredNodeRequirement(admittedRequirement, desiredRequirements, matchedDesired) { continue } if admittedRequirement.matchField || !platformRequirementAllows(admittedRequirement.requirement, nodeSelector) { return fmt.Errorf("admitted Pod added conflicting platform node affinity at %s", admittedRequirement.path) } } for _, matched := range matchedDesired { if !matched { return errors.New("admitted Pod mutated desired platform node affinity") } } return nil } func matchDesiredNodeRequirement( admitted admittedNodeRequirement, desired []admittedNodeRequirement, matched []bool, ) bool { for index, candidate := range desired { if matched[index] || admitted.preferred != candidate.preferred || admitted.matchField != candidate.matchField { continue } if reflect.DeepEqual(admitted.requirement, candidate.requirement) { matched[index] = true return true } } return false } func platformNodeRequirements(affinity *corev1.Affinity) []admittedNodeRequirement { if affinity == nil || affinity.NodeAffinity == nil { return nil } var requirements []admittedNodeRequirement nodeAffinity := affinity.NodeAffinity if required := nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution; required != nil { for termIndex, term := range required.NodeSelectorTerms { requirements = appendPlatformNodeRequirements(requirements, term, false, termIndex) } } for termIndex, term := range nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution { requirements = appendPlatformNodeRequirements(requirements, term.Preference, true, termIndex) } return requirements } func appendPlatformNodeRequirements( result []admittedNodeRequirement, term corev1.NodeSelectorTerm, preferred bool, termIndex int, ) []admittedNodeRequirement { prefix := "required" if preferred { prefix = "preferred" } for index, requirement := range term.MatchExpressions { if isPlatformNodeKey(requirement.Key) { result = append(result, admittedNodeRequirement{ requirement: requirement, path: fmt.Sprintf("%s[%d].matchExpressions[%d]", prefix, termIndex, index), preferred: preferred, }) } } for index, requirement := range term.MatchFields { if isPlatformNodeKey(requirement.Key) { result = append(result, admittedNodeRequirement{ requirement: requirement, path: fmt.Sprintf("%s[%d].matchFields[%d]", prefix, termIndex, index), preferred: preferred, matchField: true, }) } } return result } func platformRequirementAllows(requirement corev1.NodeSelectorRequirement, nodeSelector map[string]string) bool { want, ok := nodeSelector[requirement.Key] if !ok || want == "" { return false } switch requirement.Operator { case corev1.NodeSelectorOpIn: return stringSliceContains(requirement.Values, want) case corev1.NodeSelectorOpNotIn: return !stringSliceContains(requirement.Values, want) case corev1.NodeSelectorOpExists: return true default: return false } } func isPlatformNodeKey(key string) bool { return key == corev1.LabelOSStable || key == corev1.LabelArchStable } func stringSliceContains(values []string, want string) bool { for _, value := range values { if value == want { return true } } return false } func validateAdmittedContainers( admitted *corev1.Pod, desiredMain *corev1.Container, mainContainer string, ) error { for index := range admitted.Spec.Containers { container := &admitted.Spec.Containers[index] if container.Name != mainContainer { checked, _, err := companionWithoutWorkspaceGrant(container) if err != nil { return err } container = checked } if err := validateAdmittedContainer(container, desiredMain, container.Name == mainContainer); err != nil { return err } } for index := range admitted.Spec.InitContainers { if err := validateAdmittedContainer(&admitted.Spec.InitContainers[index], desiredMain, false); err != nil { return err } } for index := range admitted.Spec.EphemeralContainers { container := corev1.Container(admitted.Spec.EphemeralContainers[index].EphemeralContainerCommon) if err := validateAdmittedContainer(&container, desiredMain, false); err != nil { return err } } main := admittedContainerByName(admitted.Spec.Containers, mainContainer) return validateRequiredMainContainerFields(main, desiredMain) } func validateAdmittedContainer(container, desiredMain *corev1.Container, isMain bool) error { if err := validateAdmittedContainerEnvironment(container); err != nil { return err } if err := validateAdmittedContainerPorts(container, desiredMain, isMain); err != nil { return err } if err := validateAdmittedContainerMounts(container, desiredMain, isMain); err != nil { return err } return validateAdmittedContainerDevices(container) } func validateAdmittedContainerEnvironment(container *corev1.Container) error { if len(container.EnvFrom) > 0 { return errors.New("admitted Pod uses envFrom, which can inject reserved environment keys") } for _, env := range container.Env { if isReservedEnvironmentKey(env.Name) { return errors.New("admitted Pod added a reserved environment key") } } return nil } func validateAdmittedContainerPorts(container, desiredMain *corev1.Container, isMain bool) error { for _, port := range container.Ports { if port.Name != AgentctlContainerPort && port.ContainerPort != DefaultAgentctlPort { continue } if !isMain || countContainerPorts(desiredMain.Ports, port) != 1 { return errors.New("admitted Pod mutated the agentctl port") } } return nil } func validateAdmittedContainerMounts(container, desiredMain *corev1.Container, isMain bool) error { for _, mount := range container.VolumeMounts { if !isReservedVolumeName(mount.Name) && !isReservedMountPath(mount.MountPath) { continue } if !isMain || countVolumeMounts(desiredMain.VolumeMounts, mount) != 1 { return errors.New("admitted Pod mutated a reserved volume mount") } } return nil } func validateAdmittedContainerDevices(container *corev1.Container) error { for _, device := range container.VolumeDevices { if isReservedVolumeName(device.Name) || isReservedMountPath(device.DevicePath) { return errors.New("admitted Pod added a reserved volume device") } } return nil } func validateRequiredMainContainerFields(admitted, desired *corev1.Container) error { if admitted == nil || desired == nil { return errors.New("admitted Pod is missing the owned main container") } for _, required := range desired.Ports { if required.Name == AgentctlContainerPort && countContainerPorts(admitted.Ports, required) != 1 { return errors.New("admitted Pod mutated the agentctl port") } } for _, required := range desired.VolumeMounts { if isReservedVolumeName(required.Name) && countVolumeMounts(admitted.VolumeMounts, required) != 1 { return errors.New("admitted Pod mutated a reserved volume mount") } } return nil } func validateAdmittedVolumes(admitted, desired []corev1.Volume) error { if err := validateWorkspaceClaimAliases(admitted, desired); err != nil { return err } for _, required := range desired { if isReservedVolumeName(required.Name) && countVolumes(admitted, required) != 1 { return errors.New("admitted Pod mutated a reserved volume") } } for _, volume := range admitted { if !isReservedVolumeName(volume.Name) { continue } if countVolumes(desired, volume) != 1 { return errors.New("admitted Pod added or mutated a reserved volume") } } return nil } func admittedContainerByName(containers []corev1.Container, name string) *corev1.Container { for index := range containers { if containers[index].Name == name { return &containers[index] } } return nil } func countContainerPorts(values []corev1.ContainerPort, want corev1.ContainerPort) int { count := 0 for _, value := range values { if reflect.DeepEqual(value, want) { count++ } } return count } func countVolumeMounts(values []corev1.VolumeMount, want corev1.VolumeMount) int { count := 0 for _, value := range values { if reflect.DeepEqual(value, want) { count++ } } return count } func countVolumes(values []corev1.Volume, want corev1.Volume) int { count := 0 for _, value := range values { if reflect.DeepEqual(value, want) { count++ } } return count } func samePersistentVolumeAccessModes(left, right []corev1.PersistentVolumeAccessMode) bool { leftSet := make(map[corev1.PersistentVolumeAccessMode]struct{}, len(left)) for _, mode := range left { leftSet[mode] = struct{}{} } rightSet := make(map[corev1.PersistentVolumeAccessMode]struct{}, len(right)) for _, mode := range right { rightSet[mode] = struct{}{} } return reflect.DeepEqual(leftSet, rightSet) } func sameVolumeResourceRequirements(left, right corev1.VolumeResourceRequirements) bool { return sameResourceList(left.Requests, right.Requests) && sameResourceList(left.Limits, right.Limits) } func sameResourceList(left, right corev1.ResourceList) bool { if len(left) != len(right) { return false } for name, leftQuantity := range left { rightQuantity, exists := right[name] if !exists || leftQuantity.Cmp(rightQuantity) != 0 { return false } } return true }