1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 02:18:15 +00:00

feat: add helm configuration for reporting in different rules (#11376)

* feat: add helm configuration for reporting in different rules (forgot signoff)

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: linter and tests

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* feat: rename reporting.imageVerification

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

---------

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
Vishal Choudhary 2024-10-11 15:12:29 +05:30 committed by GitHub
parent fcb5cb1c1b
commit ec546e6fb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 268 additions and 106 deletions

View file

@ -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 |

View file

@ -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 -}}

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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) {

15
cmd/internal/reporting.go Normal file
View file

@ -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
}

View file

@ -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)
}

View file

@ -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{

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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
}

View file

@ -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(),

View file

@ -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)

View file

@ -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
}

View file

@ -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")
}

View file

@ -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

View file

@ -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

View file

@ -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(),
"",