mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-19 16:49:35 +00:00
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:
parent
ec3379a717
commit
4bbf611773
@ -3481,15 +3481,35 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic
|
|||||||
return allErrs
|
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{}
|
allErrors := field.ErrorList{}
|
||||||
|
|
||||||
if hostNetwork {
|
if hostNetwork {
|
||||||
for i, container := range containers {
|
fldPath := fldPath.Child("containers")
|
||||||
|
for i, container := range spec.Containers {
|
||||||
portsPath := fldPath.Index(i).Child("ports")
|
portsPath := fldPath.Index(i).Child("ports")
|
||||||
for i, port := range container.Ports {
|
for i, port := range container.Ports {
|
||||||
idxPath := portsPath.Index(i)
|
idxPath := portsPath.Index(i)
|
||||||
if port.HostPort != port.ContainerPort {
|
// At this point, we know that HostNetwork is true. If this
|
||||||
allErrors = append(allErrors, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, "must match `hostPort` when `hostNetwork` is true"))
|
// 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
|
AllowInvalidTopologySpreadConstraintLabelSelector bool
|
||||||
// Allow node selector additions for gated pods.
|
// Allow node selector additions for gated pods.
|
||||||
AllowMutableNodeSelectorAndNodeAffinity bool
|
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,
|
// 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
|
// we do additional validation only pertinent for pods and not pod templates
|
||||||
// this was done to preserve backwards compatibility
|
// 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 == "" {
|
if pod.Spec.ServiceAccountName == "" {
|
||||||
for vi, volume := range pod.Spec.Volumes {
|
for vi, volume := range pod.Spec.Volumes {
|
||||||
path := specPath.Child("volumes").Index(vi).Child("projected")
|
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, 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, 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, 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, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
|
||||||
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
|
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
|
||||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
|
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
|
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,
|
// validatePodSpecSecurityContext verifies the SecurityContext of a PodSpec,
|
||||||
// whether that is defined in a Pod or in an embedded PodSpec (e.g. a
|
// whether that is defined in a Pod or in an embedded PodSpec (e.g. a
|
||||||
// Deployment's pod template).
|
// Deployment's pod template).
|
||||||
|
@ -8816,7 +8816,10 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
opts := PodValidationOptions{
|
||||||
|
ResourceIsPod: true,
|
||||||
|
}
|
||||||
|
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), opts); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -8868,6 +8871,18 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
DNSPolicy: core.DNSClusterFirst,
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
},
|
},
|
||||||
|
"with hostNetwork hostPort unspecified": {
|
||||||
|
Containers: []core.Container{
|
||||||
|
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
|
||||||
|
{HostPort: 0, ContainerPort: 2600, Protocol: "TCP"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityContext: &core.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
|
},
|
||||||
"with hostNetwork hostPort not equal to containerPort": {
|
"with hostNetwork hostPort not equal to containerPort": {
|
||||||
Containers: []core.Container{
|
Containers: []core.Container{
|
||||||
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
|
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
|
||||||
@ -9036,7 +9051,10 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range failureCases {
|
for k, v := range failureCases {
|
||||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
opts := PodValidationOptions{
|
||||||
|
ResourceIsPod: true,
|
||||||
|
}
|
||||||
|
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), opts); len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %q", k)
|
t.Errorf("expected failure for %q", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ func (podStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
|
|||||||
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, nil, &pod.ObjectMeta, nil)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, nil, &pod.ObjectMeta, nil)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
return corevalidation.ValidatePodCreate(pod, opts)
|
return corevalidation.ValidatePodCreate(pod, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +142,7 @@ func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object)
|
|||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
oldPod := old.(*api.Pod)
|
oldPod := old.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
return corevalidation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
return corevalidation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,6 +227,7 @@ func (podStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
|
|||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
oldPod := old.(*api.Pod)
|
oldPod := old.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
|
|
||||||
return corevalidation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
return corevalidation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
||||||
}
|
}
|
||||||
@ -264,6 +267,7 @@ func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, o
|
|||||||
newPod := obj.(*api.Pod)
|
newPod := obj.(*api.Pod)
|
||||||
oldPod := old.(*api.Pod)
|
oldPod := old.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&newPod.Spec, &oldPod.Spec, &newPod.ObjectMeta, &oldPod.ObjectMeta)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&newPod.Spec, &oldPod.Spec, &newPod.ObjectMeta, &oldPod.ObjectMeta)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
return corevalidation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
|
return corevalidation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user