mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-09 01:16:55 +00:00
188 lines
5.3 KiB
Go
188 lines
5.3 KiB
Go
package apply
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
|
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
|
"github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/processor"
|
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/report"
|
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
kyvernoreports "github.com/kyverno/kyverno/pkg/utils/report"
|
|
"github.com/opentracing/opentracing-go/log"
|
|
"github.com/pkg/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
const divider = "----------------------------------------------------------------------"
|
|
|
|
func printSkippedAndInvalidPolicies(out io.Writer, skipInvalidPolicies SkippedInvalidPolicies) {
|
|
if len(skipInvalidPolicies.skipped) > 0 {
|
|
fmt.Fprintln(out, divider)
|
|
fmt.Fprintln(out, "Policies Skipped (as required variables are not provided by the user):")
|
|
for i, policyName := range skipInvalidPolicies.skipped {
|
|
fmt.Fprintf(out, "%d. %s\n", i+1, policyName)
|
|
}
|
|
fmt.Fprintln(out, divider)
|
|
}
|
|
if len(skipInvalidPolicies.invalid) > 0 {
|
|
fmt.Fprintln(out, divider)
|
|
fmt.Fprintln(out, "Invalid Policies:")
|
|
for i, policyName := range skipInvalidPolicies.invalid {
|
|
fmt.Fprintf(out, "%d. %s\n", i+1, policyName)
|
|
}
|
|
fmt.Fprintln(out, divider)
|
|
}
|
|
}
|
|
|
|
func printReports(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool, outputFormat string) {
|
|
clustered, namespaced := report.ComputePolicyReports(auditWarn, engineResponses...)
|
|
|
|
printReport := func(report interface{}) {
|
|
var output []byte
|
|
if outputFormat == "json" {
|
|
output, _ = json.Marshal(report)
|
|
} else {
|
|
output, _ = yaml.Marshal(report)
|
|
}
|
|
fmt.Fprintln(out, string(output))
|
|
}
|
|
|
|
if len(clustered) > 0 {
|
|
clusterReport := report.MergeClusterReports(clustered)
|
|
printReport(clusterReport)
|
|
}
|
|
|
|
for _, r := range namespaced {
|
|
fmt.Fprintln(out, "---")
|
|
printReport(r)
|
|
}
|
|
}
|
|
|
|
func printExceptions(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool, outputFormat string, ttl time.Duration) {
|
|
clustered, _ := report.ComputePolicyReports(auditWarn, engineResponses...)
|
|
for _, report := range clustered {
|
|
for _, result := range report.Results {
|
|
if result.Result == "fail" {
|
|
if err := printException(out, result, ttl, outputFormat); err != nil {
|
|
log.Error(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func printException(out io.Writer, result v1alpha2.PolicyReportResult, ttl time.Duration, outputFormat string) error {
|
|
for _, r := range result.Resources {
|
|
name := strings.Join([]string{result.Policy, result.Rule, r.Namespace, r.Name}, "-")
|
|
|
|
kinds := []string{r.Kind}
|
|
names := []string{r.Name}
|
|
rules := []string{result.Rule}
|
|
if strings.HasPrefix(result.Rule, "autogen-") {
|
|
if r.Kind == "CronJob" {
|
|
kinds = append(kinds, "Job")
|
|
rules = append(rules, strings.ReplaceAll(result.Rule, "autogen-cronjob-", "autogen-"))
|
|
kinds = append(kinds, "Pod")
|
|
rules = append(rules, result.Rule[len("autogen-cronjob-"):])
|
|
} else {
|
|
kinds = append(kinds, "Pod")
|
|
rules = append(rules, result.Rule[len("autogen-"):])
|
|
}
|
|
names = append(names, r.Name+"-*")
|
|
}
|
|
|
|
exception := kyvernov2beta1.PolicyException{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "PolicyException",
|
|
APIVersion: kyvernov2beta1.SchemeGroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
},
|
|
Spec: kyvernov2beta1.PolicyExceptionSpec{
|
|
Match: kyvernov2beta1.MatchResources{
|
|
All: kyvernov1.ResourceFilters{
|
|
kyvernov1.ResourceFilter{
|
|
ResourceDescription: kyvernov1.ResourceDescription{
|
|
Kinds: kinds,
|
|
Names: names,
|
|
Namespaces: []string{r.Namespace},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Exceptions: []kyvernov2beta1.Exception{
|
|
{
|
|
PolicyName: result.Policy,
|
|
RuleNames: rules,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if ttl > 0 {
|
|
exception.ObjectMeta.Labels = map[string]string{
|
|
"cleanup.kyverno.io/ttl": ttl.String(),
|
|
}
|
|
}
|
|
|
|
if controlList, ok := result.Properties["controlsJSON"]; ok {
|
|
pssList := make([]kyvernov1.PodSecurityStandard, 0)
|
|
var controls []kyvernoreports.Control
|
|
err := json.Unmarshal([]byte(controlList), &controls)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to unmarshall PSS controls %s", controlList)
|
|
}
|
|
for _, c := range controls {
|
|
pss := kyvernov1.PodSecurityStandard{
|
|
ControlName: c.Name,
|
|
}
|
|
if c.Images != nil {
|
|
pss.Images = wildcardTagOrDigest(c.Images)
|
|
}
|
|
pssList = append(pssList, pss)
|
|
}
|
|
exception.Spec.PodSecurity = pssList
|
|
}
|
|
|
|
var exceptionReport []byte
|
|
marshal := func(report interface{}) ([]byte, error) {
|
|
if outputFormat == "json" {
|
|
return json.Marshal(report)
|
|
}
|
|
return yaml.Marshal(report)
|
|
}
|
|
|
|
exceptionReport, err := marshal(exception)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprint(out, "---\n")
|
|
fmt.Fprint(out, string(exceptionReport))
|
|
fmt.Fprint(out, "\n")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var regexpTagOrDigest = regexp.MustCompile(":.*|@.*")
|
|
|
|
func wildcardTagOrDigest(images []string) []string {
|
|
for i, s := range images {
|
|
images[i] = regexpTagOrDigest.ReplaceAllString(s, "*")
|
|
}
|
|
return images
|
|
}
|
|
|
|
func printViolations(out io.Writer, rc *processor.ResultCounts) {
|
|
fmt.Fprintf(out, "\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n", rc.Pass, rc.Fail, rc.Warn, rc.Error, rc.Skip)
|
|
}
|