k8s.io/kubernetes@v1.29.3/pkg/apis/networking/validation/validation.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package validation
    18  
    19  import (
    20  	"fmt"
    21  	"net/netip"
    22  	"strings"
    23  
    24  	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
    25  	pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
    26  	unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    27  	"k8s.io/apimachinery/pkg/util/intstr"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  	"k8s.io/apimachinery/pkg/util/validation"
    30  	"k8s.io/apimachinery/pkg/util/validation/field"
    31  	api "k8s.io/kubernetes/pkg/apis/core"
    32  	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
    33  	"k8s.io/kubernetes/pkg/apis/networking"
    34  	netutils "k8s.io/utils/net"
    35  	utilpointer "k8s.io/utils/pointer"
    36  )
    37  
    38  const (
    39  	annotationIngressClass       = "kubernetes.io/ingress.class"
    40  	maxLenIngressClassController = 250
    41  )
    42  
    43  var (
    44  	supportedPathTypes = sets.NewString(
    45  		string(networking.PathTypeExact),
    46  		string(networking.PathTypePrefix),
    47  		string(networking.PathTypeImplementationSpecific),
    48  	)
    49  	invalidPathSequences = []string{"//", "/./", "/../", "%2f", "%2F"}
    50  	invalidPathSuffixes  = []string{"/..", "/."}
    51  
    52  	supportedIngressClassParametersReferenceScopes = sets.NewString(
    53  		networking.IngressClassParametersReferenceScopeNamespace,
    54  		networking.IngressClassParametersReferenceScopeCluster,
    55  	)
    56  )
    57  
    58  type NetworkPolicyValidationOptions struct {
    59  	AllowInvalidLabelValueInSelector bool
    60  }
    61  
    62  // ValidateNetworkPolicyName can be used to check whether the given networkpolicy
    63  // name is valid.
    64  func ValidateNetworkPolicyName(name string, prefix bool) []string {
    65  	return apimachineryvalidation.NameIsDNSSubdomain(name, prefix)
    66  }
    67  
    68  // ValidateNetworkPolicyPort validates a NetworkPolicyPort
    69  func ValidateNetworkPolicyPort(port *networking.NetworkPolicyPort, portPath *field.Path) field.ErrorList {
    70  	allErrs := field.ErrorList{}
    71  	if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP && *port.Protocol != api.ProtocolSCTP {
    72  		allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP), string(api.ProtocolSCTP)}))
    73  	}
    74  	if port.Port != nil {
    75  		if port.Port.Type == intstr.Int {
    76  			for _, msg := range validation.IsValidPortNum(int(port.Port.IntVal)) {
    77  				allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.IntVal, msg))
    78  			}
    79  			if port.EndPort != nil {
    80  				if *port.EndPort < port.Port.IntVal {
    81  					allErrs = append(allErrs, field.Invalid(portPath.Child("endPort"), port.Port.IntVal, "must be greater than or equal to `port`"))
    82  				}
    83  				for _, msg := range validation.IsValidPortNum(int(*port.EndPort)) {
    84  					allErrs = append(allErrs, field.Invalid(portPath.Child("endPort"), *port.EndPort, msg))
    85  				}
    86  			}
    87  		} else {
    88  			if port.EndPort != nil {
    89  				allErrs = append(allErrs, field.Invalid(portPath.Child("endPort"), *port.EndPort, "may not be specified when `port` is non-numeric"))
    90  			}
    91  			for _, msg := range validation.IsValidPortName(port.Port.StrVal) {
    92  				allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.StrVal, msg))
    93  			}
    94  		}
    95  	} else {
    96  		if port.EndPort != nil {
    97  			allErrs = append(allErrs, field.Invalid(portPath.Child("endPort"), *port.EndPort, "may not be specified when `port` is not specified"))
    98  		}
    99  	}
   100  
   101  	return allErrs
   102  }
   103  
   104  // ValidateNetworkPolicyPeer validates a NetworkPolicyPeer
   105  func ValidateNetworkPolicyPeer(peer *networking.NetworkPolicyPeer, opts NetworkPolicyValidationOptions, peerPath *field.Path) field.ErrorList {
   106  	allErrs := field.ErrorList{}
   107  	numPeers := 0
   108  	labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
   109  		AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
   110  	}
   111  
   112  	if peer.PodSelector != nil {
   113  		numPeers++
   114  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(peer.PodSelector, labelSelectorValidationOpts, peerPath.Child("podSelector"))...)
   115  	}
   116  	if peer.NamespaceSelector != nil {
   117  		numPeers++
   118  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(peer.NamespaceSelector, labelSelectorValidationOpts, peerPath.Child("namespaceSelector"))...)
   119  	}
   120  	if peer.IPBlock != nil {
   121  		numPeers++
   122  		allErrs = append(allErrs, ValidateIPBlock(peer.IPBlock, peerPath.Child("ipBlock"))...)
   123  	}
   124  
   125  	if numPeers == 0 {
   126  		allErrs = append(allErrs, field.Required(peerPath, "must specify a peer"))
   127  	} else if numPeers > 1 && peer.IPBlock != nil {
   128  		allErrs = append(allErrs, field.Forbidden(peerPath, "may not specify both ipBlock and another peer"))
   129  	}
   130  
   131  	return allErrs
   132  }
   133  
   134  // ValidateNetworkPolicySpec tests if required fields in the networkpolicy spec are set.
   135  func ValidateNetworkPolicySpec(spec *networking.NetworkPolicySpec, opts NetworkPolicyValidationOptions, fldPath *field.Path) field.ErrorList {
   136  	allErrs := field.ErrorList{}
   137  	labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
   138  		AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
   139  	}
   140  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(
   141  		&spec.PodSelector,
   142  		labelSelectorValidationOpts,
   143  		fldPath.Child("podSelector"),
   144  	)...)
   145  
   146  	// Validate ingress rules.
   147  	for i, ingress := range spec.Ingress {
   148  		ingressPath := fldPath.Child("ingress").Index(i)
   149  		for i, port := range ingress.Ports {
   150  			portPath := ingressPath.Child("ports").Index(i)
   151  			allErrs = append(allErrs, ValidateNetworkPolicyPort(&port, portPath)...)
   152  		}
   153  		for i, from := range ingress.From {
   154  			fromPath := ingressPath.Child("from").Index(i)
   155  			allErrs = append(allErrs, ValidateNetworkPolicyPeer(&from, opts, fromPath)...)
   156  		}
   157  	}
   158  	// Validate egress rules
   159  	for i, egress := range spec.Egress {
   160  		egressPath := fldPath.Child("egress").Index(i)
   161  		for i, port := range egress.Ports {
   162  			portPath := egressPath.Child("ports").Index(i)
   163  			allErrs = append(allErrs, ValidateNetworkPolicyPort(&port, portPath)...)
   164  		}
   165  		for i, to := range egress.To {
   166  			toPath := egressPath.Child("to").Index(i)
   167  			allErrs = append(allErrs, ValidateNetworkPolicyPeer(&to, opts, toPath)...)
   168  		}
   169  	}
   170  	// Validate PolicyTypes
   171  	allowed := sets.NewString(string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress))
   172  	if len(spec.PolicyTypes) > len(allowed) {
   173  		allErrs = append(allErrs, field.Invalid(fldPath.Child("policyTypes"), &spec.PolicyTypes, "may not specify more than two policyTypes"))
   174  		return allErrs
   175  	}
   176  	for i, pType := range spec.PolicyTypes {
   177  		policyPath := fldPath.Child("policyTypes").Index(i)
   178  		if !allowed.Has(string(pType)) {
   179  			allErrs = append(allErrs, field.NotSupported(policyPath, pType, []string{string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress)}))
   180  		}
   181  	}
   182  	return allErrs
   183  }
   184  
   185  // ValidateNetworkPolicy validates a networkpolicy.
   186  func ValidateNetworkPolicy(np *networking.NetworkPolicy, opts NetworkPolicyValidationOptions) field.ErrorList {
   187  	allErrs := apivalidation.ValidateObjectMeta(&np.ObjectMeta, true, ValidateNetworkPolicyName, field.NewPath("metadata"))
   188  	allErrs = append(allErrs, ValidateNetworkPolicySpec(&np.Spec, opts, field.NewPath("spec"))...)
   189  	return allErrs
   190  }
   191  
   192  // ValidationOptionsForNetworking generates NetworkPolicyValidationOptions for Networking
   193  func ValidationOptionsForNetworking(new, old *networking.NetworkPolicy) NetworkPolicyValidationOptions {
   194  	opts := NetworkPolicyValidationOptions{
   195  		AllowInvalidLabelValueInSelector: false,
   196  	}
   197  	if old != nil {
   198  		labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
   199  			AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
   200  		}
   201  		if len(unversionedvalidation.ValidateLabelSelector(&old.Spec.PodSelector, labelSelectorValidationOpts, nil)) > 0 {
   202  			opts.AllowInvalidLabelValueInSelector = true
   203  		}
   204  	}
   205  	return opts
   206  }
   207  
   208  // ValidateNetworkPolicyUpdate tests if an update to a NetworkPolicy is valid.
   209  func ValidateNetworkPolicyUpdate(update, old *networking.NetworkPolicy, opts NetworkPolicyValidationOptions) field.ErrorList {
   210  	allErrs := field.ErrorList{}
   211  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
   212  	allErrs = append(allErrs, ValidateNetworkPolicySpec(&update.Spec, opts, field.NewPath("spec"))...)
   213  	return allErrs
   214  }
   215  
   216  // ValidateIPBlock validates a cidr and the except fields of an IpBlock NetworkPolicyPeer
   217  func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorList {
   218  	allErrs := field.ErrorList{}
   219  	if len(ipb.CIDR) == 0 || ipb.CIDR == "" {
   220  		allErrs = append(allErrs, field.Required(fldPath.Child("cidr"), ""))
   221  		return allErrs
   222  	}
   223  	cidrIPNet, err := apivalidation.ValidateCIDR(ipb.CIDR)
   224  	if err != nil {
   225  		allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), ipb.CIDR, "not a valid CIDR"))
   226  		return allErrs
   227  	}
   228  	exceptCIDR := ipb.Except
   229  	for i, exceptIP := range exceptCIDR {
   230  		exceptPath := fldPath.Child("except").Index(i)
   231  		exceptCIDR, err := apivalidation.ValidateCIDR(exceptIP)
   232  		if err != nil {
   233  			allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "not a valid CIDR"))
   234  			return allErrs
   235  		}
   236  		cidrMaskLen, _ := cidrIPNet.Mask.Size()
   237  		exceptMaskLen, _ := exceptCIDR.Mask.Size()
   238  		if !cidrIPNet.Contains(exceptCIDR.IP) || cidrMaskLen >= exceptMaskLen {
   239  			allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "must be a strict subset of `cidr`"))
   240  		}
   241  	}
   242  	return allErrs
   243  }
   244  
   245  // ValidateIngressName validates that the given name can be used as an Ingress
   246  // name.
   247  var ValidateIngressName = apimachineryvalidation.NameIsDNSSubdomain
   248  
   249  // IngressValidationOptions cover beta to GA transitions for HTTP PathType
   250  type IngressValidationOptions struct {
   251  	// AllowInvalidSecretName indicates whether spec.tls[*].secretName values that are not valid Secret names should be allowed
   252  	AllowInvalidSecretName bool
   253  
   254  	// AllowInvalidWildcardHostRule indicates whether invalid rule values are allowed in rules with wildcard hostnames
   255  	AllowInvalidWildcardHostRule bool
   256  }
   257  
   258  // ValidateIngress validates Ingresses on create and update.
   259  func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions) field.ErrorList {
   260  	allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))
   261  	allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts)...)
   262  	return allErrs
   263  }
   264  
   265  // ValidateIngressCreate validates Ingresses on create.
   266  func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList {
   267  	allErrs := field.ErrorList{}
   268  	opts := IngressValidationOptions{
   269  		AllowInvalidSecretName:       false,
   270  		AllowInvalidWildcardHostRule: false,
   271  	}
   272  	allErrs = append(allErrs, validateIngress(ingress, opts)...)
   273  	annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass]
   274  	if annotationIsSet && ingress.Spec.IngressClassName != nil && annotationVal != *ingress.Spec.IngressClassName {
   275  		annotationPath := field.NewPath("annotations").Child(annotationIngressClass)
   276  		allErrs = append(allErrs, field.Invalid(annotationPath, annotationVal, "must match `ingressClassName` when both are specified"))
   277  	}
   278  	return allErrs
   279  }
   280  
   281  // ValidateIngressUpdate validates ingresses on update.
   282  func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
   283  	allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
   284  	opts := IngressValidationOptions{
   285  		AllowInvalidSecretName:       allowInvalidSecretName(oldIngress),
   286  		AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
   287  	}
   288  
   289  	allErrs = append(allErrs, validateIngress(ingress, opts)...)
   290  	return allErrs
   291  }
   292  
   293  func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   294  	allErrs := field.ErrorList{}
   295  	// TODO: Perform a more thorough validation of spec.TLS.Hosts that takes
   296  	// the wildcard spec from RFC 6125 into account.
   297  	for tlsIndex, itls := range spec.TLS {
   298  		for i, host := range itls.Hosts {
   299  			if strings.Contains(host, "*") {
   300  				for _, msg := range validation.IsWildcardDNS1123Subdomain(host) {
   301  					allErrs = append(allErrs, field.Invalid(fldPath.Index(tlsIndex).Child("hosts").Index(i), host, msg))
   302  				}
   303  				continue
   304  			}
   305  			for _, msg := range validation.IsDNS1123Subdomain(host) {
   306  				allErrs = append(allErrs, field.Invalid(fldPath.Index(tlsIndex).Child("hosts").Index(i), host, msg))
   307  			}
   308  		}
   309  
   310  		if !opts.AllowInvalidSecretName {
   311  			for _, msg := range validateTLSSecretName(itls.SecretName) {
   312  				allErrs = append(allErrs, field.Invalid(fldPath.Index(tlsIndex).Child("secretName"), itls.SecretName, msg))
   313  			}
   314  		}
   315  	}
   316  
   317  	return allErrs
   318  }
   319  
   320  // ValidateIngressSpec tests if required fields in the IngressSpec are set.
   321  func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   322  	allErrs := field.ErrorList{}
   323  	if len(spec.Rules) == 0 && spec.DefaultBackend == nil {
   324  		errMsg := fmt.Sprintf("either `%s` or `rules` must be specified", "defaultBackend")
   325  		allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, errMsg))
   326  	}
   327  	if spec.DefaultBackend != nil {
   328  		allErrs = append(allErrs, validateIngressBackend(spec.DefaultBackend, fldPath.Child("defaultBackend"), opts)...)
   329  	}
   330  	if len(spec.Rules) > 0 {
   331  		allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"), opts)...)
   332  	}
   333  	if len(spec.TLS) > 0 {
   334  		allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...)
   335  	}
   336  	if spec.IngressClassName != nil {
   337  		for _, msg := range ValidateIngressClassName(*spec.IngressClassName, false) {
   338  			allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressClassName"), *spec.IngressClassName, msg))
   339  		}
   340  	}
   341  	return allErrs
   342  }
   343  
   344  // ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status.
   345  func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
   346  	allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
   347  	allErrs = append(allErrs, ValidateIngressLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
   348  	return allErrs
   349  }
   350  
   351  // ValidateLIngressoadBalancerStatus validates required fields on an IngressLoadBalancerStatus
   352  func ValidateIngressLoadBalancerStatus(status *networking.IngressLoadBalancerStatus, fldPath *field.Path) field.ErrorList {
   353  	allErrs := field.ErrorList{}
   354  	for i, ingress := range status.Ingress {
   355  		idxPath := fldPath.Child("ingress").Index(i)
   356  		if len(ingress.IP) > 0 {
   357  			if isIP := (netutils.ParseIPSloppy(ingress.IP) != nil); !isIP {
   358  				allErrs = append(allErrs, field.Invalid(idxPath.Child("ip"), ingress.IP, "must be a valid IP address"))
   359  			}
   360  		}
   361  		if len(ingress.Hostname) > 0 {
   362  			for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
   363  				allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
   364  			}
   365  			if isIP := (netutils.ParseIPSloppy(ingress.Hostname) != nil); isIP {
   366  				allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
   367  			}
   368  		}
   369  	}
   370  	return allErrs
   371  }
   372  
   373  func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   374  	allErrs := field.ErrorList{}
   375  	if len(ingressRules) == 0 {
   376  		return append(allErrs, field.Required(fldPath, ""))
   377  	}
   378  	for i, ih := range ingressRules {
   379  		wildcardHost := false
   380  		if len(ih.Host) > 0 {
   381  			if isIP := (netutils.ParseIPSloppy(ih.Host) != nil); isIP {
   382  				allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address"))
   383  			}
   384  			// TODO: Ports and ips are allowed in the host part of a url
   385  			// according to RFC 3986, consider allowing them.
   386  			if strings.Contains(ih.Host, "*") {
   387  				for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) {
   388  					allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
   389  				}
   390  				wildcardHost = true
   391  			} else {
   392  				for _, msg := range validation.IsDNS1123Subdomain(ih.Host) {
   393  					allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
   394  				}
   395  			}
   396  		}
   397  
   398  		if !wildcardHost || !opts.AllowInvalidWildcardHostRule {
   399  			allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(i), opts)...)
   400  		}
   401  	}
   402  	return allErrs
   403  }
   404  
   405  func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   406  	allErrs := field.ErrorList{}
   407  	if ingressRule.HTTP != nil {
   408  		allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"), opts)...)
   409  	}
   410  	return allErrs
   411  }
   412  
   413  func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   414  	allErrs := field.ErrorList{}
   415  	if len(httpIngressRuleValue.Paths) == 0 {
   416  		allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
   417  	}
   418  	for i, path := range httpIngressRuleValue.Paths {
   419  		allErrs = append(allErrs, validateHTTPIngressPath(&path, fldPath.Child("paths").Index(i), opts)...)
   420  	}
   421  	return allErrs
   422  }
   423  
   424  func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   425  	allErrs := field.ErrorList{}
   426  
   427  	if path.PathType == nil {
   428  		return append(allErrs, field.Required(fldPath.Child("pathType"), "pathType must be specified"))
   429  	}
   430  
   431  	switch *path.PathType {
   432  	case networking.PathTypeExact, networking.PathTypePrefix:
   433  		if !strings.HasPrefix(path.Path, "/") {
   434  			allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, "must be an absolute path"))
   435  		}
   436  		if len(path.Path) > 0 {
   437  			for _, invalidSeq := range invalidPathSequences {
   438  				if strings.Contains(path.Path, invalidSeq) {
   439  					allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, fmt.Sprintf("must not contain '%s'", invalidSeq)))
   440  				}
   441  			}
   442  
   443  			for _, invalidSuff := range invalidPathSuffixes {
   444  				if strings.HasSuffix(path.Path, invalidSuff) {
   445  					allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, fmt.Sprintf("cannot end with '%s'", invalidSuff)))
   446  				}
   447  			}
   448  		}
   449  	case networking.PathTypeImplementationSpecific:
   450  		if len(path.Path) > 0 {
   451  			if !strings.HasPrefix(path.Path, "/") {
   452  				allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, "must be an absolute path"))
   453  			}
   454  		}
   455  	default:
   456  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("pathType"), *path.PathType, supportedPathTypes.List()))
   457  	}
   458  	allErrs = append(allErrs, validateIngressBackend(&path.Backend, fldPath.Child("backend"), opts)...)
   459  	return allErrs
   460  }
   461  
   462  // validateIngressBackend tests if a given backend is valid.
   463  func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
   464  	allErrs := field.ErrorList{}
   465  
   466  	hasResourceBackend := backend.Resource != nil
   467  	hasServiceBackend := backend.Service != nil
   468  
   469  	switch {
   470  	case hasResourceBackend && hasServiceBackend:
   471  		return append(allErrs, field.Invalid(fldPath, "", "cannot set both resource and service backends"))
   472  	case hasResourceBackend:
   473  		allErrs = append(allErrs, validateIngressTypedLocalObjectReference(backend.Resource, fldPath.Child("resource"))...)
   474  	case hasServiceBackend:
   475  
   476  		if len(backend.Service.Name) == 0 {
   477  			allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), ""))
   478  		} else {
   479  			for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) {
   480  				allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "name"), backend.Service.Name, msg))
   481  			}
   482  		}
   483  
   484  		hasPortName := len(backend.Service.Port.Name) > 0
   485  		hasPortNumber := backend.Service.Port.Number != 0
   486  		if hasPortName && hasPortNumber {
   487  			allErrs = append(allErrs, field.Invalid(fldPath, "", "cannot set both port name & port number"))
   488  		} else if hasPortName {
   489  			for _, msg := range validation.IsValidPortName(backend.Service.Port.Name) {
   490  				allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "name"), backend.Service.Port.Name, msg))
   491  			}
   492  		} else if hasPortNumber {
   493  			for _, msg := range validation.IsValidPortNum(int(backend.Service.Port.Number)) {
   494  				allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "number"), backend.Service.Port.Number, msg))
   495  			}
   496  		} else {
   497  			allErrs = append(allErrs, field.Required(fldPath, "port name or number is required"))
   498  		}
   499  	default:
   500  		allErrs = append(allErrs, field.Invalid(fldPath, "", "resource or service backend is required"))
   501  	}
   502  	return allErrs
   503  }
   504  
   505  // ValidateIngressClassName validates that the given name can be used as an
   506  // IngressClass name.
   507  var ValidateIngressClassName = apimachineryvalidation.NameIsDNSSubdomain
   508  
   509  // ValidateIngressClass ensures that IngressClass resources are valid.
   510  func ValidateIngressClass(ingressClass *networking.IngressClass) field.ErrorList {
   511  	allErrs := apivalidation.ValidateObjectMeta(&ingressClass.ObjectMeta, false, ValidateIngressClassName, field.NewPath("metadata"))
   512  	allErrs = append(allErrs, validateIngressClassSpec(&ingressClass.Spec, field.NewPath("spec"))...)
   513  	return allErrs
   514  }
   515  
   516  // ValidateIngressClassUpdate ensures that IngressClass updates are valid.
   517  func ValidateIngressClassUpdate(newIngressClass, oldIngressClass *networking.IngressClass) field.ErrorList {
   518  	allErrs := apivalidation.ValidateObjectMetaUpdate(&newIngressClass.ObjectMeta, &oldIngressClass.ObjectMeta, field.NewPath("metadata"))
   519  	allErrs = append(allErrs, validateIngressClassSpecUpdate(&newIngressClass.Spec, &oldIngressClass.Spec, field.NewPath("spec"))...)
   520  	allErrs = append(allErrs, ValidateIngressClass(newIngressClass)...)
   521  	return allErrs
   522  }
   523  
   524  // validateIngressClassSpec ensures that IngressClassSpec fields are valid.
   525  func validateIngressClassSpec(spec *networking.IngressClassSpec, fldPath *field.Path) field.ErrorList {
   526  	allErrs := field.ErrorList{}
   527  	if len(spec.Controller) > maxLenIngressClassController {
   528  		allErrs = append(allErrs, field.TooLong(fldPath.Child("controller"), spec.Controller, maxLenIngressClassController))
   529  	}
   530  	allErrs = append(allErrs, validation.IsDomainPrefixedPath(fldPath.Child("controller"), spec.Controller)...)
   531  	allErrs = append(allErrs, validateIngressClassParametersReference(spec.Parameters, fldPath.Child("parameters"))...)
   532  	return allErrs
   533  }
   534  
   535  // validateIngressClassSpecUpdate ensures that IngressClassSpec updates are
   536  // valid.
   537  func validateIngressClassSpecUpdate(newSpec, oldSpec *networking.IngressClassSpec, fldPath *field.Path) field.ErrorList {
   538  	return apivalidation.ValidateImmutableField(newSpec.Controller, oldSpec.Controller, fldPath.Child("controller"))
   539  }
   540  
   541  // validateIngressTypedLocalObjectReference ensures that Parameters fields are valid.
   542  func validateIngressTypedLocalObjectReference(params *api.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList {
   543  	allErrs := field.ErrorList{}
   544  
   545  	if params == nil {
   546  		return allErrs
   547  	}
   548  
   549  	if params.APIGroup != nil {
   550  		for _, msg := range validation.IsDNS1123Subdomain(*params.APIGroup) {
   551  			allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), *params.APIGroup, msg))
   552  		}
   553  	}
   554  
   555  	if params.Kind == "" {
   556  		allErrs = append(allErrs, field.Required(fldPath.Child("kind"), "kind is required"))
   557  	} else {
   558  		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Kind) {
   559  			allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), params.Kind, msg))
   560  		}
   561  	}
   562  
   563  	if params.Name == "" {
   564  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name is required"))
   565  	} else {
   566  		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) {
   567  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
   568  		}
   569  	}
   570  
   571  	return allErrs
   572  }
   573  
   574  // validateIngressClassParametersReference ensures that Parameters fields are valid.
   575  // Parameters was previously of type `TypedLocalObjectReference` and used
   576  // `validateIngressTypedLocalObjectReference()`. This function extends validation
   577  // for additional fields introduced for namespace-scoped references.
   578  func validateIngressClassParametersReference(params *networking.IngressClassParametersReference, fldPath *field.Path) field.ErrorList {
   579  	allErrs := field.ErrorList{}
   580  
   581  	if params == nil {
   582  		return allErrs
   583  	}
   584  
   585  	allErrs = append(allErrs, validateIngressTypedLocalObjectReference(&api.TypedLocalObjectReference{
   586  		APIGroup: params.APIGroup,
   587  		Kind:     params.Kind,
   588  		Name:     params.Name,
   589  	}, fldPath)...)
   590  
   591  	if params.Scope == nil {
   592  		allErrs = append(allErrs, field.Required(fldPath.Child("scope"), ""))
   593  		return allErrs
   594  	}
   595  
   596  	scope := utilpointer.StringDeref(params.Scope, "")
   597  
   598  	if !supportedIngressClassParametersReferenceScopes.Has(scope) {
   599  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("scope"), scope,
   600  			supportedIngressClassParametersReferenceScopes.List()))
   601  	} else {
   602  		if scope == networking.IngressClassParametersReferenceScopeNamespace {
   603  			if params.Namespace == nil {
   604  				allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "`parameters.scope` is set to 'Namespace'"))
   605  			} else {
   606  				for _, msg := range apivalidation.ValidateNamespaceName(*params.Namespace, false) {
   607  					allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), *params.Namespace, msg))
   608  				}
   609  			}
   610  		}
   611  
   612  		if scope == networking.IngressClassParametersReferenceScopeCluster && params.Namespace != nil {
   613  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "`parameters.scope` is set to 'Cluster'"))
   614  		}
   615  	}
   616  
   617  	return allErrs
   618  }
   619  
   620  func allowInvalidSecretName(oldIngress *networking.Ingress) bool {
   621  	if oldIngress != nil {
   622  		for _, tls := range oldIngress.Spec.TLS {
   623  			if len(validateTLSSecretName(tls.SecretName)) > 0 {
   624  				// backwards compatibility with existing persisted object
   625  				return true
   626  			}
   627  		}
   628  	}
   629  	return false
   630  }
   631  
   632  func validateTLSSecretName(name string) []string {
   633  	if len(name) == 0 {
   634  		return nil
   635  	}
   636  	return apivalidation.ValidateSecretName(name, false)
   637  }
   638  
   639  func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
   640  	if oldIngress != nil {
   641  		for _, rule := range oldIngress.Spec.Rules {
   642  			if strings.Contains(rule.Host, "*") && len(validateIngressRuleValue(&rule.IngressRuleValue, nil, IngressValidationOptions{})) > 0 {
   643  				// backwards compatibility with existing invalid data
   644  				return true
   645  			}
   646  		}
   647  	}
   648  	return false
   649  }
   650  
   651  // ValidateIPAddressName validates that the name is the decimal representation of an IP address.
   652  // IPAddress does not support generating names, prefix is not considered.
   653  func ValidateIPAddressName(name string, prefix bool) []string {
   654  	var errs []string
   655  	ip, err := netip.ParseAddr(name)
   656  	if err != nil {
   657  		errs = append(errs, err.Error())
   658  	} else if ip.String() != name {
   659  		errs = append(errs, "must be a canonical format IP address")
   660  
   661  	}
   662  	return errs
   663  }
   664  
   665  func ValidateIPAddress(ipAddress *networking.IPAddress) field.ErrorList {
   666  	allErrs := apivalidation.ValidateObjectMeta(&ipAddress.ObjectMeta, false, ValidateIPAddressName, field.NewPath("metadata"))
   667  	errs := validateIPAddressParentReference(ipAddress.Spec.ParentRef, field.NewPath("spec"))
   668  	allErrs = append(allErrs, errs...)
   669  	return allErrs
   670  
   671  }
   672  
   673  // validateIPAddressParentReference ensures that the IPAddress ParenteReference exists and is valid.
   674  func validateIPAddressParentReference(params *networking.ParentReference, fldPath *field.Path) field.ErrorList {
   675  	allErrs := field.ErrorList{}
   676  
   677  	if params == nil {
   678  		allErrs = append(allErrs, field.Required(fldPath.Child("parentRef"), ""))
   679  		return allErrs
   680  	}
   681  
   682  	fldPath = fldPath.Child("parentRef")
   683  	// group is required but the Core group used by Services is the empty value, so it can not be enforced
   684  	if params.Group != "" {
   685  		for _, msg := range validation.IsDNS1123Subdomain(params.Group) {
   686  			allErrs = append(allErrs, field.Invalid(fldPath.Child("group"), params.Group, msg))
   687  		}
   688  	}
   689  
   690  	// resource is required
   691  	if params.Resource == "" {
   692  		allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
   693  	} else {
   694  		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Resource) {
   695  			allErrs = append(allErrs, field.Invalid(fldPath.Child("resource"), params.Resource, msg))
   696  		}
   697  	}
   698  
   699  	// name is required
   700  	if params.Name == "" {
   701  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
   702  	} else {
   703  		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) {
   704  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
   705  		}
   706  	}
   707  
   708  	// namespace is optional
   709  	if params.Namespace != "" {
   710  		for _, msg := range pathvalidation.IsValidPathSegmentName(params.Namespace) {
   711  			allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), params.Namespace, msg))
   712  		}
   713  	}
   714  	return allErrs
   715  }
   716  
   717  // ValidateIPAddressUpdate tests if an update to an IPAddress is valid.
   718  func ValidateIPAddressUpdate(update, old *networking.IPAddress) field.ErrorList {
   719  	var allErrs field.ErrorList
   720  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
   721  	allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.Spec.ParentRef, old.Spec.ParentRef, field.NewPath("spec").Child("parentRef"))...)
   722  	return allErrs
   723  }
   724  
   725  var ValidateServiceCIDRName = apimachineryvalidation.NameIsDNSSubdomain
   726  
   727  func ValidateServiceCIDR(cidrConfig *networking.ServiceCIDR) field.ErrorList {
   728  	allErrs := apivalidation.ValidateObjectMeta(&cidrConfig.ObjectMeta, false, ValidateServiceCIDRName, field.NewPath("metadata"))
   729  	fieldPath := field.NewPath("spec", "cidrs")
   730  
   731  	if len(cidrConfig.Spec.CIDRs) == 0 {
   732  		allErrs = append(allErrs, field.Required(fieldPath, "at least one CIDR required"))
   733  		return allErrs
   734  	}
   735  
   736  	if len(cidrConfig.Spec.CIDRs) > 2 {
   737  		allErrs = append(allErrs, field.Invalid(fieldPath, cidrConfig.Spec, "may only hold up to 2 values"))
   738  		return allErrs
   739  	}
   740  	// validate cidrs are dual stack, one of each IP family
   741  	if len(cidrConfig.Spec.CIDRs) == 2 {
   742  		isDual, err := netutils.IsDualStackCIDRStrings(cidrConfig.Spec.CIDRs)
   743  		if err != nil || !isDual {
   744  			allErrs = append(allErrs, field.Invalid(fieldPath, cidrConfig.Spec, "may specify no more than one IP for each IP family, i.e 192.168.0.0/24 and 2001:db8::/64"))
   745  			return allErrs
   746  		}
   747  	}
   748  
   749  	for i, cidr := range cidrConfig.Spec.CIDRs {
   750  		allErrs = append(allErrs, validateCIDR(cidr, fieldPath.Index(i))...)
   751  	}
   752  
   753  	return allErrs
   754  }
   755  
   756  func validateCIDR(cidr string, fldPath *field.Path) field.ErrorList {
   757  	allErrs := field.ErrorList{}
   758  	prefix, err := netip.ParsePrefix(cidr)
   759  	if err != nil {
   760  		allErrs = append(allErrs, field.Invalid(fldPath, cidr, err.Error()))
   761  	} else {
   762  		if prefix.Addr() != prefix.Masked().Addr() {
   763  			allErrs = append(allErrs, field.Invalid(fldPath, cidr, "wrong CIDR format, IP doesn't match network IP address"))
   764  		}
   765  		if prefix.String() != cidr {
   766  			allErrs = append(allErrs, field.Invalid(fldPath, cidr, "CIDR not in RFC 5952 canonical format"))
   767  		}
   768  	}
   769  	return allErrs
   770  }
   771  
   772  // ValidateServiceCIDRUpdate tests if an update to a ServiceCIDR is valid.
   773  func ValidateServiceCIDRUpdate(update, old *networking.ServiceCIDR) field.ErrorList {
   774  	var allErrs field.ErrorList
   775  	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
   776  	allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.Spec.CIDRs, old.Spec.CIDRs, field.NewPath("spec").Child("cidrs"))...)
   777  
   778  	return allErrs
   779  }
   780  
   781  // ValidateServiceCIDRStatusUpdate tests if if an update to a ServiceCIDR Status is valid.
   782  func ValidateServiceCIDRStatusUpdate(update, old *networking.ServiceCIDR) field.ErrorList {
   783  	allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
   784  	return allErrs
   785  }