1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Generate Policy Exceptions (#9987)

* add control names and images to PSS results

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* remove init

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* fix tets

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* add --generate-exceptions flag

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* use controlsJSON

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* suppress message `Applying....`

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* generate CLI docs and fix lint issues

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* revert changes in launch.json

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* gen CLI docs

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* handle auto-gen rules

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* handle auto-gen rules for CronJob

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* handle auto-gen rules for CronJob

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

---------

Signed-off-by: Jim Bugwadia <jim@nirmata.com>
Co-authored-by: Frank Jogeleit <frank.jogeleit@web.de>
This commit is contained in:
Jim Bugwadia 2024-09-09 13:42:16 -07:00 committed by GitHub
parent 98a29e1321
commit be0ad07774
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 222 additions and 85 deletions

View file

@ -20,7 +20,6 @@ import (
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/color"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/processor"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/report"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/userinfo"
@ -37,7 +36,6 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/yaml"
)
const divider = "----------------------------------------------------------------------"
@ -68,6 +66,8 @@ type ApplyCommandConfig struct {
Exception []string
ContinueOnFail bool
inlineExceptions bool
GenerateExceptions bool
GeneratedExceptionTTL time.Duration
}
func Command() *cobra.Command {
@ -91,6 +91,8 @@ func Command() *cobra.Command {
printSkippedAndInvalidPolicies(out, skipInvalidPolicies)
if applyCommandConfig.PolicyReport {
printReports(out, responses, applyCommandConfig.AuditWarn)
} else if applyCommandConfig.GenerateExceptions {
printExceptions(out, responses, applyCommandConfig.AuditWarn, applyCommandConfig.GeneratedExceptionTTL)
} else if table {
printTable(out, detailedResults, applyCommandConfig.AuditWarn, responses...)
} else {
@ -155,6 +157,8 @@ func Command() *cobra.Command {
cmd.Flags().StringSliceVarP(&applyCommandConfig.Exception, "exceptions", "", nil, "Policy exception to be considered when evaluating policies against resources")
cmd.Flags().BoolVar(&applyCommandConfig.ContinueOnFail, "continue-on-fail", false, "If set to true, will continue to apply policies on the next resource upon failure to apply to the current resource instead of exiting out")
cmd.Flags().BoolVarP(&applyCommandConfig.inlineExceptions, "exceptions-with-resources", "", false, "Evaluate policy exceptions from the resources path")
cmd.Flags().BoolVarP(&applyCommandConfig.GenerateExceptions, "generate-exceptions", "", false, "Generate policy exceptions for each violation")
cmd.Flags().DurationVarP(&applyCommandConfig.GeneratedExceptionTTL, "generated-exception-ttl", "", time.Hour*24*30, "Default TTL for generated exceptions")
return cmd
}
@ -206,7 +210,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
return rc, resources1, skipInvalidPolicies, responses1, fmt.Errorf("Error: failed to load exceptions (%s)", err)
}
}
if !c.Stdin && !c.PolicyReport {
if !c.Stdin && !c.PolicyReport && !c.GenerateExceptions {
var policyRulesCount int
for _, policy := range policies {
policyRulesCount += len(autogen.ComputeRules(policy, ""))
@ -476,43 +480,6 @@ func (c *ApplyCommandConfig) checkArguments() (*processor.ResultCounts, []*unstr
return nil, nil, skipInvalidPolicies, nil, nil
}
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) {
clustered, namespaced := report.ComputePolicyReports(auditWarn, engineResponses...)
if len(clustered) > 0 {
report := report.MergeClusterReports(clustered)
yamlReport, _ := yaml.Marshal(report)
fmt.Fprintln(out, string(yamlReport))
}
for _, r := range namespaced {
fmt.Fprintln(out, string("---"))
yamlReport, _ := yaml.Marshal(r)
fmt.Fprintln(out, string(yamlReport))
}
}
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)
}
type WarnExitCodeError struct {
ExitCode int
}

View file

@ -0,0 +1,168 @@
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"
)
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) {
clustered, namespaced := report.ComputePolicyReports(auditWarn, engineResponses...)
if len(clustered) > 0 {
report := report.MergeClusterReports(clustered)
yamlReport, _ := yaml.Marshal(report)
fmt.Fprintln(out, string(yamlReport))
}
for _, r := range namespaced {
fmt.Fprintln(out, string("---"))
yamlReport, _ := yaml.Marshal(r)
fmt.Fprintln(out, string(yamlReport))
}
}
func printExceptions(out io.Writer, engineResponses []engineapi.EngineResponse, auditWarn bool, 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); err != nil {
log.Error(err)
}
}
}
}
}
func printException(out io.Writer, result v1alpha2.PolicyReportResult, ttl time.Duration) 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
}
exceptionYAML, err := yaml.Marshal(exception)
if err != nil {
return err
}
fmt.Fprint(out, "---\n")
fmt.Fprint(out, string(exceptionYAML))
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)
}

View file

@ -45,6 +45,8 @@ kyverno apply [flags]
-e, --exception strings Policy exception to be considered when evaluating policies against resources
--exceptions strings Policy exception to be considered when evaluating policies against resources
--exceptions-with-resources Evaluate policy exceptions from the resources path
--generate-exceptions Generate policy exceptions for each violation
--generated-exception-ttl duration Default TTL for generated exceptions (default 720h0m0s)
-b, --git-branch string test git repository branch
-h, --help help for apply
--kubeconfig string path to kubeconfig file with authorization and master location information