1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-30 19:35:06 +00:00

refactor CEL validation in Kyverno policies (#8098)

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
Mariam Fahmy 2023-08-23 16:28:40 +03:00 committed by GitHub
parent 87728f1771
commit e1783e7375
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -9,6 +9,8 @@ import (
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -45,27 +47,36 @@ func (h validateCELHandler) Process(
return resource, nil
}
// get resource's name, namespace, GroupVersionResource, and GroupVersionKind
gvr := schema.GroupVersionResource(policyContext.RequestResource())
gvk := resource.GroupVersionKind()
namespaceName := resource.GetNamespace()
resourceName := resource.GetName()
var object, oldObject, versionedParams runtime.Object
object := resource.DeepCopyObject()
// in case of update request, set the oldObject to the current resource before it gets updated
var oldObject, versionedParams runtime.Object
oldResource := policyContext.OldResource()
object = resource.DeepCopyObject()
if oldResource.Object == nil {
oldObject = nil
} else {
oldObject = oldResource.DeepCopyObject()
}
var expressions, messageExpressions, matchExpressions, auditExpressions []cel.ExpressionAccessor
// check if the rule uses parameter resources
hasParam := rule.Validation.CEL.HasParam()
// extract preconditions written as CEL expressions
matchConditions := rule.CELPreconditions
// extract CEL expressions used in validations and audit annotations
validations := rule.Validation.CEL.Expressions
auditAnnotations := rule.Validation.CEL.AuditAnnotations
matchExpressions := convertMatchExpressions(matchConditions)
validateExpressions := convertValidations(validations)
messageExpressions := convertMessageExpressions(validations)
auditExpressions := convertAuditAnnotations(auditAnnotations)
// get the parameter resource if exists
hasParam := rule.Validation.CEL.HasParam()
if hasParam {
paramKind := rule.Validation.CEL.GetParamKind()
paramRef := rule.Validation.CEL.GetParamRef()
@ -88,56 +99,22 @@ func (h validateCELHandler) Process(
versionedParams = paramResource.DeepCopyObject()
}
// extract CEL expressions from validate.cel.expressions
for _, cel := range validations {
condition := &validatingadmissionpolicy.ValidationCondition{
Expression: cel.Expression,
Message: cel.Message,
}
messageCondition := &validatingadmissionpolicy.MessageExpressionCondition{
MessageExpression: cel.MessageExpression,
}
expressions = append(expressions, condition)
messageExpressions = append(messageExpressions, messageCondition)
}
// extract CEL expressions from rule.celPreconditions
for _, condition := range rule.CELPreconditions {
matchCondition := &matchconditions.MatchCondition{
Name: condition.Name,
Expression: condition.Expression,
}
matchExpressions = append(matchExpressions, matchCondition)
}
// extract CEL expressions from validate.cel.auditAnnotations
for _, auditAnnotation := range auditAnnotations {
auditCondition := &validatingadmissionpolicy.AuditAnnotationCondition{
Key: auditAnnotation.Key,
ValueExpression: auditAnnotation.ValueExpression,
}
auditExpressions = append(auditExpressions, auditCondition)
}
optionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}
// compile CEL expressions
compositedCompiler, err := cel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
if err != nil {
return resource, handlers.WithError(rule, engineapi.Validation, "Error while creating composited compiler", err)
}
filter := compositedCompiler.Compile(expressions, cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}, environment.StoredExpressions)
messageExpressionfilter := compositedCompiler.Compile(messageExpressions, cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}, environment.StoredExpressions)
auditAnnotationFilter := compositedCompiler.Compile(auditExpressions, cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}, environment.StoredExpressions)
matchConditionFilter := compositedCompiler.Compile(matchExpressions, cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}, environment.StoredExpressions)
filter := compositedCompiler.Compile(validateExpressions, optionalVars, environment.StoredExpressions)
messageExpressionfilter := compositedCompiler.Compile(messageExpressions, optionalVars, environment.StoredExpressions)
auditAnnotationFilter := compositedCompiler.Compile(auditExpressions, optionalVars, environment.StoredExpressions)
matchConditionFilter := compositedCompiler.Compile(matchExpressions, optionalVars, environment.StoredExpressions)
// newMatcher will be used to check if the incoming resource matches the CEL preconditions
newMatcher := matchconditions.NewMatcher(matchConditionFilter, nil, "", "", "")
// newValidator will be used to validate CEL expressions against the incoming object
validator := validatingadmissionpolicy.NewValidator(filter, newMatcher, auditAnnotationFilter, messageExpressionfilter, nil)
admissionAttributes := admission.NewAttributesRecord(object, oldObject, gvk, namespaceName, resourceName, gvr, "", admission.Operation(policyContext.Operation()), nil, false, nil)
versionedAttr, _ := admission.NewVersionedAttributes(admissionAttributes, admissionAttributes.GetKind(), nil)
var namespace *corev1.Namespace
// Special case, the namespace object has the namespace of itself.
@ -154,6 +131,8 @@ func (h validateCELHandler) Process(
}
}
admissionAttributes := admission.NewAttributesRecord(object, oldObject, gvk, namespaceName, resourceName, gvr, "", admission.Operation(policyContext.Operation()), nil, false, nil)
versionedAttr, _ := admission.NewVersionedAttributes(admissionAttributes, admissionAttributes.GetKind(), nil)
// validate the incoming object against the rule
validateResult := validator.Validate(ctx, gvr, versionedAttr, versionedParams, namespace, celconfig.RuntimeCELCostBudget, nil)
@ -177,3 +156,53 @@ func (h validateCELHandler) Process(
engineapi.RulePass(rule.Name, engineapi.Validation, msg),
)
}
func convertValidations(inputValidations []admissionregistrationv1alpha1.Validation) []cel.ExpressionAccessor {
celExpressionAccessor := make([]cel.ExpressionAccessor, len(inputValidations))
for i, validation := range inputValidations {
validation := validatingadmissionpolicy.ValidationCondition{
Expression: validation.Expression,
Message: validation.Message,
Reason: validation.Reason,
}
celExpressionAccessor[i] = &validation
}
return celExpressionAccessor
}
func convertMessageExpressions(inputValidations []admissionregistrationv1alpha1.Validation) []cel.ExpressionAccessor {
celExpressionAccessor := make([]cel.ExpressionAccessor, len(inputValidations))
for i, validation := range inputValidations {
if validation.MessageExpression != "" {
condition := validatingadmissionpolicy.MessageExpressionCondition{
MessageExpression: validation.MessageExpression,
}
celExpressionAccessor[i] = &condition
}
}
return celExpressionAccessor
}
func convertAuditAnnotations(inputValidations []admissionregistrationv1alpha1.AuditAnnotation) []cel.ExpressionAccessor {
celExpressionAccessor := make([]cel.ExpressionAccessor, len(inputValidations))
for i, validation := range inputValidations {
validation := validatingadmissionpolicy.AuditAnnotationCondition{
Key: validation.Key,
ValueExpression: validation.ValueExpression,
}
celExpressionAccessor[i] = &validation
}
return celExpressionAccessor
}
func convertMatchExpressions(matchExpressions []admissionregistrationv1.MatchCondition) []cel.ExpressionAccessor {
celExpressionAccessor := make([]cel.ExpressionAccessor, len(matchExpressions))
for i, condition := range matchExpressions {
condition := matchconditions.MatchCondition{
Name: condition.Name,
Expression: condition.Expression,
}
celExpressionAccessor[i] = &condition
}
return celExpressionAccessor
}