Allow Ingress service refs to be validated with apimachineryvalidation.NameIsDNSLabel

Only validate when feature gate RelaxedServiceNameValidation is enabled
or when the Ingess resource contains a service ref that already
validates with apimachineryvalidation.NameIsDNSLabel
This commit is contained in:
Adrian Moisey 2025-06-14 20:55:28 +02:00
parent 487eb8a9e4
commit b430159c86
No known key found for this signature in database
GPG key ID: 41AE4AE32747C7CF

View file

@ -27,9 +27,11 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/features"
netutils "k8s.io/utils/net"
"k8s.io/utils/ptr"
)
@ -283,6 +285,9 @@ type IngressValidationOptions struct {
// AllowInvalidWildcardHostRule indicates whether invalid rule values are allowed in rules with wildcard hostnames
AllowInvalidWildcardHostRule bool
// AllowRelaxedServiceNameValidation indicates if the backend service name can be validated with apimachineryvalidation.NameIsDNSLabel
AllowRelaxedServiceNameValidation bool
}
// ValidateIngress validates Ingresses on create and update.
@ -296,8 +301,9 @@ func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions)
func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList {
allErrs := field.ErrorList{}
opts := IngressValidationOptions{
AllowInvalidSecretName: false,
AllowInvalidWildcardHostRule: false,
AllowInvalidSecretName: false,
AllowInvalidWildcardHostRule: false,
AllowRelaxedServiceNameValidation: allowRelaxedServiceNameValidation(nil),
}
allErrs = append(allErrs, validateIngress(ingress, opts)...)
annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass]
@ -312,8 +318,9 @@ func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList {
func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(oldIngress),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
AllowInvalidSecretName: allowInvalidSecretName(oldIngress),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
AllowRelaxedServiceNameValidation: allowRelaxedServiceNameValidation(oldIngress),
}
allErrs = append(allErrs, validateIngress(ingress, opts)...)
@ -513,7 +520,13 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P
if len(backend.Service.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), ""))
} else {
for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) {
validationFunc := apivalidation.ValidateServiceName
if opts.AllowRelaxedServiceNameValidation {
validationFunc = apimachineryvalidation.NameIsDNSLabel
}
for _, msg := range validationFunc(backend.Service.Name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "name"), backend.Service.Name, msg))
}
}
@ -685,6 +698,37 @@ func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
return false
}
func allowRelaxedServiceNameValidation(oldIngress *networking.Ingress) bool {
// Early exit if the feature gate is enabled, as it allows relaxed validation
if utilfeature.DefaultFeatureGate.Enabled(features.RelaxedServiceNameValidation) {
return true
}
// Early exit if no old Ingress is provided
if oldIngress == nil {
return false
}
// If feature gate is disabled, check if any service names in the old Ingresss
for _, rule := range oldIngress.Spec.Rules {
if rule.HTTP == nil {
continue
}
for _, path := range rule.HTTP.Paths {
if path.Backend.Service == nil {
continue
}
serviceName := path.Backend.Service.Name
// If a name doesn't validate with apimachineryvalidation.NameIsDNS1035Label, but does validate with apimachineryvalidation.NameIsDNSLabel,
// then we allow it to be used as a Service name in an Ingress.
dnsLabelValidationErrors := apimachineryvalidation.NameIsDNSLabel(serviceName, false)
dns1035LabelValidationErrors := apimachineryvalidation.NameIsDNS1035Label(serviceName, false)
if len(dnsLabelValidationErrors) == 0 && len(dns1035LabelValidationErrors) > 0 {
return true
}
}
}
return false
}
// ValidateIPAddressName validates that the name is the decimal representation of an IP address.
// IPAddress does not support generating names, prefix is not considered.
func ValidateIPAddressName(name string, prefix bool) []string {