1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/policyreport/builder.go
shuting 5e07ecc5f3
Add Policy Report (#1229)
* add report in cli

* policy report crd added

* policy report added

* configmap added

* added jobs

* added jobs

* bug fixed

* added logic for cli

* common function added

* sub command added for policy report

* subcommand added for report

* common package changed

* configmap added

* added logic for kyverno cli

* added logic for jobs

* added logic for jobs

* added logic for jobs

* added logic for cli

* buf fix

* cli changes

* count bug fix

* docs added for command

* go fmt

* refactor codebase

* remove policy controller for policyreport

* policy report removed

* bug fixes

* bug fixes

* added job trigger if needed

* job deletation logic added

* build failed fix

* fixed e2e test

* remove hard coded variables

* packages adde

* improvment added in jobs sheduler

* policy report yaml added

* cronjob added

* small fixes

* remove background sync

* documentation added for report command

* remove extra log

* small improvement

* tested policy report

* revert hardcoded changes

* changes for demo

* demo changes

* resource aggrigation added

* More changes

* More changes

* - resolve PR comments; - refactor jobs controller

* set rbac for jobs

* add clean up in job controller

* add short names

* remove application scope for policyreport

* move job controller to policyreport

* add report logic in command apply

* - update policy report types;  - upgrade k8s library; - update code gen

* temporarily comment out code to pass CI build

* generate / update policyreport to cluster

* add unit test for CLI report

* add test for apply - generate policy report

* fix unit test

* - remove job controller; - remove in-memory configmap; - clean up kustomize manifest

* remove dependency

* add reportRequest / clusterReportRequest

* clean up policy report

* generate report request

* update crd clusterReportRequest

* - update json tag of report summary; - update definition manifests; -  fix dclient creation

* aggregate reportRequest into policy report

* fix unit tests

* - update report summary to optional; - generate clusterPolicyReport; - remove reportRequests after merged to report

* remove

* generate reportRequest in kyverno namespace

* update resource filter in helm chart

* - rename reportRequest to reportChangeRequest; -rename clusterReportRequest to clusterReportChangeRequest

* generate policy report in background scan

* skip generating report change request if there's entry results

* fix results entry removal when policy / rule gets deleted

* rename apiversion from policy.kubernetes.io to policy.k8s.io

* update summary.* to lower case

* move reportChangeRequest to kyverno.io/v1alpha1

* remove policy report flag

* fix report update

* clean up policy violation CRD

* remove violation CRD from manifest

* clean up policy violation code - remove pvGenerator

* change severity fields to lower case

* update import library

* set report category

Co-authored-by: Yuvraj <yuvraj.yad001@gmail.com>
Co-authored-by: Yuvraj <10830562+evalsocket@users.noreply.github.com>
Co-authored-by: Jim Bugwadia <jim@nirmata.com>
2020-11-09 11:26:12 -08:00

253 lines
7.3 KiB
Go
Executable file

package policyreport
import (
"fmt"
"os"
"reflect"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
request "github.com/kyverno/kyverno/pkg/api/kyverno/v1alpha1"
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha1"
kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
"github.com/kyverno/kyverno/pkg/common"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
const (
clusterreportchangerequest string = "clusterreportchangerequest"
deletedLabelResource string = "kyverno.io/delete.resource"
deletedLabelResourceKind string = "kyverno.io/delete.resource.kind"
deletedLabelPolicy string = "kyverno.io/delete.policy"
deletedLabelRule string = "kyverno.io/delete.rule"
)
func generatePolicyReportName(ns string) string {
if ns == "" {
return clusterpolicyreport
}
return fmt.Sprintf("policyreport-ns-%s", ns)
}
//GeneratePRsFromEngineResponse generate Violations from engine responses
func GeneratePRsFromEngineResponse(ers []response.EngineResponse, log logr.Logger) (pvInfos []Info) {
for _, er := range ers {
// ignore creation of PV for resources that are yet to be assigned a name
if er.PolicyResponse.Resource.Name == "" {
log.V(4).Info("resource does no have a name assigned yet, not creating a policy violation", "resource", er.PolicyResponse.Resource)
continue
}
// skip when response succeed
if os.Getenv("POLICY-TYPE") != common.PolicyReport {
if er.IsSuccessful() {
continue
}
}
// build policy violation info
pvInfos = append(pvInfos, buildPVInfo(er))
}
return pvInfos
}
// Builder builds report change request struct
// this is base type of namespaced and cluster policy report
type Builder interface {
build(info Info) (*unstructured.Unstructured, error)
}
type requestBuilder struct {
cpolLister kyvernolister.ClusterPolicyLister
polLister kyvernolister.PolicyLister
}
func NewBuilder(cpolLister kyvernolister.ClusterPolicyLister, polLister kyvernolister.PolicyLister) *requestBuilder {
return &requestBuilder{cpolLister: cpolLister, polLister: polLister}
}
func (builder *requestBuilder) build(info Info) (req *unstructured.Unstructured, err error) {
results := []*report.PolicyReportResult{}
for _, rule := range info.Rules {
if rule.Type != utils.Validation.String() {
continue
}
result := &report.PolicyReportResult{
Policy: info.PolicyName,
Resources: []*v1.ObjectReference{
{
Kind: info.Resource.GetKind(),
Namespace: info.Resource.GetNamespace(),
APIVersion: info.Resource.GetAPIVersion(),
Name: info.Resource.GetName(),
UID: info.Resource.GetUID(),
},
},
Scored: true,
Category: builder.fetchCategory(info.PolicyName, info.Resource.GetNamespace()),
}
result.Rule = rule.Name
result.Message = rule.Message
result.Status = report.PolicyStatus(rule.Check)
results = append(results, result)
}
if info.Resource.GetNamespace() != "" {
rr := &request.ReportChangeRequest{
Summary: calculateSummary(results),
Results: results,
}
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(rr)
if err != nil {
return nil, err
}
req = &unstructured.Unstructured{Object: obj}
set(req, fmt.Sprintf("reportchangerequest-%s-%s-%s", info.PolicyName, info.Resource.GetNamespace(), info.Resource.GetName()), info)
} else {
rr := &request.ClusterReportChangeRequest{
Summary: calculateSummary(results),
Results: results,
}
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(rr)
if err != nil {
return nil, err
}
req = &unstructured.Unstructured{Object: obj}
set(req, fmt.Sprintf("%s-%s", clusterreportchangerequest, info.Resource.GetName()), info)
}
// deletion of a result entry
// - on resource deleteion:
// - info.Rules == 0 && info.PolicyName == ""
// - set label delete.resource=resourceKind-resourceNamespace-resourceName
// - on policy deleteion:
// - info.PolicyName != "" && info.Resource == {}
// - set label delete.policy=policyName
if len(info.Rules) == 0 && info.PolicyName == "" {
req.SetLabels(map[string]string{
"namespace": info.Resource.GetNamespace(),
deletedLabelResource: info.Resource.GetName(),
deletedLabelResourceKind: info.Resource.GetKind()})
} else if info.PolicyName != "" && reflect.DeepEqual(info.Resource, unstructured.Unstructured{}) {
req.SetKind("ReportChangeRequest")
if len(info.Rules) == 0 {
req.SetLabels(map[string]string{
deletedLabelPolicy: info.PolicyName})
req.SetName(fmt.Sprintf("reportchangerequest-%s", info.PolicyName))
} else {
req.SetLabels(map[string]string{
deletedLabelPolicy: info.PolicyName,
deletedLabelRule: info.Rules[0].Name})
req.SetName(fmt.Sprintf("reportchangerequest-%s-%s", info.PolicyName, info.Rules[0].Name))
}
} else if len(results) == 0 {
// return nil on empty result without a deletion
return nil, nil
}
return req, nil
}
func set(obj *unstructured.Unstructured, name string, info Info) {
resource := info.Resource
obj.SetName(name)
obj.SetNamespace(config.KubePolicyNamespace)
obj.SetAPIVersion(request.SchemeGroupVersion.Group + "/" + request.SchemeGroupVersion.Version)
if resource.GetNamespace() == "" {
obj.SetKind("ClusterReportChangeRequest")
} else {
obj.SetKind("ReportChangeRequest")
}
obj.SetLabels(map[string]string{
"namespace": resource.GetNamespace(),
"policy": info.PolicyName,
"resource": resource.GetKind() + "-" + resource.GetNamespace() + "-" + resource.GetName(),
})
if info.FromSync {
obj.SetAnnotations(map[string]string{
"fromSync": "true",
})
}
}
func calculateSummary(results []*report.PolicyReportResult) (summary report.PolicyReportSummary) {
for _, res := range results {
switch string(res.Status) {
case report.StatusPass:
summary.Pass++
case report.StatusFail:
summary.Fail++
case report.StatusWarn:
summary.Warn++
case report.StatusError:
summary.Error++
case report.StatusSkip:
summary.Skip++
}
}
return
}
func buildPVInfo(er response.EngineResponse) Info {
info := Info{
PolicyName: er.PolicyResponse.Policy,
Resource: er.PatchedResource,
Rules: buildViolatedRules(er),
}
return info
}
func buildViolatedRules(er response.EngineResponse) []kyverno.ViolatedRule {
var violatedRules []kyverno.ViolatedRule
for _, rule := range er.PolicyResponse.Rules {
if os.Getenv("POLICY-TYPE") != common.PolicyReport {
if rule.Success {
continue
}
}
vrule := kyverno.ViolatedRule{
Name: rule.Name,
Type: rule.Type,
Message: rule.Message,
}
vrule.Check = report.StatusFail
if rule.Success {
vrule.Check = report.StatusPass
}
violatedRules = append(violatedRules, vrule)
}
return violatedRules
}
const categoryLabel string = "policies.kyverno.io/category"
func (builder *requestBuilder) fetchCategory(policy, ns string) string {
cpol, err := builder.cpolLister.Get(policy)
if err == nil {
if ann := cpol.GetAnnotations(); ann != nil {
return ann[categoryLabel]
}
}
pol, err := builder.polLister.Policies("").Get(policy)
if err == nil {
if ann := pol.GetAnnotations(); ann != nil {
return ann[categoryLabel]
}
}
return ""
}