diff --git a/charts/kyverno/README.md b/charts/kyverno/README.md index 59cc7c4444..6174d22f83 100644 --- a/charts/kyverno/README.md +++ b/charts/kyverno/README.md @@ -327,7 +327,11 @@ The chart values are organised per component. | features.aggregateReports.enabled | bool | `true` | Enables the feature | | features.policyReports.enabled | bool | `true` | Enables the feature | | features.validatingAdmissionPolicyReports.enabled | bool | `false` | Enables the feature | -| features.backgroundReports.enabled | bool | `true` | Enables the feature | +| features.reporting.validate | bool | `true` | Enables the feature | +| features.reporting.mutate | bool | `true` | Enables the feature | +| features.reporting.mutateExisting | bool | `true` | Enables the feature | +| features.reporting.imageVerify | bool | `true` | Enables the feature | +| features.reporting.generate | bool | `true` | Enables the feature | | features.autoUpdateWebhooks.enabled | bool | `true` | Enables the feature | | features.backgroundScan.enabled | bool | `true` | Enables the feature | | features.backgroundScan.backgroundScanWorkers | int | `2` | Number of background scan workers | diff --git a/charts/kyverno/templates/_helpers.tpl b/charts/kyverno/templates/_helpers.tpl index 76c8fc22b9..f9918d661e 100644 --- a/charts/kyverno/templates/_helpers.tpl +++ b/charts/kyverno/templates/_helpers.tpl @@ -25,9 +25,6 @@ {{- with .validatingAdmissionPolicyReports -}} {{- $flags = append $flags (print "--validatingAdmissionPolicyReports=" .enabled) -}} {{- end -}} -{{- with .backgroundReports -}} - {{- $flags = append $flags (print "--backgroundReports=" .enabled) -}} -{{- end -}} {{- with .autoUpdateWebhooks -}} {{- $flags = append $flags (print "--autoUpdateWebhooks=" .enabled) -}} {{- end -}} @@ -97,6 +94,25 @@ {{- $flags = append $flags (print "--tufRootRaw=" .) -}} {{- end -}} {{- end -}} +{{- with .reporting -}} + {{- $reportingConfig := list -}} + {{- with .validate -}} + {{- $reportingConfig = append $reportingConfig "validate" -}} + {{- end -}} + {{- with .mutate -}} + {{- $reportingConfig = append $reportingConfig "mutate" -}} + {{- end -}} + {{- with .mutateExisting -}} + {{- $reportingConfig = append $reportingConfig "mutateExisting" -}} + {{- end -}} + {{- with .imageVerify -}} + {{- $reportingConfig = append $reportingConfig "imageVerify" -}} + {{- end -}} + {{- with .generate -}} + {{- $reportingConfig = append $reportingConfig "generate" -}} + {{- end -}} + {{- $flags = append $flags (print "--enableReporting=" (join "," $reportingConfig)) -}} +{{- end -}} {{- with $flags -}} {{- toYaml . -}} {{- end -}} diff --git a/charts/kyverno/templates/admission-controller/deployment.yaml b/charts/kyverno/templates/admission-controller/deployment.yaml index a8ab117c93..5326793322 100644 --- a/charts/kyverno/templates/admission-controller/deployment.yaml +++ b/charts/kyverno/templates/admission-controller/deployment.yaml @@ -174,6 +174,7 @@ spec: - --imagePullSecrets={{- join "," (concat (keys .Values.imagePullSecrets) .Values.existingImagePullSecrets) }} {{- end }} {{- include "kyverno.features.flags" (pick (mergeOverwrite .Values.features .Values.admissionController.featuresOverride) + "reporting" "admissionReports" "autoUpdateWebhooks" "configMapCaching" diff --git a/charts/kyverno/templates/background-controller/deployment.yaml b/charts/kyverno/templates/background-controller/deployment.yaml index 8a4355b7b6..f4025c8587 100644 --- a/charts/kyverno/templates/background-controller/deployment.yaml +++ b/charts/kyverno/templates/background-controller/deployment.yaml @@ -119,7 +119,7 @@ spec: - --imagePullSecrets={{- join "," (concat (keys .Values.imagePullSecrets) .Values.existingImagePullSecrets) }} {{- end }} {{- include "kyverno.features.flags" (pick (mergeOverwrite .Values.features .Values.backgroundController.featuresOverride) - "backgroundReports" + "reporting" "configMapCaching" "deferredLoading" "globalContext" diff --git a/charts/kyverno/templates/reports-controller/deployment.yaml b/charts/kyverno/templates/reports-controller/deployment.yaml index c28060f74a..5929a81df6 100644 --- a/charts/kyverno/templates/reports-controller/deployment.yaml +++ b/charts/kyverno/templates/reports-controller/deployment.yaml @@ -119,6 +119,7 @@ spec: - --imagePullSecrets={{- join "," (concat (keys .Values.imagePullSecrets) .Values.existingImagePullSecrets) }} {{- end }} {{- include "kyverno.features.flags" (pick (mergeOverwrite .Values.features .Values.reportsController.featuresOverride) + "reporting" "admissionReports" "aggregateReports" "policyReports" diff --git a/charts/kyverno/values.yaml b/charts/kyverno/values.yaml index 545e517e03..7a8e518127 100644 --- a/charts/kyverno/values.yaml +++ b/charts/kyverno/values.yaml @@ -636,9 +636,17 @@ features: validatingAdmissionPolicyReports: # -- Enables the feature enabled: false - backgroundReports: + reporting: # -- Enables the feature - enabled: true + validate: true + # -- Enables the feature + mutate: true + # -- Enables the feature + mutateExisting: true + # -- Enables the feature + imageVerify: true + # -- Enables the feature + generate: true autoUpdateWebhooks: # -- Enables the feature enabled: true diff --git a/cmd/background-controller/main.go b/cmd/background-controller/main.go index 57505082f4..6abbd2af22 100644 --- a/cmd/background-controller/main.go +++ b/cmd/background-controller/main.go @@ -29,6 +29,7 @@ import ( "github.com/kyverno/kyverno/pkg/policy" "github.com/kyverno/kyverno/pkg/utils/generator" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" apiserver "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" kubeinformers "k8s.io/client-go/informers" kyamlopenapi "sigs.k8s.io/kustomize/kyaml/openapi" @@ -55,7 +56,7 @@ func createrLeaderControllers( jp jmespath.Interface, backgroundScanInterval time.Duration, urGenerator generator.UpdateRequestGenerator, - backgroundReports bool, + reportsConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) ([]internal.Controller, error) { policyCtrl, err := policy.NewPolicyController( @@ -88,7 +89,7 @@ func createrLeaderControllers( eventGenerator, configuration, jp, - backgroundReports, + reportsConfig, reportsBreaker, ) return []internal.Controller{ @@ -103,7 +104,6 @@ func main() { maxQueuedEvents int omitEvents string maxAPICallResponseLength int64 - backgroundReports bool maxBackgroundReports int ) flagset := flag.NewFlagSet("updaterequest-controller", flag.ExitOnError) @@ -112,7 +112,6 @@ func main() { flagset.StringVar(&omitEvents, "omitEvents", "", "Set this flag to a comma sperated list of PolicyViolation, PolicyApplied, PolicyError, PolicySkipped to disable events, e.g. --omitEvents=PolicyApplied,PolicyViolation") flagset.Int64Var(&maxAPICallResponseLength, "maxAPICallResponseLength", 2*1000*1000, "Maximum allowed response size from API Calls. A value of 0 bypasses checks (not recommended).") flagset.IntVar(&maxBackgroundReports, "maxBackgroundReports", 10000, "Maximum number of ephemeralreports created for the background policies.") - flagset.BoolVar(&backgroundReports, "backgroundReports", true, "Enables or disables reports for mutate existing and generate rules.") // config appConfig := internal.NewConfiguration( @@ -132,6 +131,7 @@ func main() { internal.WithApiServerClient(), internal.WithMetadataClient(), internal.WithFlagSets(flagset), + internal.WithReporting(), ) // parse flags internal.ParseFlags(appConfig) @@ -252,7 +252,7 @@ func main() { setup.Jp, bgscanInterval, urGenerator, - backgroundReports, + setup.ReportingConfiguration, reportsBreaker, ) if err != nil { diff --git a/cmd/internal/config.go b/cmd/internal/config.go index be92d66aee..d105f85cea 100644 --- a/cmd/internal/config.go +++ b/cmd/internal/config.go @@ -22,6 +22,7 @@ type Configuration interface { UsesMetadataClient() bool UsesKyvernoDynamicClient() bool UsesEventsClient() bool + UsesReporting() bool FlagSets() []*flag.FlagSet } @@ -145,6 +146,12 @@ func WithFlagSets(flagsets ...*flag.FlagSet) ConfigurationOption { } } +func WithReporting() ConfigurationOption { + return func(c *configuration) { + c.usesReporting = true + } +} + type configuration struct { usesMetrics bool usesTracing bool @@ -163,6 +170,7 @@ type configuration struct { usesMetadataClient bool usesKyvernoDynamicClient bool usesEventsClient bool + usesReporting bool flagSets []*flag.FlagSet } @@ -234,6 +242,10 @@ func (c *configuration) UsesEventsClient() bool { return c.usesEventsClient } +func (c *configuration) UsesReporting() bool { + return c.usesReporting +} + func (c *configuration) FlagSets() []*flag.FlagSet { return c.flagSets } diff --git a/cmd/internal/flag.go b/cmd/internal/flag.go index cc13f6c958..18908c1b4d 100644 --- a/cmd/internal/flag.go +++ b/cmd/internal/flag.go @@ -59,6 +59,8 @@ var ( imageVerifyCacheMaxSize int64 // global context enableGlobalContext bool + // reporting + enableReporting string ) func initLoggingFlags() { @@ -137,6 +139,10 @@ func initCleanupFlags() { flag.StringVar(&cleanupServerPort, "cleanupServerPort", "9443", "kyverno cleanup server port, defaults to '9443'.") } +func initReportingFlags() { + flag.StringVar(&enableReporting, "enableReporting", "validate,mutate,mutateExisting,generate,imageVerify", "Comma separated list to enables reporting for different rule types. (validate,mutate,mutateExisting,generate,imageVerify)") +} + type options struct { clientRateLimitQPS float64 clientRateLimitBurst int @@ -220,6 +226,11 @@ func initFlags(config Configuration, opts ...Option) { if config.UsesLeaderElection() { initLeaderElectionFlags() } + // reporting + if config.UsesReporting() { + initReportingFlags() + } + initCleanupFlags() for _, flagset := range config.FlagSets() { flagset.VisitAll(func(f *flag.Flag) { diff --git a/cmd/internal/reporting.go b/cmd/internal/reporting.go new file mode 100644 index 0000000000..3287d201c2 --- /dev/null +++ b/cmd/internal/reporting.go @@ -0,0 +1,15 @@ +package internal + +import ( + "strings" + + "github.com/go-logr/logr" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" +) + +func setupReporting(logger logr.Logger) reportutils.ReportingConfiguration { + logger = logger.WithName("setup-reporting").WithValues("enableReporting", enableReporting) + cfg := reportutils.NewReportingConfig(strings.Split(enableReporting, ",")...) + logger.Info("setting up reporting...", "validate", cfg.ValidateReportsEnabled(), "mutate", cfg.MutateReportsEnabled(), "mutateExisiting", cfg.MutateExistingReportsEnabled(), "imageVerify", cfg.ImageVerificationReportsEnabled(), "generate", cfg.GenerateReportsEnabled()) + return cfg +} diff --git a/cmd/internal/setup.go b/cmd/internal/setup.go index 040ad51df3..5cb70b048a 100644 --- a/cmd/internal/setup.go +++ b/cmd/internal/setup.go @@ -16,6 +16,7 @@ import ( "github.com/kyverno/kyverno/pkg/imageverifycache" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/registryclient" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" eventsv1 "k8s.io/client-go/kubernetes/typed/events/v1" corev1listers "k8s.io/client-go/listers/core/v1" ) @@ -48,6 +49,7 @@ type SetupResult struct { MetadataClient metadataclient.UpstreamInterface KyvernoDynamicClient dclient.Interface EventsClient eventsv1.EventsV1Interface + ReportingConfiguration reportutils.ReportingConfiguration } func Setup(config Configuration, name string, skipResourceFilters bool) (context.Context, SetupResult, context.CancelFunc) { @@ -105,6 +107,10 @@ func Setup(config Configuration, name string, skipResourceFilters bool) (context if config.UsesMetadataClient() { metadataClient = createMetadataClient(logger, metadataclient.WithMetrics(metricsManager, metrics.MetadataClient), metadataclient.WithTracing()) } + var reportingConfig reportutils.ReportingConfiguration + if config.UsesReporting() { + reportingConfig = setupReporting(logger) + } return ctx, SetupResult{ Logger: logger, @@ -123,6 +129,7 @@ func Setup(config Configuration, name string, skipResourceFilters bool) (context MetadataClient: metadataClient, KyvernoDynamicClient: dClient, EventsClient: eventsClient, + ReportingConfiguration: reportingConfig, }, shutdown(logger.WithName("shutdown"), sdownMaxProcs, sdownMetrics, sdownTracing, sdownSignals) } diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index fc3c9d9e04..ec495aa697 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -320,6 +320,7 @@ func main() { internal.WithApiServerClient(), internal.WithMetadataClient(), internal.WithFlagSets(flagset), + internal.WithReporting(), ) // parse flags internal.ParseFlags(appConfig) @@ -578,6 +579,7 @@ func main() { setup.Jp, maxAuditWorkers, maxAuditCapacity, + setup.ReportingConfiguration, reportsBreaker, ) exceptionHandlers := webhooksexception.NewHandlers(exception.ValidationOptions{ diff --git a/cmd/reports-controller/main.go b/cmd/reports-controller/main.go index 5096ef26a3..c761a312cf 100644 --- a/cmd/reports-controller/main.go +++ b/cmd/reports-controller/main.go @@ -27,6 +27,7 @@ import ( "github.com/kyverno/kyverno/pkg/leaderelection" "github.com/kyverno/kyverno/pkg/logging" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" "github.com/kyverno/kyverno/pkg/validatingadmissionpolicy" apiserver "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" kubeinformers "k8s.io/client-go/informers" @@ -66,6 +67,7 @@ func createReportControllers( configuration config.Configuration, jp jmespath.Interface, eventGenerator event.Interface, + reportsConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) ([]internal.Controller, func(context.Context) error) { var ctrls []internal.Controller @@ -126,6 +128,7 @@ func createReportControllers( jp, eventGenerator, policyReports, + reportsConfig, reportsBreaker, ) ctrls = append(ctrls, internal.NewController( @@ -149,6 +152,7 @@ func createrLeaderControllers( eng engineapi.Engine, backgroundScan bool, admissionReports bool, + reportsConfig reportutils.ReportingConfiguration, aggregateReports bool, policyReports bool, validatingAdmissionPolicyReports bool, @@ -183,6 +187,7 @@ func createrLeaderControllers( configuration, jp, eventGenerator, + reportsConfig, reportsBreaker, ) return reportControllers, warmup, nil @@ -238,6 +243,7 @@ func main() { internal.WithEventsClient(), internal.WithApiServerClient(), internal.WithFlagSets(flagset), + internal.WithReporting(), ) // parse flags internal.ParseFlags( @@ -350,6 +356,7 @@ func main() { engine, backgroundScan, admissionReports, + setup.ReportingConfiguration, aggregateReports, policyReports, validatingAdmissionPolicyReports, diff --git a/config/install-latest-testing.yaml b/config/install-latest-testing.yaml index 008bf56185..e8e5a263d3 100644 --- a/config/install-latest-testing.yaml +++ b/config/install-latest-testing.yaml @@ -50737,6 +50737,7 @@ spec: - --protectManagedResources=false - --allowInsecureRegistry=false - --registryCredentialHelpers=default,google,amazon,azure,github + - --enableReporting=validate,mutate,mutateExisting,imageVerify,generate resources: limits: @@ -50881,7 +50882,6 @@ spec: - --disableMetrics=false - --otelConfig=prometheus - --metricsPort=8000 - - --backgroundReports=true - --enableConfigMapCaching=true - --enableDeferredLoading=true - --maxAPICallResponseLength=2000000 @@ -50889,6 +50889,7 @@ spec: - --v=2 - --omitEvents=PolicyApplied,PolicySkipped - --enablePolicyException=true + - --enableReporting=validate,mutate,mutateExisting,imageVerify,generate env: - name: KYVERNO_SERVICEACCOUNT_NAME @@ -51142,6 +51143,7 @@ spec: - --enablePolicyException=true - --allowInsecureRegistry=false - --registryCredentialHelpers=default,google,amazon,azure,github + - --enableReporting=validate,mutate,mutateExisting,imageVerify,generate env: - name: KYVERNO_SERVICEACCOUNT_NAME diff --git a/pkg/background/generate/controller.go b/pkg/background/generate/controller.go index 0012674344..9b7eb0f07c 100644 --- a/pkg/background/generate/controller.go +++ b/pkg/background/generate/controller.go @@ -58,8 +58,8 @@ type GenerateController struct { log logr.Logger jp jmespath.Interface - backgroundReports bool - reportsBreaker breaker.Breaker + reportsConfig reportutils.ReportingConfiguration + reportsBreaker breaker.Breaker } // NewGenerateController returns an instance of the Generate-Request Controller @@ -76,24 +76,24 @@ func NewGenerateController( eventGen event.Interface, log logr.Logger, jp jmespath.Interface, - backgroundReports bool, + reportsConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) *GenerateController { c := GenerateController{ - client: client, - kyvernoClient: kyvernoClient, - statusControl: statusControl, - engine: engine, - policyLister: policyLister, - npolicyLister: npolicyLister, - urLister: urLister, - nsLister: nsLister, - configuration: dynamicConfig, - eventGen: eventGen, - log: log, - jp: jp, - backgroundReports: backgroundReports, - reportsBreaker: reportsBreaker, + client: client, + kyvernoClient: kyvernoClient, + statusControl: statusControl, + engine: engine, + policyLister: policyLister, + npolicyLister: npolicyLister, + urLister: urLister, + nsLister: nsLister, + configuration: dynamicConfig, + eventGen: eventGen, + log: log, + jp: jp, + reportsConfig: reportsConfig, + reportsBreaker: reportsBreaker, } return &c } @@ -236,7 +236,7 @@ func (c *GenerateController) applyGenerate(trigger unstructured.Unstructured, ur logger.V(4).Info(doesNotApply) return nil, errors.New(doesNotApply) } - if c.needsReports(trigger, c.backgroundReports) { + if c.needsReports(trigger) { if err := c.createReports(context.TODO(), policyContext.NewResource(), engineResponse); err != nil { c.log.Error(err, "failed to create report") } @@ -373,8 +373,8 @@ func (c *GenerateController) GetUnstrResource(genResourceSpec kyvernov1.Resource return resource, nil } -func (c *GenerateController) needsReports(trigger unstructured.Unstructured, backgroundReports bool) bool { - createReport := backgroundReports +func (c *GenerateController) needsReports(trigger unstructured.Unstructured) bool { + createReport := c.reportsConfig.GenerateReportsEnabled() // check if the resource supports reporting if !reportutils.IsGvkSupported(trigger.GroupVersionKind()) { createReport = false diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go index a6ec5d864f..6933bbd9e6 100644 --- a/pkg/background/mutate/mutate.go +++ b/pkg/background/mutate/mutate.go @@ -47,8 +47,8 @@ type mutateExistingController struct { log logr.Logger jp jmespath.Interface - backgroundReports bool - reportsBreaker breaker.Breaker + reportsConfig reportutils.ReportingConfiguration + reportsBreaker breaker.Breaker } // NewMutateExistingController returns an instance of the MutateExistingController @@ -64,23 +64,23 @@ func NewMutateExistingController( eventGen event.Interface, log logr.Logger, jp jmespath.Interface, - backgroundReports bool, + reportsConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) *mutateExistingController { c := mutateExistingController{ - client: client, - kyvernoClient: kyvernoClient, - statusControl: statusControl, - engine: engine, - policyLister: policyLister, - npolicyLister: npolicyLister, - nsLister: nsLister, - configuration: dynamicConfig, - eventGen: eventGen, - log: log, - jp: jp, - backgroundReports: backgroundReports, - reportsBreaker: reportsBreaker, + client: client, + kyvernoClient: kyvernoClient, + statusControl: statusControl, + engine: engine, + policyLister: policyLister, + npolicyLister: npolicyLister, + nsLister: nsLister, + configuration: dynamicConfig, + eventGen: eventGen, + log: log, + jp: jp, + reportsConfig: reportsConfig, + reportsBreaker: reportsBreaker, } return &c } @@ -167,7 +167,7 @@ func (c *mutateExistingController) ProcessUR(ur *kyvernov2.UpdateRequest) error } er := c.engine.Mutate(context.TODO(), policyContext) - if c.needsReports(trigger, c.backgroundReports) { + if c.needsReports(trigger) { if err := c.createReports(context.TODO(), policyContext.NewResource(), er); err != nil { c.log.Error(err, "failed to create report") } @@ -258,8 +258,8 @@ func (c *mutateExistingController) report(err error, policy kyvernov1.PolicyInte c.eventGen.Add(events...) } -func (c *mutateExistingController) needsReports(trigger *unstructured.Unstructured, backgroundReports bool) bool { - createReport := backgroundReports +func (c *mutateExistingController) needsReports(trigger *unstructured.Unstructured) bool { + createReport := c.reportsConfig.MutateExistingReportsEnabled() if trigger == nil { return createReport } diff --git a/pkg/background/update_request_controller.go b/pkg/background/update_request_controller.go index d4d10f87bc..60bd3ace0c 100644 --- a/pkg/background/update_request_controller.go +++ b/pkg/background/update_request_controller.go @@ -21,6 +21,7 @@ import ( engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/jmespath" "github.com/kyverno/kyverno/pkg/event" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/runtime" @@ -58,11 +59,11 @@ type controller struct { // queue queue workqueue.TypedRateLimitingInterface[any] - eventGen event.Interface - configuration config.Configuration - jp jmespath.Interface - backgroundReports bool - reportsBreaker breaker.Breaker + eventGen event.Interface + configuration config.Configuration + jp jmespath.Interface + reportsConfig reportutils.ReportingConfiguration + reportsBreaker breaker.Breaker } // NewController returns an instance of the Generate-Request Controller @@ -77,24 +78,24 @@ func NewController( eventGen event.Interface, configuration config.Configuration, jp jmespath.Interface, - backgroundReports bool, + reportsConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) Controller { urLister := urInformer.Lister().UpdateRequests(config.KyvernoNamespace()) c := controller{ - client: client, - kyvernoClient: kyvernoClient, - engine: engine, - cpolLister: cpolInformer.Lister(), - polLister: polInformer.Lister(), - urLister: urLister, - nsLister: namespaceInformer.Lister(), - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any](), "background"), - eventGen: eventGen, - configuration: configuration, - jp: jp, - backgroundReports: backgroundReports, - reportsBreaker: reportsBreaker, + client: client, + kyvernoClient: kyvernoClient, + engine: engine, + cpolLister: cpolInformer.Lister(), + polLister: polInformer.Lister(), + urLister: urLister, + nsLister: namespaceInformer.Lister(), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any](), "background"), + eventGen: eventGen, + configuration: configuration, + jp: jp, + reportsConfig: reportsConfig, + reportsBreaker: reportsBreaker, } _, _ = urInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: c.addUR, @@ -227,10 +228,10 @@ func (c *controller) processUR(ur *kyvernov2.UpdateRequest) error { statusControl := common.NewStatusControl(c.kyvernoClient, c.urLister) switch ur.Spec.GetRequestType() { case kyvernov2.Mutate: - ctrl := mutate.NewMutateExistingController(c.client, c.kyvernoClient, statusControl, c.engine, c.cpolLister, c.polLister, c.nsLister, c.configuration, c.eventGen, logger, c.jp, c.backgroundReports, c.reportsBreaker) + ctrl := mutate.NewMutateExistingController(c.client, c.kyvernoClient, statusControl, c.engine, c.cpolLister, c.polLister, c.nsLister, c.configuration, c.eventGen, logger, c.jp, c.reportsConfig, c.reportsBreaker) return ctrl.ProcessUR(ur) case kyvernov2.Generate: - ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.engine, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger, c.jp, c.backgroundReports, c.reportsBreaker) + ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.engine, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger, c.jp, c.reportsConfig, c.reportsBreaker) return ctrl.ProcessUR(ur) } return nil diff --git a/pkg/controllers/report/background/controller.go b/pkg/controllers/report/background/controller.go index 5ea6896a13..8f543c58c6 100644 --- a/pkg/controllers/report/background/controller.go +++ b/pkg/controllers/report/background/controller.go @@ -78,6 +78,7 @@ type controller struct { jp jmespath.Interface eventGen event.Interface policyReports bool + reportsConfig reportutils.ReportingConfiguration breaker breaker.Breaker } @@ -98,6 +99,7 @@ func NewController( jp jmespath.Interface, eventGen event.Interface, policyReports bool, + reportsConfig reportutils.ReportingConfiguration, breaker breaker.Breaker, ) controllers.Controller { ephrInformer := metadataFactory.ForResource(reportsv1.SchemeGroupVersion.WithResource("ephemeralreports")) @@ -120,6 +122,7 @@ func NewController( jp: jp, eventGen: eventGen, policyReports: policyReports, + reportsConfig: reportsConfig, breaker: breaker, } if vapInformer != nil { @@ -422,7 +425,7 @@ func (c *controller) reconcileReport( } } if full || reevaluate || actual[reportutils.PolicyLabel(policy)] != policy.GetResourceVersion() { - scanner := utils.NewScanner(logger, c.engine, c.config, c.jp, c.client) + scanner := utils.NewScanner(logger, c.engine, c.config, c.jp, c.client, c.reportsConfig) for _, result := range scanner.ScanResource(ctx, *target, nsLabels, bindings, policy) { if result.Error != nil { return result.Error diff --git a/pkg/controllers/report/utils/scanner.go b/pkg/controllers/report/utils/scanner.go index 16aee57a33..6cdcf38952 100644 --- a/pkg/controllers/report/utils/scanner.go +++ b/pkg/controllers/report/utils/scanner.go @@ -11,6 +11,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/jmespath" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" "github.com/kyverno/kyverno/pkg/validatingadmissionpolicy" "go.uber.org/multierr" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" @@ -18,11 +19,12 @@ import ( ) type scanner struct { - logger logr.Logger - engine engineapi.Engine - config config.Configuration - jp jmespath.Interface - client dclient.Interface + logger logr.Logger + engine engineapi.Engine + config config.Configuration + jp jmespath.Interface + client dclient.Interface + reportingConfig reportutils.ReportingConfiguration } type ScanResult struct { @@ -40,13 +42,15 @@ func NewScanner( config config.Configuration, jp jmespath.Interface, client dclient.Interface, + reportingConfig reportutils.ReportingConfiguration, ) Scanner { return &scanner{ - logger: logger, - engine: engine, - config: config, - jp: jp, - client: client, + logger: logger, + engine: engine, + config: config, + jp: jp, + client: client, + reportingConfig: reportingConfig, } } @@ -59,13 +63,15 @@ func (s *scanner) ScanResource(ctx context.Context, resource unstructured.Unstru if policy.GetType() == engineapi.KyvernoPolicyType { var err error pol := policy.AsKyvernoPolicy() - response, err = s.validateResource(ctx, resource, nsLabels, pol) - if err != nil { - logger.Error(err, "failed to scan resource") - errors = append(errors, err) + if s.reportingConfig.ValidateReportsEnabled() { + response, err = s.validateResource(ctx, resource, nsLabels, pol) + if err != nil { + logger.Error(err, "failed to scan resource") + errors = append(errors, err) + } } spec := pol.GetSpec() - if spec.HasVerifyImages() && len(errors) == 0 { + if spec.HasVerifyImages() && len(errors) == 0 && s.reportingConfig.ImageVerificationReportsEnabled() { if response != nil { // remove responses of verify image rules ruleResponses := make([]engineapi.RuleResponse, 0, len(response.PolicyResponse.Rules)) diff --git a/pkg/utils/report/config.go b/pkg/utils/report/config.go new file mode 100644 index 0000000000..a4eda9c42f --- /dev/null +++ b/pkg/utils/report/config.go @@ -0,0 +1,41 @@ +package report + +import "k8s.io/apimachinery/pkg/util/sets" + +type reportingConfig struct { + helper sets.Set[string] +} + +func (r *reportingConfig) ValidateReportsEnabled() bool { + return r.helper.Has("validate") +} + +func (r *reportingConfig) MutateReportsEnabled() bool { + return r.helper.Has("mutate") +} + +func (r *reportingConfig) MutateExistingReportsEnabled() bool { + return r.helper.Has("mutateExisting") +} + +func (r *reportingConfig) ImageVerificationReportsEnabled() bool { + return r.helper.Has("imageVerify") +} + +func (r *reportingConfig) GenerateReportsEnabled() bool { + return r.helper.Has("generate") +} + +func NewReportingConfig(items ...string) ReportingConfiguration { + return &reportingConfig{ + helper: sets.New(items...), + } +} + +type ReportingConfiguration interface { + ValidateReportsEnabled() bool + MutateReportsEnabled() bool + MutateExistingReportsEnabled() bool + ImageVerificationReportsEnabled() bool + GenerateReportsEnabled() bool +} diff --git a/pkg/webhooks/resource/fake.go b/pkg/webhooks/resource/fake.go index 8dd04bdc61..51f7fbe677 100644 --- a/pkg/webhooks/resource/fake.go +++ b/pkg/webhooks/resource/fake.go @@ -19,6 +19,7 @@ import ( "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/policycache" "github.com/kyverno/kyverno/pkg/registryclient" + "github.com/kyverno/kyverno/pkg/utils/report" "github.com/kyverno/kyverno/pkg/webhooks/updaterequest" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" kubeinformers "k8s.io/client-go/informers" @@ -45,16 +46,17 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) *resour rclient := registryclient.NewOrDie() return &resourceHandlers{ - client: dclient, - configuration: configuration, - metricsConfig: metricsConfig, - pCache: policyCache, - nsLister: informers.Core().V1().Namespaces().Lister(), - urLister: urLister, - urGenerator: updaterequest.NewFake(), - eventGen: event.NewFake(), - pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), - auditPool: pond.New(8, 1000), + client: dclient, + configuration: configuration, + metricsConfig: metricsConfig, + pCache: policyCache, + nsLister: informers.Core().V1().Namespaces().Lister(), + urLister: urLister, + urGenerator: updaterequest.NewFake(), + eventGen: event.NewFake(), + pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, jp), + auditPool: pond.New(8, 1000), + reportingConfig: report.NewReportingConfig("validate", "mutate", "mutateExisiting", "generate", "imageVerify"), engine: engine.NewEngine( configuration, config.NewDefaultMetricsConfiguration(), diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index fe67c6b28b..589b20e1bd 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -27,6 +27,7 @@ import ( admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" + reportutils "github.com/kyverno/kyverno/pkg/utils/report" "github.com/kyverno/kyverno/pkg/webhooks" "github.com/kyverno/kyverno/pkg/webhooks/handlers" "github.com/kyverno/kyverno/pkg/webhooks/resource/imageverification" @@ -65,6 +66,7 @@ type resourceHandlers struct { backgroundServiceAccountName string reportsServiceAccountName string auditPool *pond.WorkerPool + reportingConfig reportutils.ReportingConfiguration reportsBreaker breaker.Breaker } @@ -87,6 +89,7 @@ func NewHandlers( jp jmespath.Interface, maxAuditWorkers int, maxAuditCapacity int, + reportingConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) webhooks.ResourceHandlers { return &resourceHandlers{ @@ -107,6 +110,7 @@ func NewHandlers( backgroundServiceAccountName: backgroundServiceAccountName, reportsServiceAccountName: reportsServiceAccountName, auditPool: pond.New(maxAuditWorkers, maxAuditCapacity, pond.Strategy(pond.Lazy())), + reportingConfig: reportingConfig, reportsBreaker: reportsBreaker, } } @@ -138,6 +142,7 @@ func (h *resourceHandlers) Validate(ctx context.Context, logger logr.Logger, req h.metricsConfig, h.configuration, h.nsLister, + h.reportingConfig, h.reportsBreaker, ) var wg sync.WaitGroup @@ -200,7 +205,7 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque logger.Error(err, "failed to build policy context") return admissionutils.Response(request.UID, err) } - mh := mutation.NewMutationHandler(logger, h.kyvernoClient, h.engine, h.eventGen, h.nsLister, h.metricsConfig, h.admissionReports, h.reportsBreaker) + mh := mutation.NewMutationHandler(logger, h.kyvernoClient, h.engine, h.eventGen, h.nsLister, h.metricsConfig, h.admissionReports, h.reportingConfig, h.reportsBreaker) patches, warnings, err := mh.HandleMutation(ctx, request, mutatePolicies, policyContext, startTime, h.configuration) if err != nil { logger.Error(err, "mutation failed") @@ -222,6 +227,7 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque h.admissionReports, h.configuration, h.nsLister, + h.reportingConfig, h.reportsBreaker, ) imagePatches, imageVerifyWarnings, err := ivh.Handle(ctx, newRequest, verifyImagesPolicies, policyContext) diff --git a/pkg/webhooks/resource/imageverification/handler.go b/pkg/webhooks/resource/imageverification/handler.go index 4095374aa5..2b8adf0757 100644 --- a/pkg/webhooks/resource/imageverification/handler.go +++ b/pkg/webhooks/resource/imageverification/handler.go @@ -40,6 +40,7 @@ type imageVerificationHandler struct { admissionReports bool cfg config.Configuration nsLister corev1listers.NamespaceLister + reportConfig reportutils.ReportingConfiguration reportsBreaker breaker.Breaker } @@ -51,6 +52,7 @@ func NewImageVerificationHandler( admissionReports bool, cfg config.Configuration, nsLister corev1listers.NamespaceLister, + reportConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) ImageVerificationHandler { return &imageVerificationHandler{ @@ -61,6 +63,7 @@ func NewImageVerificationHandler( admissionReports: admissionReports, cfg: cfg, nsLister: nsLister, + reportConfig: reportConfig, reportsBreaker: reportsBreaker, } } @@ -161,6 +164,9 @@ func (v *imageVerificationHandler) handleAudit( engineResponses ...engineapi.EngineResponse, ) { createReport := v.admissionReports + if !v.reportConfig.ImageVerificationReportsEnabled() { + createReport = false + } if admissionutils.IsDryRun(request) { createReport = false } diff --git a/pkg/webhooks/resource/mutation/mutation.go b/pkg/webhooks/resource/mutation/mutation.go index 55735336c3..5a3b2ed939 100644 --- a/pkg/webhooks/resource/mutation/mutation.go +++ b/pkg/webhooks/resource/mutation/mutation.go @@ -44,6 +44,7 @@ func NewMutationHandler( nsLister corev1listers.NamespaceLister, metrics metrics.MetricsConfigManager, admissionReports bool, + reportsConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) MutationHandler { return &mutationHandler{ @@ -54,6 +55,7 @@ func NewMutationHandler( nsLister: nsLister, metrics: metrics, admissionReports: admissionReports, + reportsConfig: reportsConfig, reportsBreaker: reportsBreaker, } } @@ -66,6 +68,7 @@ type mutationHandler struct { nsLister corev1listers.NamespaceLister metrics metrics.MetricsConfigManager admissionReports bool + reportsConfig reportutils.ReportingConfiguration reportsBreaker breaker.Breaker } @@ -156,7 +159,7 @@ func (v *mutationHandler) applyMutations( v.eventGen.Add(events...) go func() { - if v.needsReports(request, policyContext.NewResource(), v.admissionReports) { + if v.needsReports(request, v.admissionReports) { if err := v.createReports(context.TODO(), policyContext.NewResource(), request, engineResponses...); err != nil { v.log.Error(err, "failed to create report") } diff --git a/pkg/webhooks/resource/mutation/utils.go b/pkg/webhooks/resource/mutation/utils.go index 4562239eee..0c4387e8bd 100644 --- a/pkg/webhooks/resource/mutation/utils.go +++ b/pkg/webhooks/resource/mutation/utils.go @@ -5,15 +5,17 @@ import ( reportutils "github.com/kyverno/kyverno/pkg/utils/report" "github.com/kyverno/kyverno/pkg/webhooks/handlers" admissionv1 "k8s.io/api/admission/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) -func (v *mutationHandler) needsReports(request handlers.AdmissionRequest, resource unstructured.Unstructured, admissionReport bool) bool { +func (v *mutationHandler) needsReports(request handlers.AdmissionRequest, admissionReport bool) bool { createReport := admissionReport if admissionutils.IsDryRun(request.AdmissionRequest) { createReport = false } + if !v.reportsConfig.MutateReportsEnabled() { + createReport = false + } // we don't need reports for deletions if request.Operation == admissionv1.Delete { createReport = false diff --git a/pkg/webhooks/resource/validation/utils.go b/pkg/webhooks/resource/validation/utils.go index cdf5e9d606..dba708e0f0 100644 --- a/pkg/webhooks/resource/validation/utils.go +++ b/pkg/webhooks/resource/validation/utils.go @@ -9,11 +9,14 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -func needsReports(request handlers.AdmissionRequest, resource unstructured.Unstructured, admissionReport bool) bool { +func needsReports(request handlers.AdmissionRequest, resource unstructured.Unstructured, admissionReport bool, reportConfig reportutils.ReportingConfiguration) bool { createReport := admissionReport if admissionutils.IsDryRun(request.AdmissionRequest) { createReport = false } + if !reportConfig.ValidateReportsEnabled() { + createReport = false + } // we don't need reports for deletions if request.Operation == admissionv1.Delete { createReport = false diff --git a/pkg/webhooks/resource/validation/validation.go b/pkg/webhooks/resource/validation/validation.go index 81b7a50706..374a95d893 100644 --- a/pkg/webhooks/resource/validation/validation.go +++ b/pkg/webhooks/resource/validation/validation.go @@ -46,6 +46,7 @@ func NewValidationHandler( metrics metrics.MetricsConfigManager, cfg config.Configuration, nsLister corev1listers.NamespaceLister, + reportConfig reportutils.ReportingConfiguration, reportsBreaker breaker.Breaker, ) ValidationHandler { return &validationHandler{ @@ -59,6 +60,7 @@ func NewValidationHandler( metrics: metrics, cfg: cfg, nsLister: nsLister, + reportConfig: reportConfig, reportsBreaker: reportsBreaker, } } @@ -74,6 +76,7 @@ type validationHandler struct { metrics metrics.MetricsConfigManager cfg config.Configuration nsLister corev1listers.NamespaceLister + reportConfig reportutils.ReportingConfiguration reportsBreaker breaker.Breaker } @@ -159,7 +162,7 @@ func (v *validationHandler) HandleValidationEnforce( } go func() { - if needsReports(request, policyContext.NewResource(), v.admissionReports) { + if needsReports(request, policyContext.NewResource(), v.admissionReports, v.reportConfig) { if err := v.createReports(context.TODO(), policyContext.NewResource(), request, engineResponses...); err != nil { v.log.Error(err, "failed to create report") } @@ -188,7 +191,7 @@ func (v *validationHandler) HandleValidationAudit( } var responses []engineapi.EngineResponse - needsReport := needsReports(request, policyContext.NewResource(), v.admissionReports) + needsReport := needsReports(request, policyContext.NewResource(), v.admissionReports, v.reportConfig) tracing.Span( context.Background(), "",