Retool validation for pod HostNetwork ports

This will ensure that HostPort == ContainerPort for pods and that
HostPort == 0 || HostPort == ContainerPort for embedded PodSpecs.
This commit is contained in:
Tim Hockin
2023-05-09 15:57:16 -07:00
parent ec3379a717
commit 4bbf611773
3 changed files with 52 additions and 20 deletions

View File

@@ -3481,15 +3481,35 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic
return allErrs
}
func validateHostNetwork(hostNetwork bool, containers []core.Container, fldPath *field.Path) field.ErrorList {
// validatePodHostNetworkDeps checks fields which depend on whether HostNetwork is
// true or not. It should be called on all PodSpecs, but opts can change what
// is enforce. E.g. opts.ResourceIsPod should only be set when called in the
// context of a Pod, and not on PodSpecs which are embedded in other resources
// (e.g. Deployments).
func validatePodHostNetworkDeps(spec *core.PodSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
// For <reasons> we keep `.HostNetwork` in .SecurityContext on the internal
// version of Pod.
hostNetwork := false
if spec.SecurityContext != nil {
hostNetwork = spec.SecurityContext.HostNetwork
}
allErrors := field.ErrorList{}
if hostNetwork {
for i, container := range containers {
fldPath := fldPath.Child("containers")
for i, container := range spec.Containers {
portsPath := fldPath.Index(i).Child("ports")
for i, port := range container.Ports {
idxPath := portsPath.Index(i)
if port.HostPort != port.ContainerPort {
allErrors = append(allErrors, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, "must match `hostPort` when `hostNetwork` is true"))
// At this point, we know that HostNetwork is true. If this
// PodSpec is in a Pod (opts.ResourceIsPod), then HostPort must
// be the same value as ContainerPort. If this PodSpec is in
// some other resource (e.g. Deployment) we allow 0 (i.e.
// unspecified) because it will be defaulted when the Pod is
// ultimately created, but we do not allow any other values.
if hp, cp := port.HostPort, port.ContainerPort; (opts.ResourceIsPod || hp != 0) && hp != cp {
allErrors = append(allErrors, field.Invalid(idxPath.Child("hostPort"), port.HostPort, "must match `containerPort` when `hostNetwork` is true"))
}
}
}
@@ -3694,6 +3714,9 @@ type PodValidationOptions struct {
AllowInvalidTopologySpreadConstraintLabelSelector bool
// Allow node selector additions for gated pods.
AllowMutableNodeSelectorAndNodeAffinity bool
// The top-level resource being validated is a Pod, not just a PodSpec
// embedded in some other resource.
ResourceIsPod bool
}
// validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
@@ -3709,8 +3732,6 @@ func validatePodMetadataAndSpec(pod *core.Pod, opts PodValidationOptions) field.
// we do additional validation only pertinent for pods and not pod templates
// this was done to preserve backwards compatibility
allErrs = append(allErrs, validatePodSecurityContext(pod.Spec.SecurityContext, &pod.Spec, specPath, specPath.Child("securityContext"), opts)...)
if pod.Spec.ServiceAccountName == "" {
for vi, volume := range pod.Spec.Volumes {
path := specPath.Child("volumes").Index(vi).Child("projected")
@@ -3793,6 +3814,7 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
allErrs = append(allErrs, validateContainers(spec.Containers, vols, podClaimNames, fldPath.Child("containers"), opts)...)
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, podClaimNames, fldPath.Child("initContainers"), opts)...)
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts)...)
allErrs = append(allErrs, validatePodHostNetworkDeps(spec, fldPath, opts)...)
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
@@ -4399,18 +4421,6 @@ func validateSysctls(sysctls []core.Sysctl, fldPath *field.Path) field.ErrorList
return allErrs
}
// validatePodSecurityContext verifies the SecurityContext of a Pod, but only
// when it is a Pod and not an embedded PodSpec.
func validatePodSecurityContext(securityContext *core.PodSecurityContext, spec *core.PodSpec, specPath, scPath *field.Path, opts PodValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if securityContext != nil {
allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers, specPath.Child("containers"))...)
}
return allErrs
}
// validatePodSpecSecurityContext verifies the SecurityContext of a PodSpec,
// whether that is defined in a Pod or in an embedded PodSpec (e.g. a
// Deployment's pod template).