From 1960ced0306f4e578ec22800f27008be19e7d540 Mon Sep 17 00:00:00 2001 From: Steven Lahouchuc Date: Mon, 21 Nov 2022 09:21:32 -0500 Subject: [PATCH] adding --audit-warn flag (#5321) Signed-off-by: Steven Lahouchuc Co-authored-by: Vyankatesh Kudtarkar --- .../kubectl-kyverno/apply/apply_command.go | 3 ++ .../apply/apply_command_test.go | 19 ++++++++++++ cmd/cli/kubectl-kyverno/apply/report_test.go | 4 +-- .../kubectl-kyverno/utils/common/common.go | 29 ++++++++++++++----- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/cmd/cli/kubectl-kyverno/apply/apply_command.go b/cmd/cli/kubectl-kyverno/apply/apply_command.go index 15b30b7108..486805e4f9 100644 --- a/cmd/cli/kubectl-kyverno/apply/apply_command.go +++ b/cmd/cli/kubectl-kyverno/apply/apply_command.go @@ -55,6 +55,7 @@ type ApplyCommandConfig struct { PolicyReport bool Stdin bool RegistryAccess bool + AuditWarn bool ResourcePaths []string PolicyPaths []string } @@ -163,6 +164,7 @@ func Command() *cobra.Command { cmd.Flags().BoolVarP(&applyCommandConfig.RegistryAccess, "registry", "", false, "If set to true, access the image registry using local docker credentials to populate external data") cmd.Flags().StringVarP(&applyCommandConfig.KubeConfig, "kubeconfig", "", "", "path to kubeconfig file with authorization and master location information") cmd.Flags().StringVarP(&applyCommandConfig.Context, "context", "", "", "The name of the kubeconfig context to use") + cmd.Flags().BoolVarP(&applyCommandConfig.AuditWarn, "audit-warn", "", false, "If set to true, will flag audit policies as warnings instead of failures") return cmd } @@ -360,6 +362,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso Rc: rc, PrintPatchResource: true, Client: dClient, + AuditWarn: c.AuditWarn, } _, info, err := common.ApplyPolicyOnResource(applyPolicyConfig) if err != nil { diff --git a/cmd/cli/kubectl-kyverno/apply/apply_command_test.go b/cmd/cli/kubectl-kyverno/apply/apply_command_test.go index 06243576f0..32aca6b0bc 100644 --- a/cmd/cli/kubectl-kyverno/apply/apply_command_test.go +++ b/cmd/cli/kubectl-kyverno/apply/apply_command_test.go @@ -70,6 +70,25 @@ func Test_Apply(t *testing.T) { }, }, }, + { + config: ApplyCommandConfig{ + PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"}, + ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"}, + PolicyReport: true, + AuditWarn: true, + }, + expectedPolicyReports: []preport.PolicyReport{ + { + Summary: preport.PolicyReportSummary{ + Pass: 1, + Fail: 0, + Skip: 0, + Error: 0, + Warn: 1, + }, + }, + }, + }, } compareSummary := func(expected preport.PolicyReportSummary, actual map[string]interface{}) { diff --git a/cmd/cli/kubectl-kyverno/apply/report_test.go b/cmd/cli/kubectl-kyverno/apply/report_test.go index 94cd3ea9be..a8fdee5d0e 100644 --- a/cmd/cli/kubectl-kyverno/apply/report_test.go +++ b/cmd/cli/kubectl-kyverno/apply/report_test.go @@ -96,7 +96,7 @@ func Test_buildPolicyReports(t *testing.T) { err = json.Unmarshal(rawEngRes, &er) assert.NilError(t, err) - info := kyvCommon.ProcessValidateEngineResponse(&policy, &er, "", rc, true) + info := kyvCommon.ProcessValidateEngineResponse(&policy, &er, "", rc, true, false) pvInfos = append(pvInfos, info) reports := buildPolicyReports(pvInfos) @@ -132,7 +132,7 @@ func Test_buildPolicyResults(t *testing.T) { err = json.Unmarshal(rawEngRes, &er) assert.NilError(t, err) - info := kyvCommon.ProcessValidateEngineResponse(&policy, &er, "", rc, true) + info := kyvCommon.ProcessValidateEngineResponse(&policy, &er, "", rc, true, false) pvInfos = append(pvInfos, info) results := buildPolicyResults(pvInfos) diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index 168971ebdf..3eefcd2372 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -83,6 +83,7 @@ type ApplyPolicyConfig struct { PrintPatchResource bool RuleToCloneSourceResource map[string]string Client dclient.Interface + AuditWarn bool } // HasVariables - check for variables in the policy @@ -480,7 +481,7 @@ OuterLoop: var validateResponse *response.EngineResponse if policyHasValidate { validateResponse = engine.Validate(policyContext) - info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport) + info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn) } if validateResponse != nil && !validateResponse.IsEmpty() { @@ -490,7 +491,7 @@ OuterLoop: verifyImageResponse, _ := engine.VerifyAndPatchImages(policyContext) if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() { engineResponses = append(engineResponses, verifyImageResponse) - info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport) + info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn) } var policyHasGenerate bool @@ -521,7 +522,7 @@ OuterLoop: } engineResponses = append(engineResponses, generateResponse) } - updateResultCounts(c.Policy, generateResponse, resPath, c.Rc) + updateResultCounts(c.Policy, generateResponse, resPath, c.Rc, c.AuditWarn) } return engineResponses, info, nil @@ -673,7 +674,7 @@ func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []str return resources, err } -func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateResponse *response.EngineResponse, resPath string, rc *ResultCounts, policyReport bool) Info { +func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateResponse *response.EngineResponse, resPath string, rc *ResultCounts, policyReport bool, auditWarn bool) Info { var violatedRules []kyvernov1.ViolatedRule printCount := 0 @@ -698,11 +699,16 @@ func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateRes vrule.Status = policyreportv1alpha2.StatusPass case response.RuleStatusFail: + auditWarning := false ann := policy.GetAnnotations() if scored, ok := ann[kyvernov1.AnnotationPolicyScored]; ok && scored == "false" { rc.Warn++ vrule.Status = policyreportv1alpha2.StatusWarn break + } else if auditWarn && validateResponse.GetValidationFailureAction().Audit() { + rc.Warn++ + auditWarning = true + vrule.Status = policyreportv1alpha2.StatusWarn } else { rc.Fail++ vrule.Status = policyreportv1alpha2.StatusFail @@ -710,7 +716,11 @@ func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateRes if !policyReport { if printCount < 1 { - fmt.Printf("\npolicy %s -> resource %s failed: \n", policy.GetName(), resPath) + if auditWarning { + fmt.Printf("\npolicy %s -> resource %s failed as audit warning: \n", policy.GetName(), resPath) + } else { + fmt.Printf("\npolicy %s -> resource %s failed: \n", policy.GetName(), resPath) + } printCount++ } @@ -763,7 +773,7 @@ func buildPVInfo(er *response.EngineResponse, violatedRules []kyvernov1.Violated return info } -func updateResultCounts(policy kyvernov1.PolicyInterface, engineResponse *response.EngineResponse, resPath string, rc *ResultCounts) { +func updateResultCounts(policy kyvernov1.PolicyInterface, engineResponse *response.EngineResponse, resPath string, rc *ResultCounts, auditWarn bool) { printCount := 0 for _, policyRule := range autogen.ComputeRules(policy) { ruleFoundInEngineResponse := false @@ -779,7 +789,12 @@ func updateResultCounts(policy kyvernov1.PolicyInterface, engineResponse *respon printCount++ } fmt.Printf("%d. %s - %s\n", i+1, ruleResponse.Name, ruleResponse.Message) - rc.Fail++ + + if auditWarn && engineResponse.GetValidationFailureAction().Audit() { + rc.Warn++ + } else { + rc.Fail++ + } } continue }