@@ -28,7 +28,6 @@ import (
2828 "unicode/utf8"
2929
3030 celgo "github.com/google/cel-go/cel"
31-
3231 "k8s.io/apiextensions-apiserver/pkg/apihelpers"
3332 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
3433 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -94,6 +93,8 @@ func ValidateCustomResourceDefinition(ctx context.Context, obj *apiextensions.Cu
9493 requireMapListKeysMapSetValidation : true ,
9594 // strictCost is always true to enforce cost limits.
9695 celEnvironmentSet : environment .MustBaseEnvSet (environment .DefaultCompatibilityVersion (), true ),
96+ // allowInvalidCABundle is set to true since the CRD is not established yet.
97+ allowInvalidCABundle : true ,
9798 }
9899
99100 allErrs := genericvalidation .ValidateObjectMeta (& obj .ObjectMeta , false , nameValidationFn , field .NewPath ("metadata" ))
@@ -140,6 +141,9 @@ type validationOptions struct {
140141 suppressPerExpressionCost bool
141142
142143 celEnvironmentSet * environment.EnvSet
144+ // allowInvalidCABundle allows an invalid conversion webhook CABundle on update only if the existing CABundle is invalid.
145+ // An invalid CABundle is also permitted on create and before a CRD is in an Established=True condition.
146+ allowInvalidCABundle bool
143147}
144148
145149type preexistingExpressions struct {
@@ -233,7 +237,8 @@ func ValidateCustomResourceDefinitionUpdate(ctx context.Context, obj, oldObj *ap
233237 preexistingExpressions : findPreexistingExpressions (& oldObj .Spec ),
234238 versionsWithUnchangedSchemas : findVersionsWithUnchangedSchemas (obj , oldObj ),
235239 // strictCost is always true to enforce cost limits.
236- celEnvironmentSet : environment .MustBaseEnvSet (environment .DefaultCompatibilityVersion (), true ),
240+ celEnvironmentSet : environment .MustBaseEnvSet (environment .DefaultCompatibilityVersion (), true ),
241+ allowInvalidCABundle : allowInvalidCABundle (oldObj ),
237242 }
238243 return validateCustomResourceDefinitionUpdate (ctx , obj , oldObj , opts )
239244}
@@ -485,7 +490,7 @@ func validateCustomResourceDefinitionSpec(ctx context.Context, spec *apiextensio
485490 if (spec .Conversion != nil && spec .Conversion .Strategy != apiextensions .NoneConverter ) && (spec .PreserveUnknownFields == nil || * spec .PreserveUnknownFields ) {
486491 allErrs = append (allErrs , field .Invalid (fldPath .Child ("conversion" ).Child ("strategy" ), spec .Conversion .Strategy , "must be None if spec.preserveUnknownFields is true" ))
487492 }
488- allErrs = append (allErrs , validateCustomResourceConversion (spec .Conversion , opts .requireRecognizedConversionReviewVersion , fldPath .Child ("conversion" ))... )
493+ allErrs = append (allErrs , validateCustomResourceConversion (spec .Conversion , opts .requireRecognizedConversionReviewVersion , fldPath .Child ("conversion" ), opts )... )
489494
490495 return allErrs
491496}
@@ -545,6 +550,20 @@ func validateConversionReviewVersions(versions []string, requireRecognizedVersio
545550 return allErrs
546551}
547552
553+ // Allows invalid CA Bundle to be specified only if the existing CABundle is invalid
554+ // or if the CRD is not established yet.
555+ func allowInvalidCABundle (oldCRD * apiextensions.CustomResourceDefinition ) bool {
556+ if ! apiextensions .IsCRDConditionTrue (oldCRD , apiextensions .Established ) {
557+ return true
558+ }
559+ oldConversion := oldCRD .Spec .Conversion
560+ if oldConversion == nil || oldConversion .WebhookClientConfig == nil ||
561+ len (oldConversion .WebhookClientConfig .CABundle ) == 0 {
562+ return false
563+ }
564+ return len (webhook .ValidateCABundle (field .NewPath ("caBundle" ), oldConversion .WebhookClientConfig .CABundle )) > 0
565+ }
566+
548567// hasValidConversionReviewVersion return true if there is a valid version or if the list is empty.
549568func hasValidConversionReviewVersionOrEmpty (versions []string ) bool {
550569 if len (versions ) < 1 {
@@ -558,12 +577,7 @@ func hasValidConversionReviewVersionOrEmpty(versions []string) bool {
558577 return false
559578}
560579
561- // ValidateCustomResourceConversion statically validates
562- func ValidateCustomResourceConversion (conversion * apiextensions.CustomResourceConversion , fldPath * field.Path ) field.ErrorList {
563- return validateCustomResourceConversion (conversion , true , fldPath )
564- }
565-
566- func validateCustomResourceConversion (conversion * apiextensions.CustomResourceConversion , requireRecognizedVersion bool , fldPath * field.Path ) field.ErrorList {
580+ func validateCustomResourceConversion (conversion * apiextensions.CustomResourceConversion , requireRecognizedVersion bool , fldPath * field.Path , opts validationOptions ) field.ErrorList {
567581 allErrs := field.ErrorList {}
568582 if conversion == nil {
569583 return allErrs
@@ -582,6 +596,9 @@ func validateCustomResourceConversion(conversion *apiextensions.CustomResourceCo
582596 case cc .Service != nil :
583597 allErrs = append (allErrs , webhook .ValidateWebhookService (fldPath .Child ("webhookClientConfig" ).Child ("service" ), cc .Service .Name , cc .Service .Namespace , cc .Service .Path , cc .Service .Port )... )
584598 }
599+ if len (cc .CABundle ) > 0 && ! opts .allowInvalidCABundle {
600+ allErrs = append (allErrs , webhook .ValidateCABundle (fldPath .Child ("webhookClientConfig" ).Child ("caBundle" ), cc .CABundle )... )
601+ }
585602 }
586603 allErrs = append (allErrs , validateConversionReviewVersions (conversion .ConversionReviewVersions , requireRecognizedVersion , fldPath .Child ("conversionReviewVersions" ))... )
587604 } else {
0 commit comments