1
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 "k8s.io/utils/ptr"
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
63
64 func ValidateNetworkPolicyName(name string, prefix bool) []string {
65 return apimachineryvalidation.NameIsDNSSubdomain(name, prefix)
66 }
67
68
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
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
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
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
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
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
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
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
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
217 func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorList {
218 allErrs := field.ErrorList{}
219 if ipb.CIDR == "" {
220 allErrs = append(allErrs, field.Required(fldPath.Child("cidr"), ""))
221 return allErrs
222 }
223 allErrs = append(allErrs, validation.IsValidCIDR(fldPath.Child("cidr"), ipb.CIDR)...)
224 _, cidrIPNet, err := netutils.ParseCIDRSloppy(ipb.CIDR)
225 if err != nil {
226
227 return allErrs
228 }
229
230 for i, exceptCIDRStr := range ipb.Except {
231 exceptPath := fldPath.Child("except").Index(i)
232 allErrs = append(allErrs, validation.IsValidCIDR(exceptPath, exceptCIDRStr)...)
233 _, exceptCIDR, err := netutils.ParseCIDRSloppy(exceptCIDRStr)
234 if err != nil {
235
236 continue
237 }
238
239 cidrMaskLen, _ := cidrIPNet.Mask.Size()
240 exceptMaskLen, _ := exceptCIDR.Mask.Size()
241 if !cidrIPNet.Contains(exceptCIDR.IP) || cidrMaskLen >= exceptMaskLen {
242 allErrs = append(allErrs, field.Invalid(exceptPath, exceptCIDRStr, "must be a strict subset of `cidr`"))
243 }
244 }
245 return allErrs
246 }
247
248
249
250 var ValidateIngressName = apimachineryvalidation.NameIsDNSSubdomain
251
252
253 type IngressValidationOptions struct {
254
255 AllowInvalidSecretName bool
256
257
258 AllowInvalidWildcardHostRule bool
259 }
260
261
262 func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions) field.ErrorList {
263 allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))
264 allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts)...)
265 return allErrs
266 }
267
268
269 func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList {
270 allErrs := field.ErrorList{}
271 opts := IngressValidationOptions{
272 AllowInvalidSecretName: false,
273 AllowInvalidWildcardHostRule: false,
274 }
275 allErrs = append(allErrs, validateIngress(ingress, opts)...)
276 annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass]
277 if annotationIsSet && ingress.Spec.IngressClassName != nil && annotationVal != *ingress.Spec.IngressClassName {
278 annotationPath := field.NewPath("annotations").Child(annotationIngressClass)
279 allErrs = append(allErrs, field.Invalid(annotationPath, annotationVal, "must match `ingressClassName` when both are specified"))
280 }
281 return allErrs
282 }
283
284
285 func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
286 allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
287 opts := IngressValidationOptions{
288 AllowInvalidSecretName: allowInvalidSecretName(oldIngress),
289 AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
290 }
291
292 allErrs = append(allErrs, validateIngress(ingress, opts)...)
293 return allErrs
294 }
295
296 func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
297 allErrs := field.ErrorList{}
298
299
300 for tlsIndex, itls := range spec.TLS {
301 for i, host := range itls.Hosts {
302 if strings.Contains(host, "*") {
303 for _, msg := range validation.IsWildcardDNS1123Subdomain(host) {
304 allErrs = append(allErrs, field.Invalid(fldPath.Index(tlsIndex).Child("hosts").Index(i), host, msg))
305 }
306 continue
307 }
308 for _, msg := range validation.IsDNS1123Subdomain(host) {
309 allErrs = append(allErrs, field.Invalid(fldPath.Index(tlsIndex).Child("hosts").Index(i), host, msg))
310 }
311 }
312
313 if !opts.AllowInvalidSecretName {
314 for _, msg := range validateTLSSecretName(itls.SecretName) {
315 allErrs = append(allErrs, field.Invalid(fldPath.Index(tlsIndex).Child("secretName"), itls.SecretName, msg))
316 }
317 }
318 }
319
320 return allErrs
321 }
322
323
324 func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
325 allErrs := field.ErrorList{}
326 if len(spec.Rules) == 0 && spec.DefaultBackend == nil {
327 errMsg := fmt.Sprintf("either `%s` or `rules` must be specified", "defaultBackend")
328 allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, errMsg))
329 }
330 if spec.DefaultBackend != nil {
331 allErrs = append(allErrs, validateIngressBackend(spec.DefaultBackend, fldPath.Child("defaultBackend"), opts)...)
332 }
333 if len(spec.Rules) > 0 {
334 allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"), opts)...)
335 }
336 if len(spec.TLS) > 0 {
337 allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...)
338 }
339 if spec.IngressClassName != nil {
340 for _, msg := range ValidateIngressClassName(*spec.IngressClassName, false) {
341 allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressClassName"), *spec.IngressClassName, msg))
342 }
343 }
344 return allErrs
345 }
346
347
348 func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
349 allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
350 allErrs = append(allErrs, ValidateIngressLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
351 return allErrs
352 }
353
354
355 func ValidateIngressLoadBalancerStatus(status *networking.IngressLoadBalancerStatus, fldPath *field.Path) field.ErrorList {
356 allErrs := field.ErrorList{}
357 for i, ingress := range status.Ingress {
358 idxPath := fldPath.Child("ingress").Index(i)
359 if len(ingress.IP) > 0 {
360 allErrs = append(allErrs, validation.IsValidIP(idxPath.Child("ip"), ingress.IP)...)
361 }
362 if len(ingress.Hostname) > 0 {
363 for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
364 allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
365 }
366 if isIP := (netutils.ParseIPSloppy(ingress.Hostname) != nil); isIP {
367 allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
368 }
369 }
370 }
371 return allErrs
372 }
373
374 func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
375 allErrs := field.ErrorList{}
376 if len(ingressRules) == 0 {
377 return append(allErrs, field.Required(fldPath, ""))
378 }
379 for i, ih := range ingressRules {
380 wildcardHost := false
381 if len(ih.Host) > 0 {
382 if isIP := (netutils.ParseIPSloppy(ih.Host) != nil); isIP {
383 allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address"))
384 }
385
386
387 if strings.Contains(ih.Host, "*") {
388 for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) {
389 allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
390 }
391 wildcardHost = true
392 } else {
393 for _, msg := range validation.IsDNS1123Subdomain(ih.Host) {
394 allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
395 }
396 }
397 }
398
399 if !wildcardHost || !opts.AllowInvalidWildcardHostRule {
400 allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(i), opts)...)
401 }
402 }
403 return allErrs
404 }
405
406 func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
407 allErrs := field.ErrorList{}
408 if ingressRule.HTTP != nil {
409 allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"), opts)...)
410 }
411 return allErrs
412 }
413
414 func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
415 allErrs := field.ErrorList{}
416 if len(httpIngressRuleValue.Paths) == 0 {
417 allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
418 }
419 for i, path := range httpIngressRuleValue.Paths {
420 allErrs = append(allErrs, validateHTTPIngressPath(&path, fldPath.Child("paths").Index(i), opts)...)
421 }
422 return allErrs
423 }
424
425 func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
426 allErrs := field.ErrorList{}
427
428 if path.PathType == nil {
429 return append(allErrs, field.Required(fldPath.Child("pathType"), "pathType must be specified"))
430 }
431
432 switch *path.PathType {
433 case networking.PathTypeExact, networking.PathTypePrefix:
434 if !strings.HasPrefix(path.Path, "/") {
435 allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, "must be an absolute path"))
436 }
437 if len(path.Path) > 0 {
438 for _, invalidSeq := range invalidPathSequences {
439 if strings.Contains(path.Path, invalidSeq) {
440 allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, fmt.Sprintf("must not contain '%s'", invalidSeq)))
441 }
442 }
443
444 for _, invalidSuff := range invalidPathSuffixes {
445 if strings.HasSuffix(path.Path, invalidSuff) {
446 allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, fmt.Sprintf("cannot end with '%s'", invalidSuff)))
447 }
448 }
449 }
450 case networking.PathTypeImplementationSpecific:
451 if len(path.Path) > 0 {
452 if !strings.HasPrefix(path.Path, "/") {
453 allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), path.Path, "must be an absolute path"))
454 }
455 }
456 default:
457 allErrs = append(allErrs, field.NotSupported(fldPath.Child("pathType"), *path.PathType, supportedPathTypes.List()))
458 }
459 allErrs = append(allErrs, validateIngressBackend(&path.Backend, fldPath.Child("backend"), opts)...)
460 return allErrs
461 }
462
463
464 func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
465 allErrs := field.ErrorList{}
466
467 hasResourceBackend := backend.Resource != nil
468 hasServiceBackend := backend.Service != nil
469
470 switch {
471 case hasResourceBackend && hasServiceBackend:
472 return append(allErrs, field.Invalid(fldPath, "", "cannot set both resource and service backends"))
473 case hasResourceBackend:
474 allErrs = append(allErrs, validateIngressTypedLocalObjectReference(backend.Resource, fldPath.Child("resource"))...)
475 case hasServiceBackend:
476
477 if len(backend.Service.Name) == 0 {
478 allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), ""))
479 } else {
480 for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) {
481 allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "name"), backend.Service.Name, msg))
482 }
483 }
484
485 hasPortName := len(backend.Service.Port.Name) > 0
486 hasPortNumber := backend.Service.Port.Number != 0
487 if hasPortName && hasPortNumber {
488 allErrs = append(allErrs, field.Invalid(fldPath, "", "cannot set both port name & port number"))
489 } else if hasPortName {
490 for _, msg := range validation.IsValidPortName(backend.Service.Port.Name) {
491 allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "name"), backend.Service.Port.Name, msg))
492 }
493 } else if hasPortNumber {
494 for _, msg := range validation.IsValidPortNum(int(backend.Service.Port.Number)) {
495 allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "number"), backend.Service.Port.Number, msg))
496 }
497 } else {
498 allErrs = append(allErrs, field.Required(fldPath, "port name or number is required"))
499 }
500 default:
501 allErrs = append(allErrs, field.Invalid(fldPath, "", "resource or service backend is required"))
502 }
503 return allErrs
504 }
505
506
507
508 var ValidateIngressClassName = apimachineryvalidation.NameIsDNSSubdomain
509
510
511 func ValidateIngressClass(ingressClass *networking.IngressClass) field.ErrorList {
512 allErrs := apivalidation.ValidateObjectMeta(&ingressClass.ObjectMeta, false, ValidateIngressClassName, field.NewPath("metadata"))
513 allErrs = append(allErrs, validateIngressClassSpec(&ingressClass.Spec, field.NewPath("spec"))...)
514 return allErrs
515 }
516
517
518 func ValidateIngressClassUpdate(newIngressClass, oldIngressClass *networking.IngressClass) field.ErrorList {
519 allErrs := apivalidation.ValidateObjectMetaUpdate(&newIngressClass.ObjectMeta, &oldIngressClass.ObjectMeta, field.NewPath("metadata"))
520 allErrs = append(allErrs, validateIngressClassSpecUpdate(&newIngressClass.Spec, &oldIngressClass.Spec, field.NewPath("spec"))...)
521 allErrs = append(allErrs, ValidateIngressClass(newIngressClass)...)
522 return allErrs
523 }
524
525
526 func validateIngressClassSpec(spec *networking.IngressClassSpec, fldPath *field.Path) field.ErrorList {
527 allErrs := field.ErrorList{}
528 if len(spec.Controller) > maxLenIngressClassController {
529 allErrs = append(allErrs, field.TooLong(fldPath.Child("controller"), spec.Controller, maxLenIngressClassController))
530 }
531 allErrs = append(allErrs, validation.IsDomainPrefixedPath(fldPath.Child("controller"), spec.Controller)...)
532 allErrs = append(allErrs, validateIngressClassParametersReference(spec.Parameters, fldPath.Child("parameters"))...)
533 return allErrs
534 }
535
536
537
538 func validateIngressClassSpecUpdate(newSpec, oldSpec *networking.IngressClassSpec, fldPath *field.Path) field.ErrorList {
539 return apivalidation.ValidateImmutableField(newSpec.Controller, oldSpec.Controller, fldPath.Child("controller"))
540 }
541
542
543 func validateIngressTypedLocalObjectReference(params *api.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList {
544 allErrs := field.ErrorList{}
545
546 if params == nil {
547 return allErrs
548 }
549
550 if params.APIGroup != nil {
551 for _, msg := range validation.IsDNS1123Subdomain(*params.APIGroup) {
552 allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), *params.APIGroup, msg))
553 }
554 }
555
556 if params.Kind == "" {
557 allErrs = append(allErrs, field.Required(fldPath.Child("kind"), "kind is required"))
558 } else {
559 for _, msg := range pathvalidation.IsValidPathSegmentName(params.Kind) {
560 allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), params.Kind, msg))
561 }
562 }
563
564 if params.Name == "" {
565 allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name is required"))
566 } else {
567 for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) {
568 allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
569 }
570 }
571
572 return allErrs
573 }
574
575
576
577
578
579 func validateIngressClassParametersReference(params *networking.IngressClassParametersReference, fldPath *field.Path) field.ErrorList {
580 allErrs := field.ErrorList{}
581
582 if params == nil {
583 return allErrs
584 }
585
586 allErrs = append(allErrs, validateIngressTypedLocalObjectReference(&api.TypedLocalObjectReference{
587 APIGroup: params.APIGroup,
588 Kind: params.Kind,
589 Name: params.Name,
590 }, fldPath)...)
591
592 if params.Scope == nil {
593 allErrs = append(allErrs, field.Required(fldPath.Child("scope"), ""))
594 return allErrs
595 }
596
597 scope := ptr.Deref(params.Scope, "")
598
599 if !supportedIngressClassParametersReferenceScopes.Has(scope) {
600 allErrs = append(allErrs, field.NotSupported(fldPath.Child("scope"), scope,
601 supportedIngressClassParametersReferenceScopes.List()))
602 } else {
603 if scope == networking.IngressClassParametersReferenceScopeNamespace {
604 if params.Namespace == nil {
605 allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "`parameters.scope` is set to 'Namespace'"))
606 } else {
607 for _, msg := range apivalidation.ValidateNamespaceName(*params.Namespace, false) {
608 allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), *params.Namespace, msg))
609 }
610 }
611 }
612
613 if scope == networking.IngressClassParametersReferenceScopeCluster && params.Namespace != nil {
614 allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "`parameters.scope` is set to 'Cluster'"))
615 }
616 }
617
618 return allErrs
619 }
620
621 func allowInvalidSecretName(oldIngress *networking.Ingress) bool {
622 if oldIngress != nil {
623 for _, tls := range oldIngress.Spec.TLS {
624 if len(validateTLSSecretName(tls.SecretName)) > 0 {
625
626 return true
627 }
628 }
629 }
630 return false
631 }
632
633 func validateTLSSecretName(name string) []string {
634 if len(name) == 0 {
635 return nil
636 }
637 return apivalidation.ValidateSecretName(name, false)
638 }
639
640 func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
641 if oldIngress != nil {
642 for _, rule := range oldIngress.Spec.Rules {
643 if strings.Contains(rule.Host, "*") && len(validateIngressRuleValue(&rule.IngressRuleValue, nil, IngressValidationOptions{})) > 0 {
644
645 return true
646 }
647 }
648 }
649 return false
650 }
651
652
653
654 func ValidateIPAddressName(name string, prefix bool) []string {
655 var errs []string
656 ip, err := netip.ParseAddr(name)
657 if err != nil {
658 errs = append(errs, err.Error())
659 } else if ip.String() != name {
660 errs = append(errs, "must be a canonical format IP address")
661
662 }
663 return errs
664 }
665
666 func ValidateIPAddress(ipAddress *networking.IPAddress) field.ErrorList {
667 allErrs := apivalidation.ValidateObjectMeta(&ipAddress.ObjectMeta, false, ValidateIPAddressName, field.NewPath("metadata"))
668 errs := validateIPAddressParentReference(ipAddress.Spec.ParentRef, field.NewPath("spec"))
669 allErrs = append(allErrs, errs...)
670 return allErrs
671
672 }
673
674
675 func validateIPAddressParentReference(params *networking.ParentReference, fldPath *field.Path) field.ErrorList {
676 allErrs := field.ErrorList{}
677
678 if params == nil {
679 allErrs = append(allErrs, field.Required(fldPath.Child("parentRef"), ""))
680 return allErrs
681 }
682
683 fldPath = fldPath.Child("parentRef")
684
685 if params.Group != "" {
686 for _, msg := range validation.IsDNS1123Subdomain(params.Group) {
687 allErrs = append(allErrs, field.Invalid(fldPath.Child("group"), params.Group, msg))
688 }
689 }
690
691
692 if params.Resource == "" {
693 allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
694 } else {
695 for _, msg := range pathvalidation.IsValidPathSegmentName(params.Resource) {
696 allErrs = append(allErrs, field.Invalid(fldPath.Child("resource"), params.Resource, msg))
697 }
698 }
699
700
701 if params.Name == "" {
702 allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
703 } else {
704 for _, msg := range pathvalidation.IsValidPathSegmentName(params.Name) {
705 allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
706 }
707 }
708
709
710 if params.Namespace != "" {
711 for _, msg := range pathvalidation.IsValidPathSegmentName(params.Namespace) {
712 allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), params.Namespace, msg))
713 }
714 }
715 return allErrs
716 }
717
718
719 func ValidateIPAddressUpdate(update, old *networking.IPAddress) field.ErrorList {
720 var allErrs field.ErrorList
721 allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
722 allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.Spec.ParentRef, old.Spec.ParentRef, field.NewPath("spec").Child("parentRef"))...)
723 return allErrs
724 }
725
726 var ValidateServiceCIDRName = apimachineryvalidation.NameIsDNSSubdomain
727
728 func ValidateServiceCIDR(cidrConfig *networking.ServiceCIDR) field.ErrorList {
729 allErrs := apivalidation.ValidateObjectMeta(&cidrConfig.ObjectMeta, false, ValidateServiceCIDRName, field.NewPath("metadata"))
730 fieldPath := field.NewPath("spec", "cidrs")
731
732 if len(cidrConfig.Spec.CIDRs) == 0 {
733 allErrs = append(allErrs, field.Required(fieldPath, "at least one CIDR required"))
734 return allErrs
735 }
736
737 if len(cidrConfig.Spec.CIDRs) > 2 {
738 allErrs = append(allErrs, field.Invalid(fieldPath, cidrConfig.Spec, "may only hold up to 2 values"))
739 return allErrs
740 }
741
742 if len(cidrConfig.Spec.CIDRs) == 2 {
743 isDual, err := netutils.IsDualStackCIDRStrings(cidrConfig.Spec.CIDRs)
744 if err != nil || !isDual {
745 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"))
746 return allErrs
747 }
748 }
749
750 for i, cidr := range cidrConfig.Spec.CIDRs {
751 allErrs = append(allErrs, validateCIDR(cidr, fieldPath.Index(i))...)
752 }
753
754 return allErrs
755 }
756
757 func validateCIDR(cidr string, fldPath *field.Path) field.ErrorList {
758 allErrs := field.ErrorList{}
759 prefix, err := netip.ParsePrefix(cidr)
760 if err != nil {
761 allErrs = append(allErrs, field.Invalid(fldPath, cidr, err.Error()))
762 } else {
763 if prefix.Addr() != prefix.Masked().Addr() {
764 allErrs = append(allErrs, field.Invalid(fldPath, cidr, "wrong CIDR format, IP doesn't match network IP address"))
765 }
766 if prefix.String() != cidr {
767 allErrs = append(allErrs, field.Invalid(fldPath, cidr, "CIDR not in RFC 5952 canonical format"))
768 }
769 }
770 return allErrs
771 }
772
773
774 func ValidateServiceCIDRUpdate(update, old *networking.ServiceCIDR) field.ErrorList {
775 var allErrs field.ErrorList
776 allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
777 allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.Spec.CIDRs, old.Spec.CIDRs, field.NewPath("spec").Child("cidrs"))...)
778
779 return allErrs
780 }
781
782
783 func ValidateServiceCIDRStatusUpdate(update, old *networking.ServiceCIDR) field.ErrorList {
784 allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
785 return allErrs
786 }
787
View as plain text