Skip to content

Commit

Permalink
Support custom labels and annotations for blocked policy reports (#6)
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Jogeleit <frank.jogeleit@web.de>
  • Loading branch information
fjogeleit authored Oct 12, 2024
1 parent 70c6968 commit 5106362
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 40 deletions.
50 changes: 41 additions & 9 deletions plugins/kyverno/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/spf13/cobra"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
"k8s.io/client-go/tools/clientcmd"

"github.com/kyverno/policy-reporter/kyverno-plugin/pkg/config"
Expand Down Expand Up @@ -49,6 +50,8 @@ func newRunCMD() *cobra.Command {
return err
}

group := &errgroup.Group{}

if c.BlockReports.Enabled {
logger.Info("block reports enabled", zap.Int("resultsPerReport", c.BlockReports.Results.MaxPerReport))
eventClient, err := resolver.EventClient()
Expand Down Expand Up @@ -77,9 +80,20 @@ func newRunCMD() *cobra.Command {
leClient.RegisterOnStart(func(c context.Context) {
logger.Info("started leadership")

stop = make(chan struct{})
g := &errgroup.Group{}
g.Go(func() error {
return policyReportClient.UpdatePolicyReports(c)
})
g.Go(func() error {
return policyReportClient.UpdateClusterPolicyReports(c)
})

if err := g.Wait(); err != nil {
logger.Error("failed to update existing policy reports", zap.Error(err))
}

if err = eventClient.Run(c, stop); err != nil {
stop = make(chan struct{})
if err := eventClient.Run(c, stop); err != nil {
logger.Error("failed to run EventClient", zap.Error(err))
}
}).RegisterOnNew(func(currentID, lockID string) {
Expand All @@ -91,17 +105,35 @@ func newRunCMD() *cobra.Command {
close(stop)
})

go leClient.Run(cmd.Context())
group.Go(func() error {
leClient.Run(cmd.Context())
return nil
})
} else {
stop = make(chan struct{})
if err = eventClient.Run(cmd.Context(), stop); err != nil {
return err
}
group.Go(func() error {
g := &errgroup.Group{}
g.Go(func() error {
return policyReportClient.UpdatePolicyReports(cmd.Context())
})
g.Go(func() error {
return policyReportClient.UpdateClusterPolicyReports(cmd.Context())
})

if err := g.Wait(); err != nil {
logger.Error("failed to update existing policy reports", zap.Error(err))
}

return eventClient.Run(cmd.Context(), stop)
})
}
}

logger.Info("server starts", zap.Int("port", c.Server.Port))
return server.Start()
group.Go(func() error {
logger.Info("server starts", zap.Int("port", c.Server.Port))
return server.Start()
})

return group.Wait()
},
}

Expand Down
8 changes: 4 additions & 4 deletions plugins/kyverno/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,21 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.31.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 // indirect
k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094 // indirect
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
16 changes: 8 additions & 8 deletions plugins/kyverno/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw=
golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down Expand Up @@ -218,8 +218,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down Expand Up @@ -247,15 +247,15 @@ k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8=
k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo=
k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA=
k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094 h1:MErs8YA0abvOqJ8gIupA1Tz6PKXYUw34XsGlA7uSL1k=
k8s.io/kube-openapi v0.0.0-20241009091222-67ed5848f094/go.mod h1:7ioBJr1A6igWjsR2fxq2EZ0mlMwYLejazSIc2bzMp2U=
k8s.io/pod-security-admission v0.31.1 h1:j++ISpfQU0mWpKhoS4tY06Wm5EKdn65teL4lPJhEMIM=
k8s.io/pod-security-admission v0.31.1/go.mod h1:0aE5T6MGm/50Nr/diBrC6+wwpxsT2E7NECe+TepUuEg=
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI=
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
Expand Down
14 changes: 10 additions & 4 deletions plugins/kyverno/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ type LeaderElection struct {
Enabled bool `mapstructure:"enabled"`
}

type PolicyReport struct {
Labels map[string]string `mapstructure:"labels"`
Annotations map[string]string `mapstructure:"annotations"`
}

type BlockReports struct {
Enabled bool `mapstructure:"enabled"`
Results Results `mapstructure:"results"`
Source string `mapstructure:"source"`
EventNamespace string `mapstructure:"eventNamespace"`
Enabled bool `mapstructure:"enabled"`
Results Results `mapstructure:"results"`
Source string `mapstructure:"source"`
EventNamespace string `mapstructure:"eventNamespace"`
PolicyReport PolicyReport `mapstructure:"policyReport"`
}

type CoreAPI struct {
Expand Down
3 changes: 2 additions & 1 deletion plugins/kyverno/pkg/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import (
)

func Load(c *Config, cfgFile string) error {
v := viper.New()
v := viper.NewWithOptions(viper.KeyDelimiter("!"))

if cfgFile != "" {
v.SetConfigFile(cfgFile)
} else {
v.AddConfigPath(".")
v.SetConfigName("config")
v.AllKeys()
}

v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
Expand Down
10 changes: 10 additions & 0 deletions plugins/kyverno/pkg/config/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,21 @@ func (r *Resolver) PolicyReportClient() (policyreport.Client, error) {
return nil, err
}

if r.config.BlockReports.PolicyReport.Labels == nil {
r.config.BlockReports.PolicyReport.Labels = make(map[string]string, 0)
}

if r.config.BlockReports.PolicyReport.Annotations == nil {
r.config.BlockReports.PolicyReport.Annotations = make(map[string]string, 0)
}

policyreportClient := policyreport.NewClient(
client,
r.config.BlockReports.Results.MaxPerReport,
r.config.BlockReports.Source,
r.config.BlockReports.Results.KeepOnlyLatest,
r.config.BlockReports.PolicyReport.Labels,
r.config.BlockReports.PolicyReport.Annotations,
)

r.polrClient = policyreportClient
Expand Down
101 changes: 95 additions & 6 deletions plugins/kyverno/pkg/kubernetes/policyreport/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"fmt"
"time"

"go.uber.org/zap"
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/kyverno/policy-reporter/kyverno-plugin/pkg/crd/api/policyreport/v1alpha2"
Expand All @@ -22,6 +24,8 @@ type policyReportClient struct {
maxResults int
source string
keepOnlyLatest bool
labels map[string]string
annotations map[string]string
}

func (p *policyReportClient) ProcessViolation(ctx context.Context, violation violation.PolicyViolation) error {
Expand All @@ -32,14 +36,92 @@ func (p *policyReportClient) ProcessViolation(ctx context.Context, violation vio
return p.handleNamespaced(ctx, violation, violation.Resource.Namespace)
}

func updateReport[T v1alpha2.ReportInterface](report T, labels, annotations map[string]string) (T, bool) {
update := false

rLabels := report.GetLabels()
if rLabels == nil {
rLabels = make(map[string]string)
report.SetLabels(rLabels)
}

rAnnotations := report.GetAnnotations()
if rAnnotations == nil {
rAnnotations = make(map[string]string)
report.SetAnnotations(rAnnotations)
}

for l, v := range labels {
if rLabels[l] != v {
rLabels[l] = v
update = true
}
}

for l, v := range rLabels {
if labels[l] != v {
delete(rLabels, l)
update = true
}
}

for a, v := range annotations {
if rAnnotations[a] != v {
rAnnotations[a] = v
update = true
}
}

for a, v := range rAnnotations {
if annotations[a] != v {
delete(rAnnotations, a)
update = true
}
}

return report, update
}

func (p *policyReportClient) UpdatePolicyReports(ctx context.Context) error {
labelSelector := metav1.FormatLabelSelector(&metav1.LabelSelector{MatchLabels: reportLabels})

list, err := p.client.PolicyReports("").List(ctx, v1.ListOptions{LabelSelector: labelSelector})
for _, r := range list.Items {
report, update := updateReport(&r, p.labels, p.annotations)

if update {
zap.L().Info("policy report updated", zap.String("name", report.Name))
p.client.PolicyReports(report.Namespace).Update(ctx, report, v1.UpdateOptions{})
}
}

return err
}

func (p *policyReportClient) UpdateClusterPolicyReports(ctx context.Context) error {
labelSelector := metav1.FormatLabelSelector(&metav1.LabelSelector{MatchLabels: reportLabels})

list, err := p.client.ClusterPolicyReports().List(ctx, v1.ListOptions{LabelSelector: labelSelector})
for _, r := range list.Items {
report, update := updateReport(&r, p.labels, p.annotations)

if update {
p.client.ClusterPolicyReports().Update(ctx, report, v1.UpdateOptions{})
}
}

return err
}

func (p *policyReportClient) handleNamespaced(ctx context.Context, violation violation.PolicyViolation, ns string) error {
polr, err := p.client.PolicyReports(ns).Get(ctx, GeneratePolicyReportName(ns), v1.GetOptions{})
if err != nil {
polr = &v1alpha2.PolicyReport{
ObjectMeta: v1.ObjectMeta{
Name: GeneratePolicyReportName(ns),
Namespace: ns,
Labels: reportLabels,
Name: GeneratePolicyReportName(ns),
Namespace: ns,
Labels: p.labels,
Annotations: p.annotations,
},
}

Expand Down Expand Up @@ -90,8 +172,9 @@ func (p *policyReportClient) handleClusterScoped(ctx context.Context, violation
if err != nil {
polr = &v1alpha2.ClusterPolicyReport{
ObjectMeta: v1.ObjectMeta{
Name: ClusterPolicyReport,
Labels: reportLabels,
Name: ClusterPolicyReport,
Labels: p.labels,
Annotations: p.annotations,
},
}

Expand Down Expand Up @@ -172,11 +255,17 @@ func prevIndex(results []v1alpha2.PolicyReportResult, violation violation.Policy
return -1
}

func NewClient(client pr.Wgpolicyk8sV1alpha2Interface, maxResults int, source string, keepOnlyLatest bool) Client {
func NewClient(client pr.Wgpolicyk8sV1alpha2Interface, maxResults int, source string, keepOnlyLatest bool, labels, annotations map[string]string) Client {
for l, v := range reportLabels {
labels[l] = v
}

return &policyReportClient{
client: client,
maxResults: maxResults,
source: source,
keepOnlyLatest: keepOnlyLatest,
labels: labels,
annotations: annotations,
}
}
Loading

0 comments on commit 5106362

Please sign in to comment.