Skip to content

Commit

Permalink
Validate CABundle when writing CRD
Browse files Browse the repository at this point in the history
  • Loading branch information
Jefftree committed Jul 19, 2024
1 parent e1aa819 commit a5791b3
Show file tree
Hide file tree
Showing 6 changed files with 805 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"unicode/utf8"

celgo "github.com/google/cel-go/cel"

"k8s.io/apiextensions-apiserver/pkg/apihelpers"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
Expand Down Expand Up @@ -94,6 +93,8 @@ func ValidateCustomResourceDefinition(ctx context.Context, obj *apiextensions.Cu
requireMapListKeysMapSetValidation: true,
// strictCost is always true to enforce cost limits.
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true),
// allowInvalidCABundle is set to true since the CRD is not established yet.
allowInvalidCABundle: true,
}

allErrs := genericvalidation.ValidateObjectMeta(&obj.ObjectMeta, false, nameValidationFn, field.NewPath("metadata"))
Expand Down Expand Up @@ -140,6 +141,9 @@ type validationOptions struct {
suppressPerExpressionCost bool

celEnvironmentSet *environment.EnvSet
// allowInvalidCABundle allows an invalid conversion webhook CABundle on update only if the existing CABundle is invalid.
// An invalid CABundle is also permitted on create and before a CRD is in an Established=True condition.
allowInvalidCABundle bool
}

type preexistingExpressions struct {
Expand Down Expand Up @@ -233,7 +237,8 @@ func ValidateCustomResourceDefinitionUpdate(ctx context.Context, obj, oldObj *ap
preexistingExpressions: findPreexistingExpressions(&oldObj.Spec),
versionsWithUnchangedSchemas: findVersionsWithUnchangedSchemas(obj, oldObj),
// strictCost is always true to enforce cost limits.
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true),
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true),
allowInvalidCABundle: allowInvalidCABundle(oldObj),
}
return validateCustomResourceDefinitionUpdate(ctx, obj, oldObj, opts)
}
Expand Down Expand Up @@ -485,7 +490,7 @@ func validateCustomResourceDefinitionSpec(ctx context.Context, spec *apiextensio
if (spec.Conversion != nil && spec.Conversion.Strategy != apiextensions.NoneConverter) && (spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("conversion").Child("strategy"), spec.Conversion.Strategy, "must be None if spec.preserveUnknownFields is true"))
}
allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, opts.requireRecognizedConversionReviewVersion, fldPath.Child("conversion"))...)
allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, opts.requireRecognizedConversionReviewVersion, fldPath.Child("conversion"), opts)...)

return allErrs
}
Expand Down Expand Up @@ -545,6 +550,20 @@ func validateConversionReviewVersions(versions []string, requireRecognizedVersio
return allErrs
}

// Allows invalid CA Bundle to be specified only if the existing CABundle is invalid
// or if the CRD is not established yet.
func allowInvalidCABundle(oldCRD *apiextensions.CustomResourceDefinition) bool {
if !apiextensions.IsCRDConditionTrue(oldCRD, apiextensions.Established) {
return true
}
oldConversion := oldCRD.Spec.Conversion
if oldConversion == nil || oldConversion.WebhookClientConfig == nil ||
len(oldConversion.WebhookClientConfig.CABundle) == 0 {
return false
}
return len(webhook.ValidateCABundle(field.NewPath("caBundle"), oldConversion.WebhookClientConfig.CABundle)) > 0
}

// hasValidConversionReviewVersion return true if there is a valid version or if the list is empty.
func hasValidConversionReviewVersionOrEmpty(versions []string) bool {
if len(versions) < 1 {
Expand All @@ -558,12 +577,7 @@ func hasValidConversionReviewVersionOrEmpty(versions []string) bool {
return false
}

// ValidateCustomResourceConversion statically validates
func ValidateCustomResourceConversion(conversion *apiextensions.CustomResourceConversion, fldPath *field.Path) field.ErrorList {
return validateCustomResourceConversion(conversion, true, fldPath)
}

func validateCustomResourceConversion(conversion *apiextensions.CustomResourceConversion, requireRecognizedVersion bool, fldPath *field.Path) field.ErrorList {
func validateCustomResourceConversion(conversion *apiextensions.CustomResourceConversion, requireRecognizedVersion bool, fldPath *field.Path, opts validationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if conversion == nil {
return allErrs
Expand All @@ -582,6 +596,9 @@ func validateCustomResourceConversion(conversion *apiextensions.CustomResourceCo
case cc.Service != nil:
allErrs = append(allErrs, webhook.ValidateWebhookService(fldPath.Child("webhookClientConfig").Child("service"), cc.Service.Name, cc.Service.Namespace, cc.Service.Path, cc.Service.Port)...)
}
if len(cc.CABundle) > 0 && !opts.allowInvalidCABundle {
allErrs = append(allErrs, webhook.ValidateCABundle(fldPath.Child("webhookClientConfig").Child("caBundle"), cc.CABundle)...)
}
}
allErrs = append(allErrs, validateConversionReviewVersions(conversion.ConversionReviewVersions, requireRecognizedVersion, fldPath.Child("conversionReviewVersions"))...)
} else {
Expand Down
Loading

0 comments on commit a5791b3

Please sign in to comment.