mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-08 18:15:48 +00:00
refactor: make use of handlers in engine validation (#6704)
* refactor: make use of handlers in engine validation Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * polex Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
54c5a4e127
commit
341ed36e54
18 changed files with 155 additions and 259 deletions
|
@ -8,8 +8,12 @@ import (
|
|||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
)
|
||||
|
||||
// EngineContextLoader provides a function to load context entries from the various clients initialised with the engine ones
|
||||
type EngineContextLoader = func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error
|
||||
|
||||
// EngineContextLoaderFactory provides an EngineContextLoader given a policy and rule name
|
||||
type EngineContextLoaderFactory = func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) EngineContextLoader
|
||||
|
||||
// Engine is the main interface to run policies against resources
|
||||
type Engine interface {
|
||||
// Validate applies validation rules from policy on the resource
|
||||
|
|
|
@ -69,7 +69,7 @@ func (e *engine) filterRule(
|
|||
}
|
||||
|
||||
// check if there is a corresponding policy exception
|
||||
ruleResp := hasPolicyExceptions(logger, ruleType, e.exceptionSelector, policyContext, rule, e.configuration)
|
||||
ruleResp := e.hasPolicyExceptions(logger, ruleType, policyContext, rule)
|
||||
if ruleResp != nil {
|
||||
return ruleResp
|
||||
}
|
||||
|
@ -80,15 +80,14 @@ func (e *engine) filterRule(
|
|||
oldResource := policyContext.OldResource()
|
||||
admissionInfo := policyContext.AdmissionInfo()
|
||||
ctx := policyContext.JSONContext()
|
||||
excludeGroupRole := e.configuration.GetExcludedGroups()
|
||||
namespaceLabels := policyContext.NamespaceLabels()
|
||||
policy := policyContext.Policy()
|
||||
gvk, subresource := policyContext.ResourceKind()
|
||||
|
||||
if err := engineutils.MatchesResourceDescription(newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, policy.GetNamespace(), gvk, subresource); err != nil {
|
||||
if err := engineutils.MatchesResourceDescription(newResource, rule, admissionInfo, namespaceLabels, policy.GetNamespace(), gvk, subresource); err != nil {
|
||||
if ruleType == engineapi.Generation {
|
||||
// if the oldResource matched, return "false" to delete GR for it
|
||||
if err = engineutils.MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, policy.GetNamespace(), gvk, subresource); err == nil {
|
||||
if err = engineutils.MatchesResourceDescription(oldResource, rule, admissionInfo, namespaceLabels, policy.GetNamespace(), gvk, subresource); err == nil {
|
||||
return &engineapi.RuleResponse{
|
||||
Name: rule.Name,
|
||||
Type: ruleType,
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers/manifest"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers/mutation"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers/validation"
|
||||
"github.com/kyverno/kyverno/pkg/engine/internal"
|
||||
|
@ -26,16 +25,16 @@ import (
|
|||
)
|
||||
|
||||
type engine struct {
|
||||
configuration config.Configuration
|
||||
client dclient.Interface
|
||||
rclient registryclient.Client
|
||||
contextLoader engineapi.ContextLoaderFactory
|
||||
exceptionSelector engineapi.PolicyExceptionSelector
|
||||
verifyManifestHandler handlers.Handler
|
||||
mutateHandler handlers.Handler
|
||||
mutateExistingHandler handlers.Handler
|
||||
validateHandler handlers.Handler
|
||||
validateImageHandler handlers.Handler
|
||||
configuration config.Configuration
|
||||
client dclient.Interface
|
||||
rclient registryclient.Client
|
||||
engineContextLoaderFactory engineapi.EngineContextLoaderFactory
|
||||
exceptionSelector engineapi.PolicyExceptionSelector
|
||||
validateManifestHandler handlers.Handler
|
||||
mutateResourceHandler handlers.Handler
|
||||
mutateExistingHandler handlers.Handler
|
||||
validateResourceHandler handlers.Handler
|
||||
validateImageHandler handlers.Handler
|
||||
}
|
||||
|
||||
func NewEngine(
|
||||
|
@ -45,19 +44,30 @@ func NewEngine(
|
|||
contextLoader engineapi.ContextLoaderFactory,
|
||||
exceptionSelector engineapi.PolicyExceptionSelector,
|
||||
) engineapi.Engine {
|
||||
e := &engine{
|
||||
configuration: configuration,
|
||||
client: client,
|
||||
rclient: rclient,
|
||||
contextLoader: contextLoader,
|
||||
exceptionSelector: exceptionSelector,
|
||||
verifyManifestHandler: manifest.NewHandler(client),
|
||||
engineContextLoaderFactory := func(policy kyvernov1.PolicyInterface, rule kyvernov1.Rule) engineapi.EngineContextLoader {
|
||||
loader := contextLoader(policy, rule)
|
||||
return func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error {
|
||||
return loader.Load(
|
||||
ctx,
|
||||
client,
|
||||
rclient,
|
||||
contextEntries,
|
||||
jsonContext,
|
||||
)
|
||||
}
|
||||
}
|
||||
return &engine{
|
||||
configuration: configuration,
|
||||
client: client,
|
||||
rclient: rclient,
|
||||
engineContextLoaderFactory: engineContextLoaderFactory,
|
||||
exceptionSelector: exceptionSelector,
|
||||
validateManifestHandler: validation.NewValidateManifestHandler(client),
|
||||
validateImageHandler: validation.NewValidateImageHandler(configuration),
|
||||
validateResourceHandler: validation.NewValidateResourceHandler(engineContextLoaderFactory),
|
||||
mutateResourceHandler: mutation.NewMutateResourceHandler(engineContextLoaderFactory),
|
||||
mutateExistingHandler: mutation.NewMutateExistingHandler(client, engineContextLoaderFactory),
|
||||
}
|
||||
e.mutateHandler = mutation.NewHandler(configuration, e.ContextLoader)
|
||||
e.mutateExistingHandler = mutation.NewMutateExistingHandler(configuration, client, e.ContextLoader)
|
||||
e.validateHandler = validation.NewHandler(e.ContextLoader)
|
||||
e.validateImageHandler = validation.NewValidateImageHandler(configuration, e.ContextLoader)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *engine) Validate(
|
||||
|
@ -120,16 +130,44 @@ func (e *engine) ContextLoader(
|
|||
policy kyvernov1.PolicyInterface,
|
||||
rule kyvernov1.Rule,
|
||||
) engineapi.EngineContextLoader {
|
||||
loader := e.contextLoader(policy, rule)
|
||||
return func(ctx context.Context, contextEntries []kyvernov1.ContextEntry, jsonContext enginecontext.Interface) error {
|
||||
return loader.Load(
|
||||
ctx,
|
||||
e.client,
|
||||
e.rclient,
|
||||
contextEntries,
|
||||
jsonContext,
|
||||
)
|
||||
return e.engineContextLoaderFactory(policy, rule)
|
||||
}
|
||||
|
||||
// matches checks if either the new or old resource satisfies the filter conditions defined in the rule
|
||||
func matches(
|
||||
rule kyvernov1.Rule,
|
||||
policyContext engineapi.PolicyContext,
|
||||
resource unstructured.Unstructured,
|
||||
) error {
|
||||
gvk, subresource := policyContext.ResourceKind()
|
||||
err := engineutils.MatchesResourceDescription(
|
||||
resource,
|
||||
rule,
|
||||
policyContext.AdmissionInfo(),
|
||||
policyContext.NamespaceLabels(),
|
||||
policyContext.Policy().GetNamespace(),
|
||||
gvk,
|
||||
subresource,
|
||||
)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
oldResource := policyContext.OldResource()
|
||||
if oldResource.Object != nil {
|
||||
err := engineutils.MatchesResourceDescription(
|
||||
policyContext.OldResource(),
|
||||
rule,
|
||||
policyContext.AdmissionInfo(),
|
||||
policyContext.NamespaceLabels(),
|
||||
policyContext.Policy().GetNamespace(),
|
||||
gvk,
|
||||
subresource,
|
||||
)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *engine) invokeRuleHandler(
|
||||
|
@ -139,7 +177,7 @@ func (e *engine) invokeRuleHandler(
|
|||
policyContext engineapi.PolicyContext,
|
||||
resource unstructured.Unstructured,
|
||||
rule kyvernov1.Rule,
|
||||
polexFilter func(logr.Logger, engineapi.PolicyContext, kyvernov1.Rule) *engineapi.RuleResponse,
|
||||
ruleType engineapi.RuleType,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
return tracing.ChildSpan2(
|
||||
ctx,
|
||||
|
@ -147,26 +185,12 @@ func (e *engine) invokeRuleHandler(
|
|||
fmt.Sprintf("RULE %s", rule.Name),
|
||||
func(ctx context.Context, span trace.Span) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if resource and rule match
|
||||
var excludeResource []string
|
||||
if len(e.configuration.GetExcludedGroups()) > 0 {
|
||||
excludeResource = e.configuration.GetExcludedGroups()
|
||||
}
|
||||
gvk, subresource := policyContext.ResourceKind()
|
||||
if err := engineutils.MatchesResourceDescription(
|
||||
resource,
|
||||
rule,
|
||||
policyContext.AdmissionInfo(),
|
||||
excludeResource,
|
||||
policyContext.NamespaceLabels(),
|
||||
policyContext.Policy().GetNamespace(),
|
||||
gvk,
|
||||
subresource,
|
||||
); err != nil {
|
||||
if err := matches(rule, policyContext, resource); err != nil {
|
||||
logger.V(4).Info("rule not matched", "reason", err.Error())
|
||||
return resource, nil
|
||||
}
|
||||
// check if there's an exception
|
||||
if ruleResp := polexFilter(logger, policyContext, rule); ruleResp != nil {
|
||||
if ruleResp := e.hasPolicyExceptions(logger, ruleType, policyContext, rule); ruleResp != nil {
|
||||
return resource, handlers.RuleResponses(ruleResp)
|
||||
}
|
||||
// load rule context
|
||||
|
|
|
@ -71,25 +71,23 @@ func matchesException(
|
|||
|
||||
// hasPolicyExceptions returns nil when there are no matching exceptions.
|
||||
// A rule response is returned when an exception is matched, or there is an error.
|
||||
func hasPolicyExceptions(
|
||||
log logr.Logger,
|
||||
func (e *engine) hasPolicyExceptions(
|
||||
logger logr.Logger,
|
||||
ruleType engineapi.RuleType,
|
||||
selector engineapi.PolicyExceptionSelector,
|
||||
ctx engineapi.PolicyContext,
|
||||
rule kyvernov1.Rule,
|
||||
cfg config.Configuration,
|
||||
) *engineapi.RuleResponse {
|
||||
// if matches, check if there is a corresponding policy exception
|
||||
exception, err := matchesException(selector, ctx, rule, cfg)
|
||||
exception, err := matchesException(e.exceptionSelector, ctx, rule, e.configuration)
|
||||
var response *engineapi.RuleResponse
|
||||
// if we found an exception
|
||||
if err == nil && exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
response = internal.RuleError(&rule, ruleType, "failed to compute exception key", err)
|
||||
} else {
|
||||
log.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
response = internal.RuleSkip(&rule, ruleType, "rule skipped due to policy exception "+key)
|
||||
response.Exception = exception
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers"
|
||||
"github.com/kyverno/kyverno/pkg/engine/internal"
|
||||
|
@ -14,25 +13,22 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
type handlerExisting struct {
|
||||
configuration config.Configuration
|
||||
type mutateExistingHandler struct {
|
||||
client dclient.Interface
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader
|
||||
contextLoader engineapi.EngineContextLoaderFactory
|
||||
}
|
||||
|
||||
func NewMutateExistingHandler(
|
||||
configuration config.Configuration,
|
||||
client dclient.Interface,
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader,
|
||||
contextLoader engineapi.EngineContextLoaderFactory,
|
||||
) handlers.Handler {
|
||||
return handlerExisting{
|
||||
configuration: configuration,
|
||||
return mutateExistingHandler{
|
||||
client: client,
|
||||
contextLoader: contextLoader,
|
||||
}
|
||||
}
|
||||
|
||||
func (h handlerExisting) Process(
|
||||
func (h mutateExistingHandler) Process(
|
||||
ctx context.Context,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
|
@ -5,7 +5,6 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers"
|
||||
"github.com/kyverno/kyverno/pkg/engine/mutate"
|
||||
|
@ -13,22 +12,19 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
configuration config.Configuration
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader
|
||||
type mutateResourceHandler struct {
|
||||
contextLoader engineapi.EngineContextLoaderFactory
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
configuration config.Configuration,
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader,
|
||||
func NewMutateResourceHandler(
|
||||
contextLoader engineapi.EngineContextLoaderFactory,
|
||||
) handlers.Handler {
|
||||
return handler{
|
||||
configuration: configuration,
|
||||
return mutateResourceHandler{
|
||||
contextLoader: contextLoader,
|
||||
}
|
||||
}
|
||||
|
||||
func (h handler) Process(
|
||||
func (h mutateResourceHandler) Process(
|
||||
ctx context.Context,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
gojmespath "github.com/jmespath/go-jmespath"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
|
@ -18,16 +17,13 @@ import (
|
|||
|
||||
type validateImageHandler struct {
|
||||
configuration config.Configuration
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader
|
||||
}
|
||||
|
||||
func NewValidateImageHandler(
|
||||
configuration config.Configuration,
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader,
|
||||
) handlers.Handler {
|
||||
return validateImageHandler{
|
||||
configuration: configuration,
|
||||
contextLoader: contextLoader,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,8 +37,6 @@ func (h validateImageHandler) Process(
|
|||
if engineutils.IsDeleteRequest(policyContext) {
|
||||
return resource, nil
|
||||
}
|
||||
policy := policyContext.Policy()
|
||||
contextLoader := h.contextLoader(policy, rule)
|
||||
matchingImages, _, err := engineutils.ExtractMatchingImages(
|
||||
policyContext.NewResource(),
|
||||
policyContext.JSONContext(),
|
||||
|
@ -55,15 +49,6 @@ func (h validateImageHandler) Process(
|
|||
if len(matchingImages) == 0 {
|
||||
return resource, handlers.RuleResponses(internal.RuleSkip(&rule, engineapi.Validation, "image verified"))
|
||||
}
|
||||
if err := contextLoader(ctx, rule.Context, policyContext.JSONContext()); err != nil {
|
||||
if _, ok := err.(gojmespath.NotFoundError); ok {
|
||||
logger.V(3).Info("failed to load context", "reason", err.Error())
|
||||
} else {
|
||||
logger.Error(err, "failed to load context")
|
||||
}
|
||||
|
||||
return resource, handlers.RuleResponses(internal.RuleError(&rule, engineapi.Validation, "failed to load context", err))
|
||||
}
|
||||
preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext, rule.RawAnyAllConditions)
|
||||
if err != nil {
|
||||
return resource, handlers.RuleResponses(internal.RuleError(&rule, engineapi.Validation, "failed to evaluate preconditions", err))
|
|
@ -1,4 +1,4 @@
|
|||
package manifest
|
||||
package validation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -34,17 +34,17 @@ const (
|
|||
CosignEnvVariable = "COSIGN_EXPERIMENTAL"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
type validateManifestHandler struct {
|
||||
client dclient.Interface
|
||||
}
|
||||
|
||||
func NewHandler(client dclient.Interface) handlers.Handler {
|
||||
return handler{
|
||||
func NewValidateManifestHandler(client dclient.Interface) handlers.Handler {
|
||||
return validateManifestHandler{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (h handler) Process(
|
||||
func (h validateManifestHandler) Process(
|
||||
ctx context.Context,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
||||
|
@ -66,7 +66,7 @@ func (h handler) Process(
|
|||
return resource, handlers.RuleResponses(internal.RulePass(&rule, engineapi.Validation, reason))
|
||||
}
|
||||
|
||||
func (h handler) verifyManifest(
|
||||
func (h validateManifestHandler) verifyManifest(
|
||||
ctx context.Context,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
||||
|
@ -167,7 +167,7 @@ func (h handler) verifyManifest(
|
|||
return true, msg, nil
|
||||
}
|
||||
|
||||
func (h handler) checkDryRunPermission(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
func (h validateManifestHandler) checkDryRunPermission(ctx context.Context, kind, namespace string) (bool, error) {
|
||||
canI := auth.NewCanI(h.client.Discovery(), h.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "create", "")
|
||||
ok, err := canI.RunAccessCheck(ctx)
|
||||
if err != nil {
|
|
@ -1,4 +1,4 @@
|
|||
package manifest
|
||||
package validation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -618,7 +618,7 @@ FdGxexVrR4YqO1pRViKxmD9oMu4I7K/4sM51nbH65ycB2uRiDfIdRoV/+A==
|
|||
`
|
||||
|
||||
var (
|
||||
h = handler{}
|
||||
h = validateManifestHandler{}
|
||||
cfg = config.NewDefaultConfiguration()
|
||||
)
|
||||
|
|
@ -26,19 +26,19 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader
|
||||
type validateResourceHandler struct {
|
||||
contextLoader engineapi.EngineContextLoaderFactory
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
contextLoader func(kyvernov1.PolicyInterface, kyvernov1.Rule) engineapi.EngineContextLoader,
|
||||
func NewValidateResourceHandler(
|
||||
contextLoader engineapi.EngineContextLoaderFactory,
|
||||
) handlers.Handler {
|
||||
return handler{
|
||||
return validateResourceHandler{
|
||||
contextLoader: contextLoader,
|
||||
}
|
||||
}
|
||||
|
||||
func (h handler) Process(
|
||||
func (h validateResourceHandler) Process(
|
||||
ctx context.Context,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
|
@ -77,12 +77,13 @@ func (e *engine) doVerifyAndPatch(
|
|||
startTime := time.Now()
|
||||
logger = internal.LoggerWithRule(logger, *rule)
|
||||
|
||||
if !matches(logger, rule, policyContext, e.configuration) {
|
||||
if err := matches(*rule, policyContext, policyContext.NewResource()); err != nil {
|
||||
logger.V(5).Info("resource does not match rule", "reason", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// check if there is a corresponding policy exception
|
||||
ruleResp := hasPolicyExceptions(logger, engineapi.ImageVerify, e.exceptionSelector, policyContext, *rule, e.configuration)
|
||||
ruleResp := e.hasPolicyExceptions(logger, engineapi.ImageVerify, policyContext, *rule)
|
||||
if ruleResp != nil {
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
||||
return
|
||||
|
|
|
@ -35,14 +35,11 @@ func (e *engine) mutate(
|
|||
if !rule.HasMutate() {
|
||||
continue
|
||||
}
|
||||
polexFilter := func(logger logr.Logger, policyContext engineapi.PolicyContext, rule kyvernov1.Rule) *engineapi.RuleResponse {
|
||||
return hasPolicyExceptions(logger, engineapi.Validation, e.exceptionSelector, policyContext, rule, e.configuration)
|
||||
}
|
||||
handler := e.mutateHandler
|
||||
handler := e.mutateResourceHandler
|
||||
if !policyContext.AdmissionOperation() && rule.IsMutateExisting() {
|
||||
handler = e.mutateExistingHandler
|
||||
}
|
||||
resource, ruleResp := e.invokeRuleHandler(ctx, logger, handler, policyContext, matchedResource, rule, polexFilter)
|
||||
resource, ruleResp := e.invokeRuleHandler(ctx, logger, handler, policyContext, matchedResource, rule, engineapi.Mutation)
|
||||
matchedResource = resource
|
||||
for _, ruleResp := range ruleResp {
|
||||
ruleResp := ruleResp
|
||||
|
|
|
@ -59,13 +59,6 @@ func (c *PolicyContext) Policy() kyvernov1.PolicyInterface {
|
|||
}
|
||||
|
||||
func (c *PolicyContext) NewResource() unstructured.Unstructured {
|
||||
// object, err := c.jsonContext.Query("request.object")
|
||||
// if err == nil {
|
||||
// if o, ok := object.(map[string]interface{}); ok {
|
||||
// return unstructured.Unstructured{Object: o}
|
||||
// }
|
||||
// }
|
||||
// return unstructured.Unstructured{}
|
||||
return c.newResource
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,6 @@ func MatchesResourceDescription(
|
|||
resourceRef unstructured.Unstructured,
|
||||
ruleRef kyvernov1.Rule,
|
||||
admissionInfoRef kyvernov1beta1.RequestInfo,
|
||||
dynamicConfig []string,
|
||||
namespaceLabels map[string]string,
|
||||
policyNamespace string,
|
||||
gvk schema.GroupVersionKind,
|
||||
|
@ -204,7 +203,7 @@ func MatchesResourceDescription(
|
|||
if len(rule.ExcludeResources.Any) > 0 {
|
||||
// exclude the object if ANY of the criteria match
|
||||
for _, rer := range rule.ExcludeResources.Any {
|
||||
reasonsForFailure = append(reasonsForFailure, matchesResourceDescriptionExcludeHelper(rer, admissionInfo, resource, dynamicConfig, namespaceLabels, gvk, subresource)...)
|
||||
reasonsForFailure = append(reasonsForFailure, matchesResourceDescriptionExcludeHelper(rer, admissionInfo, resource, namespaceLabels, gvk, subresource)...)
|
||||
}
|
||||
} else if len(rule.ExcludeResources.All) > 0 {
|
||||
// exclude the object if ALL the criteria match
|
||||
|
@ -212,7 +211,7 @@ func MatchesResourceDescription(
|
|||
for _, rer := range rule.ExcludeResources.All {
|
||||
// we got no errors inplying a resource did NOT exclude it
|
||||
// "matchesResourceDescriptionExcludeHelper" returns errors if resource is excluded by a filter
|
||||
if len(matchesResourceDescriptionExcludeHelper(rer, admissionInfo, resource, dynamicConfig, namespaceLabels, gvk, subresource)) == 0 {
|
||||
if len(matchesResourceDescriptionExcludeHelper(rer, admissionInfo, resource, namespaceLabels, gvk, subresource)) == 0 {
|
||||
excludedByAll = false
|
||||
break
|
||||
}
|
||||
|
@ -222,7 +221,7 @@ func MatchesResourceDescription(
|
|||
}
|
||||
} else {
|
||||
rer := kyvernov1.ResourceFilter{UserInfo: rule.ExcludeResources.UserInfo, ResourceDescription: rule.ExcludeResources.ResourceDescription}
|
||||
reasonsForFailure = append(reasonsForFailure, matchesResourceDescriptionExcludeHelper(rer, admissionInfo, resource, dynamicConfig, namespaceLabels, gvk, subresource)...)
|
||||
reasonsForFailure = append(reasonsForFailure, matchesResourceDescriptionExcludeHelper(rer, admissionInfo, resource, namespaceLabels, gvk, subresource)...)
|
||||
}
|
||||
|
||||
// creating final error
|
||||
|
@ -269,7 +268,6 @@ func matchesResourceDescriptionExcludeHelper(
|
|||
rer kyvernov1.ResourceFilter,
|
||||
admissionInfo kyvernov1beta1.RequestInfo,
|
||||
resource unstructured.Unstructured,
|
||||
dynamicConfig []string,
|
||||
namespaceLabels map[string]string,
|
||||
gvk schema.GroupVersionKind,
|
||||
subresource string,
|
||||
|
|
|
@ -906,7 +906,7 @@ func TestMatchesResourceDescription(t *testing.T) {
|
|||
resource, _ := kubeutils.BytesToUnstructured(tc.Resource)
|
||||
|
||||
for _, rule := range autogen.ComputeRules(&policy) {
|
||||
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, []string{}, nil, "", resource.GroupVersionKind(), "")
|
||||
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, nil, "", resource.GroupVersionKind(), "")
|
||||
if err != nil {
|
||||
if !tc.areErrorsExpected {
|
||||
t.Errorf("Testcase %d Unexpected error: %v\nmsg: %s", i+1, err, tc.Description)
|
||||
|
@ -1811,7 +1811,7 @@ func TestMatchesResourceDescription_GenerateName(t *testing.T) {
|
|||
resource, _ := kubeutils.BytesToUnstructured(tc.Resource)
|
||||
|
||||
for _, rule := range autogen.ComputeRules(&policy) {
|
||||
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, []string{}, nil, "", resource.GroupVersionKind(), "")
|
||||
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, nil, "", resource.GroupVersionKind(), "")
|
||||
if err != nil {
|
||||
if !tc.areErrorsExpected {
|
||||
t.Errorf("Testcase %d Unexpected error: %v\nmsg: %s", i+1, err, tc.Description)
|
||||
|
@ -1878,7 +1878,7 @@ func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -1967,15 +1967,6 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
|
|||
ExcludeResources: v1.MatchResources{},
|
||||
}
|
||||
|
||||
// the following groups are added by default to ensure that these groups or users are always excluded
|
||||
// these should not affect the rule in our case, we expect our rule to NOT match with the nginx pod.
|
||||
|
||||
dynamicConfig := []string{
|
||||
"system:serviceaccounts:kube-system",
|
||||
"system:nodes",
|
||||
"system:kube-scheduler",
|
||||
}
|
||||
|
||||
// this is the request info that was also passed with the mocked pod
|
||||
requestInfo := v1beta1.RequestInfo{
|
||||
AdmissionUserInfo: authenticationv1.UserInfo{
|
||||
|
@ -1990,7 +1981,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
|
|||
}
|
||||
|
||||
// First test: confirm that this above rule produces errors (and raise an error if err == nil)
|
||||
if err := MatchesResourceDescription(*resource, rule, requestInfo, dynamicConfig, nil, "", resource.GroupVersionKind(), ""); err == nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, requestInfo, nil, "", resource.GroupVersionKind(), ""); err == nil {
|
||||
t.Error("Testcase was expected to fail, but err was nil")
|
||||
}
|
||||
|
||||
|
@ -2010,7 +2001,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
|
|||
}
|
||||
|
||||
// Second test: confirm that matching this rule does not create any errors (and raise if err != nil)
|
||||
if err := MatchesResourceDescription(*resource, rule2, requestInfo, dynamicConfig, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule2, requestInfo, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase was expected to not fail, but err was %s", err)
|
||||
}
|
||||
|
||||
|
@ -2024,7 +2015,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) {
|
|||
}}
|
||||
|
||||
// Third test: confirm that now the custom exclude-snippet should run in CheckSubjects() and that should result in this rule failing (raise if err == nil for that reason)
|
||||
if err := MatchesResourceDescription(*resource, rule2, requestInfo, dynamicConfig, nil, "", resource.GroupVersionKind(), ""); err == nil {
|
||||
if err := MatchesResourceDescription(*resource, rule2, requestInfo, nil, "", resource.GroupVersionKind(), ""); err == nil {
|
||||
t.Error("Testcase was expected to fail, but err was nil #1!")
|
||||
}
|
||||
}
|
||||
|
@ -2083,7 +2074,7 @@ func TestResourceDescriptionMatch_Name(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -2141,7 +2132,7 @@ func TestResourceDescriptionMatch_GenerateName(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -2200,7 +2191,7 @@ func TestResourceDescriptionMatch_Name_Regex(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -2258,7 +2249,7 @@ func TestResourceDescriptionMatch_GenerateName_Regex(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -2325,7 +2316,7 @@ func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -2393,7 +2384,7 @@ func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) {
|
|||
}
|
||||
rule := v1.Rule{MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err != nil {
|
||||
t.Errorf("Testcase has failed due to the following:%v", err)
|
||||
}
|
||||
}
|
||||
|
@ -2474,7 +2465,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
|
|||
ExcludeResources: v1.MatchResources{ResourceDescription: resourceDescriptionExclude},
|
||||
}
|
||||
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, []string{}, nil, "", resource.GroupVersionKind(), ""); err == nil {
|
||||
if err := MatchesResourceDescription(*resource, rule, v1beta1.RequestInfo{}, nil, "", resource.GroupVersionKind(), ""); err == nil {
|
||||
t.Errorf("Testcase has failed due to the following:\n Function has returned no error, even though it was supposed to fail")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,19 +2,14 @@ package engine
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"github.com/kyverno/kyverno/pkg/config"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/engine/handlers"
|
||||
"github.com/kyverno/kyverno/pkg/engine/internal"
|
||||
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
"github.com/kyverno/kyverno/pkg/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func (e *engine) validate(
|
||||
|
@ -41,117 +36,36 @@ func (e *engine) validateResource(
|
|||
policyContext.JSONContext().Checkpoint()
|
||||
defer policyContext.JSONContext().Restore()
|
||||
|
||||
rules := autogen.ComputeRules(policyContext.Policy())
|
||||
matchCount := 0
|
||||
applyRules := policyContext.Policy().GetSpec().GetApplyRules()
|
||||
|
||||
for i := range rules {
|
||||
rule := &rules[i]
|
||||
logger := internal.LoggerWithRule(logger, rules[i])
|
||||
logger.V(3).Info("processing validation rule", "matchCount", matchCount)
|
||||
policyContext.JSONContext().Reset()
|
||||
for _, rule := range autogen.ComputeRules(policyContext.Policy()) {
|
||||
logger := internal.LoggerWithRule(logger, rule)
|
||||
startTime := time.Now()
|
||||
ruleResp := tracing.ChildSpan1(
|
||||
ctx,
|
||||
"pkg/engine",
|
||||
fmt.Sprintf("RULE %s", rule.Name),
|
||||
func(ctx context.Context, span trace.Span) []engineapi.RuleResponse {
|
||||
hasValidate := rule.HasValidate()
|
||||
hasValidateImage := rule.HasImagesValidationChecks()
|
||||
hasYAMLSignatureVerify := rule.HasYAMLSignatureVerify()
|
||||
if !hasValidate && !hasValidateImage {
|
||||
return nil
|
||||
}
|
||||
if !matches(logger, rule, policyContext, e.configuration) {
|
||||
return nil
|
||||
}
|
||||
// check if there is a corresponding policy exception
|
||||
ruleResp := hasPolicyExceptions(logger, engineapi.Validation, e.exceptionSelector, policyContext, *rule, e.configuration)
|
||||
if ruleResp != nil {
|
||||
return handlers.RuleResponses(ruleResp)
|
||||
}
|
||||
policyContext.JSONContext().Reset()
|
||||
if hasValidate && !hasYAMLSignatureVerify {
|
||||
_, rr := e.validateHandler.Process(
|
||||
ctx,
|
||||
logger,
|
||||
policyContext,
|
||||
policyContext.NewResource(),
|
||||
*rule,
|
||||
)
|
||||
return rr
|
||||
} else if hasValidateImage {
|
||||
_, rr := e.validateImageHandler.Process(
|
||||
ctx,
|
||||
logger,
|
||||
policyContext,
|
||||
policyContext.NewResource(),
|
||||
*rule,
|
||||
)
|
||||
return rr
|
||||
} else if hasYAMLSignatureVerify {
|
||||
_, rr := e.verifyManifestHandler.Process(
|
||||
ctx,
|
||||
logger,
|
||||
policyContext,
|
||||
policyContext.NewResource(),
|
||||
*rule,
|
||||
)
|
||||
return rr
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
for _, ruleResp := range ruleResp {
|
||||
ruleResp := ruleResp
|
||||
internal.AddRuleResponse(resp, &ruleResp, startTime)
|
||||
logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String())
|
||||
hasValidate := rule.HasValidate()
|
||||
hasValidateImage := rule.HasImagesValidationChecks()
|
||||
hasYAMLSignatureVerify := rule.HasYAMLSignatureVerify()
|
||||
if !hasValidate && !hasValidateImage {
|
||||
continue
|
||||
}
|
||||
var handler handlers.Handler
|
||||
if hasValidate && !hasYAMLSignatureVerify {
|
||||
handler = e.validateResourceHandler
|
||||
} else if hasValidateImage {
|
||||
handler = e.validateImageHandler
|
||||
} else if hasYAMLSignatureVerify {
|
||||
handler = e.validateManifestHandler
|
||||
}
|
||||
if handler != nil {
|
||||
_, ruleResp := e.invokeRuleHandler(ctx, logger, handler, policyContext, policyContext.NewResource(), rule, engineapi.Validation)
|
||||
for _, ruleResp := range ruleResp {
|
||||
ruleResp := ruleResp
|
||||
internal.AddRuleResponse(resp, &ruleResp, startTime)
|
||||
logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String())
|
||||
}
|
||||
}
|
||||
if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// matches checks if either the new or old resource satisfies the filter conditions defined in the rule
|
||||
func matches(
|
||||
logger logr.Logger,
|
||||
rule *kyvernov1.Rule,
|
||||
ctx engineapi.PolicyContext,
|
||||
cfg config.Configuration,
|
||||
) bool {
|
||||
gvk, subresource := ctx.ResourceKind()
|
||||
err := engineutils.MatchesResourceDescription(
|
||||
ctx.NewResource(),
|
||||
*rule,
|
||||
ctx.AdmissionInfo(),
|
||||
cfg.GetExcludedGroups(),
|
||||
ctx.NamespaceLabels(),
|
||||
"",
|
||||
gvk,
|
||||
subresource,
|
||||
)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
oldResource := ctx.OldResource()
|
||||
if oldResource.Object != nil {
|
||||
err := engineutils.MatchesResourceDescription(
|
||||
ctx.OldResource(),
|
||||
*rule,
|
||||
ctx.AdmissionInfo(),
|
||||
cfg.GetExcludedGroups(),
|
||||
ctx.NamespaceLabels(),
|
||||
"",
|
||||
gvk,
|
||||
subresource,
|
||||
)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
logger.V(5).Info("resource does not match rule", "reason", err.Error())
|
||||
return false
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue