Skip to content

Commit

Permalink
make securityhub a sync target
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Jogeleit <frank.jogeleit@web.de>
  • Loading branch information
fjogeleit committed Sep 6, 2024
1 parent 5e123a4 commit 87e9f17
Show file tree
Hide file tree
Showing 29 changed files with 185 additions and 72 deletions.
2 changes: 1 addition & 1 deletion charts/policy-reporter/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: |
It creates Prometheus Metrics and can send rule validation events to different targets like Loki, Elasticsearch, Slack or Discord
type: application
version: 3.0.0-beta.11
version: 3.0.0-beta.12
appVersion: 3.0.0-beta

icon: https://github.com/kyverno/kyverno/raw/main/img/logo.png
Expand Down
5 changes: 3 additions & 2 deletions charts/policy-reporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Policy Reporter watches for PolicyReport Resources.
It creates Prometheus Metrics and can send rule validation events to different targets like Loki, Elasticsearch, Slack or Discord

![Version: 3.0.0-beta.11](https://img.shields.io/badge/Version-3.0.0--beta.11-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0-beta](https://img.shields.io/badge/AppVersion-3.0.0--beta-informational?style=flat-square)
![Version: 3.0.0-beta.12](https://img.shields.io/badge/Version-3.0.0--beta.12-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0-beta](https://img.shields.io/badge/AppVersion-3.0.0--beta-informational?style=flat-square)

## Documentation

Expand Down Expand Up @@ -350,7 +350,7 @@ Check the [Documentation](https://kyverno.github.io/policy-reporter/guide/02-get
| ui.image.registry | string | `"ghcr.io"` | Image registry |
| ui.image.repository | string | `"kyverno/policy-reporter-ui"` | Image repository |
| ui.image.pullPolicy | string | `"IfNotPresent"` | Image PullPolicy |
| ui.image.tag | string | `"2.0.0-beta.12"` | Image tag Defaults to `Chart.AppVersion` if omitted |
| ui.image.tag | string | `"2.0.0-beta.13"` | Image tag Defaults to `Chart.AppVersion` if omitted |
| ui.replicaCount | int | `1` | Deployment replica count |
| ui.tempDir | string | `"/tmp"` | Temporary Directory to persist session data for authentication |
| ui.logging.encoding | string | `"console"` | log encoding possible encodings are console and json |
Expand All @@ -372,6 +372,7 @@ Check the [Documentation](https://kyverno.github.io/policy-reporter/guide/02-get
| ui.oauth.clientSecret | string | `""` | OpenID Connect ClientSecret |
| ui.oauth.scopes | list | `[]` | OpenID Connect allowed Scopes |
| ui.oauth.secretRef | string | `""` | Provide OpenID Connect configuration via Secret supported keys: `provider`, `clientId`, `clientSecret` |
| ui.banner | string | `""` | optional banner text |
| ui.displayMode | string | `""` | DisplayMode dark/light uses the OS configured prefered color scheme as default |
| ui.customBoards | list | `[]` | Additional customizable dashboards |
| ui.sources | list | `[]` | source specific configurations |
Expand Down
1 change: 1 addition & 0 deletions charts/policy-reporter/configs/ui.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ server:

ui:
displayMode: {{ .Values.ui.displayMode }}
banner: {{ .Values.ui.banner }}

{{- $default := false -}}
{{- range .Values.ui.clusters }}
Expand Down
5 changes: 4 additions & 1 deletion charts/policy-reporter/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ ui:
pullPolicy: IfNotPresent
# -- (string) Image tag
# Defaults to `Chart.AppVersion` if omitted
tag: "2.0.0-beta.12"
tag: "2.0.0-beta.13"

# -- Deployment replica count
replicaCount: 1
Expand Down Expand Up @@ -816,6 +816,9 @@ ui:
# supported keys: `provider`, `clientId`, `clientSecret`
secretRef: ""

# -- optional banner text
banner: ""

# -- DisplayMode dark/light
# uses the OS configured prefered color scheme as default
displayMode: ""
Expand Down
1 change: 1 addition & 0 deletions pkg/config/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ func (r *Resolver) RegisterSendResultListener() {

r.resultListener.RegisterListener(listener.NewSendResultListener(targets))
r.resultListener.RegisterScopeListener(listener.NewSendScopeResultsListener(targets))
r.resultListener.RegisterSyncListener(listener.NewSendSyncResultsListener(targets))
}

// UnregisterSendResultListener resolver method
Expand Down
16 changes: 4 additions & 12 deletions pkg/listener/cleanup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package listener
import (
"context"

"github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2"
"github.com/kyverno/policy-reporter/pkg/helper"
"github.com/kyverno/policy-reporter/pkg/report"
"github.com/kyverno/policy-reporter/pkg/target"
)
Expand All @@ -13,17 +11,11 @@ const CleanUpListener = "cleanup_listener"

func NewCleanupListener(ctx context.Context, targets *target.Collection) report.PolicyReportListener {
return func(event report.LifecycleEvent) {
for _, handler := range targets.Clients() {
if event.Type != report.Deleted {
filtered := helper.Filter(event.PolicyReport.GetResults(), func(result v1alpha2.PolicyReportResult) bool {
return handler.Validate(event.PolicyReport, result)
})

if len(filtered) == 0 {
return
}
}
if event.Type == report.Added {
return
}

for _, handler := range targets.SyncClients() {
handler.CleanUp(ctx, event.PolicyReport)
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/listener/cleanup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

func Test_CleanupListener(t *testing.T) {
t.Run("Execute Cleanup Handler", func(t *testing.T) {
c := &client{}
c := &client{cleanup: true}

slistener := listener.NewCleanupListener(ctx, target.NewCollection(&target.Target{Client: c}))
slistener(report.LifecycleEvent{Type: report.Deleted, PolicyReport: preport1})
Expand Down
24 changes: 24 additions & 0 deletions pkg/listener/new_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ResultListener struct {
skipExisting bool
listener []report.PolicyReportResultListener
scopeListener []report.ScopeResultsListener
syncListener []report.SyncResultsListener
cache cache.Cache
startUp time.Time
}
Expand All @@ -36,6 +37,14 @@ func (l *ResultListener) UnregisterScopeListener() {
l.scopeListener = make([]report.ScopeResultsListener, 0)
}

func (l *ResultListener) RegisterSyncListener(listener report.SyncResultsListener) {
l.syncListener = append(l.syncListener, listener)
}

func (l *ResultListener) UnregisterSyncListener() {
l.syncListener = make([]report.SyncResultsListener, 0)
}

func (l *ResultListener) Listen(event report.LifecycleEvent) {
if event.Type != report.Added && event.Type != report.Updated {
l.cache.RemoveReport(event.PolicyReport.GetID())
Expand All @@ -49,6 +58,21 @@ func (l *ResultListener) Listen(event report.LifecycleEvent) {

listenerCount := len(l.listener)
scopeListenerCount := len(l.scopeListener)
syncListenerCount := len(l.syncListener)

if syncListenerCount > 0 {
wg := sync.WaitGroup{}
wg.Add(syncListenerCount)

for _, cb := range l.syncListener {
go func(callback report.SyncResultsListener) {
defer wg.Done()

callback(event.PolicyReport)
}(cb)
}
}

if listenerCount == 0 && scopeListenerCount == 0 {
l.cache.AddReport(event.PolicyReport)
return
Expand Down
12 changes: 10 additions & 2 deletions pkg/listener/send_result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type client struct {
validated bool
cleanupCalled bool
batchSend bool
cleanup bool
}

func (c *client) Send(result v1alpha2.PolicyReportResult) {
Expand Down Expand Up @@ -51,8 +52,15 @@ func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReport
c.Called = true
}

func (c *client) SupportsBatchSend() bool {
return c.batchSend
func (c *client) Type() target.ClientType {
if c.cleanup {
return target.SyncSend
}
if c.batchSend {
return target.BatchSend
}

return target.SingleSend
}

func Test_SendResultListener(t *testing.T) {
Expand Down
38 changes: 38 additions & 0 deletions pkg/listener/sync_results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package listener

import (
"sync"

"github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2"
"github.com/kyverno/policy-reporter/pkg/helper"
"github.com/kyverno/policy-reporter/pkg/report"
"github.com/kyverno/policy-reporter/pkg/target"
)

const SendSyncResults = "send_sync_results_listener"

func NewSendSyncResultsListener(targets *target.Collection) report.SyncResultsListener {
return func(rep v1alpha2.ReportInterface) {
clients := targets.SyncClients()
if len(clients) == 0 {
return
}

wg := &sync.WaitGroup{}
wg.Add(len(clients))

for _, t := range clients {
go func(target target.Client, re v1alpha2.ReportInterface) {
defer wg.Done()

filtered := helper.Filter(re.GetResults(), func(result v1alpha2.PolicyReportResult) bool {
return target.Validate(re, result)
})

target.BatchSend(re, filtered)
}(t, rep)
}

wg.Wait()
}
}
3 changes: 3 additions & 0 deletions pkg/report/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ type PolicyReportResultListener = func(v1alpha2.ReportInterface, v1alpha2.Policy
// ScopeResultsListener is called whenever a new PolicyReport with a single resource scope and new results comes in
type ScopeResultsListener = func(v1alpha2.ReportInterface, []v1alpha2.PolicyReportResult, bool)

// SyncResultsListener is called whenever a PolicyReport event comes in
type SyncResultsListener = func(v1alpha2.ReportInterface)

// PolicyReportClient watches for PolicyReport Events and executes registered callback
type PolicyReportClient interface {
// Run starts the informer and workerqueue
Expand Down
12 changes: 10 additions & 2 deletions pkg/target/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import (
"github.com/kyverno/policy-reporter/pkg/validate"
)

type ClientType = string

const (
SingleSend ClientType = "single"
BatchSend ClientType = "batch"
SyncSend ClientType = "sync"
)

// Client for a provided Target
type Client interface {
// Send the given Result to the configured Target
Expand All @@ -29,8 +37,8 @@ type Client interface {
MinimumPriority() string
// Sources of the Results which should send to this target, empty means all sources
Sources() []string
// SupportsBatchSend for the given target
SupportsBatchSend() bool
// Type for the given target
Type() ClientType
// CleanUp old results if supported by the target
CleanUp(context.Context, v1alpha2.ReportInterface)
}
Expand Down
9 changes: 7 additions & 2 deletions pkg/target/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,18 @@ func (c *Collection) Client(name string) Client {

func (c *Collection) SingleSendClients() []Client {
return helper.Filter(c.Clients(), func(c Client) bool {
return !c.SupportsBatchSend()
return c.Type() == SingleSend
})
}
func (c *Collection) SyncClients() []Client {
return helper.Filter(c.Clients(), func(c Client) bool {
return c.Type() == SyncSend
})
}

func (c *Collection) BatchSendClients() []Client {
return helper.Filter(c.Clients(), func(c Client) bool {
return c.SupportsBatchSend()
return c.Type() == BatchSend
})
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/target/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ func TestCollection(t *testing.T) {

t.Run("SingleSendClients only returns clients which do not support batch sending", func(t *testing.T) {
for _, c := range collection.SingleSendClients() {
assert.False(t, c.SupportsBatchSend())
assert.Equal(t, target.SingleSend, c.Type())
}
})

t.Run("BatchSendClients only returns clients which do support batch sending", func(t *testing.T) {
for _, c := range collection.BatchSendClients() {
assert.True(t, c.SupportsBatchSend())
assert.Equal(t, target.BatchSend, c.Type())
}
})
}
4 changes: 2 additions & 2 deletions pkg/target/discord/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ func (d *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}

func (d *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {}

func (d *client) SupportsBatchSend() bool {
return false
func (d *client) Type() target.ClientType {
return target.SingleSend
}

// NewClient creates a new loki.client to send Results to Discord
Expand Down
4 changes: 2 additions & 2 deletions pkg/target/elasticsearch/elasticsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}

func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {}

func (e *client) SupportsBatchSend() bool {
return false
func (e *client) Type() target.ClientType {
return target.SingleSend
}

// NewClient creates a new elasticsearch.client to send Results to Elasticsearch
Expand Down
4 changes: 2 additions & 2 deletions pkg/target/gcs/gcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ func (c *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}

func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {}

func (c *client) SupportsBatchSend() bool {
return false
func (c *client) Type() target.ClientType {
return target.SingleSend
}

// NewClient creates a new GCS.client to send Results to Google Cloud Storage.
Expand Down
4 changes: 2 additions & 2 deletions pkg/target/googlechat/googlechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}

func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {}

func (e *client) SupportsBatchSend() bool {
return false
func (e *client) Type() target.ClientType {
return target.SingleSend
}

// NewClient creates a new loki.client to send Results to Elasticsearch
Expand Down
4 changes: 2 additions & 2 deletions pkg/target/kinesis/kinesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ func (c *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}

func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {}

func (c *client) SupportsBatchSend() bool {
return false
func (c *client) Type() target.ClientType {
return target.SingleSend
}

// NewClient creates a new Kinesis.client to send Results to AWS Kinesis compatible source
Expand Down
4 changes: 2 additions & 2 deletions pkg/target/loki/loki.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ func (l *client) send(payload payload) {
http.ProcessHTTPResponse(l.Name(), resp, err)
}

func (l *client) SupportsBatchSend() bool {
return true
func (l *client) Type() target.ClientType {
return target.BatchSend
}

func (l *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}
Expand Down
4 changes: 2 additions & 2 deletions pkg/target/s3/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ func (c *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {}

func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {}

func (c *client) SupportsBatchSend() bool {
return false
func (c *client) Type() target.ClientType {
return target.SingleSend
}

// NewClient creates a new S3.client to send Results to S3.
Expand Down
Loading

0 comments on commit 87e9f17

Please sign in to comment.