mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-06 16:06:56 +00:00
* 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>
253 lines
7.3 KiB
Go
Executable file
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 ""
|
|
}
|