From ba8030bec016a10ca44490f5261262ab3a58b47e Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Thu, 16 Jan 2020 11:57:28 -0800 Subject: [PATCH] change to use validationFailureAction for the mutation failure action --- pkg/engine/mutation.go | 37 ++++++++++++++++------------ pkg/engine/validation.go | 1 - pkg/webhooks/common.go | 20 ++++++++++++++++ pkg/webhooks/mutation.go | 49 +++++++++++++++++++++++++------------- pkg/webhooks/server.go | 12 +++++++++- pkg/webhooks/validation.go | 2 +- 6 files changed, 87 insertions(+), 34 deletions(-) diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index a8c36b7262..f332338a38 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -13,6 +13,7 @@ import ( "github.com/nirmata/kyverno/pkg/engine/response" "github.com/nirmata/kyverno/pkg/engine/utils" "github.com/nirmata/kyverno/pkg/engine/variables" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) const ( @@ -28,22 +29,10 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) { resource := policyContext.NewResource ctx := policyContext.Context - // policy information - func() { - // set policy information - resp.PolicyResponse.Policy = policy.Name - // resource details - resp.PolicyResponse.Resource.Name = resource.GetName() - resp.PolicyResponse.Resource.Namespace = resource.GetNamespace() - resp.PolicyResponse.Resource.Kind = resource.GetKind() - resp.PolicyResponse.Resource.APIVersion = resource.GetAPIVersion() - }() + startMutateResultResponse(&resp, policy, resource) glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime) - defer func() { - resp.PolicyResponse.ProcessingTime = time.Since(startTime) - glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, resp.PolicyResponse.ProcessingTime) - glog.V(4).Infof("Mutation Rules appplied count %v for policy %q", resp.PolicyResponse.RulesAppliedCount, policy.Name) - }() + defer endMutateResultResponse(&resp, startTime) + incrementAppliedRuleCount := func() { // rules applied succesfully count resp.PolicyResponse.RulesAppliedCount++ @@ -146,6 +135,24 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) { return resp } +func startMutateResultResponse(resp *response.EngineResponse, policy kyverno.ClusterPolicy, resource unstructured.Unstructured) { + // set policy information + resp.PolicyResponse.Policy = policy.Name + // resource details + resp.PolicyResponse.Resource.Name = resource.GetName() + resp.PolicyResponse.Resource.Namespace = resource.GetNamespace() + resp.PolicyResponse.Resource.Kind = resource.GetKind() + resp.PolicyResponse.Resource.APIVersion = resource.GetAPIVersion() + // TODO: replace with mutationFailureAction ? + resp.PolicyResponse.ValidationFailureAction = policy.Spec.ValidationFailureAction +} + +func endMutateResultResponse(resp *response.EngineResponse, startTime time.Time) { + resp.PolicyResponse.ProcessingTime = time.Since(startTime) + glog.V(4).Infof("finished applying mutation rules policy %v (%v)", resp.PolicyResponse.Policy, resp.PolicyResponse.ProcessingTime) + glog.V(4).Infof("Mutation Rules appplied count %v for policy %q", resp.PolicyResponse.RulesAppliedCount, resp.PolicyResponse.Policy) +} + // podTemplateRule mutate pod template with annotation // pod-policies.kyverno.io/autogen-applied=true var podTemplateRule = kyverno.Rule{ diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index fb52ef6927..cecf91b320 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -74,7 +74,6 @@ func startResultResponse(resp *response.EngineResponse, policy kyverno.ClusterPo resp.PolicyResponse.Resource.Kind = newR.GetKind() resp.PolicyResponse.Resource.APIVersion = newR.GetAPIVersion() resp.PolicyResponse.ValidationFailureAction = policy.Spec.ValidationFailureAction - } func endResultResponse(resp *response.EngineResponse, startTime time.Time) { diff --git a/pkg/webhooks/common.go b/pkg/webhooks/common.go index d2c1a9720f..0c4e780101 100644 --- a/pkg/webhooks/common.go +++ b/pkg/webhooks/common.go @@ -36,6 +36,26 @@ func toBlockResource(engineReponses []response.EngineResponse) bool { return false } +// getEnforceFailureErrorMsg gets the error messages for failed enforce policy +func getEnforceFailureErrorMsg(engineReponses []response.EngineResponse) string { + var str []string + var resourceInfo string + + for _, er := range engineReponses { + if !er.IsSuccesful() && er.PolicyResponse.ValidationFailureAction == Enforce { + resourceInfo = fmt.Sprintf("%s/%s/%s", er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name) + str = append(str, fmt.Sprintf("failed policy %s:", er.PolicyResponse.Policy)) + for _, rule := range er.PolicyResponse.Rules { + if !rule.Success { + str = append(str, rule.ToString()) + } + } + } + } + return fmt.Sprintf("Resource %s %s", resourceInfo, strings.Join(str, ";")) +} + +// getErrorMsg gets all failed engine response message func getErrorMsg(engineReponses []response.EngineResponse) string { var str []string var resourceInfo string diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index db64a1b6d1..6af4dc0d26 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -1,6 +1,8 @@ package webhooks import ( + "time" + "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine" @@ -15,8 +17,8 @@ import ( ) // HandleMutation handles mutating webhook admission request -// return generated patches -func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured, policies []kyverno.ClusterPolicy, roles, clusterRoles []string) []byte { +// return value: blocked, generated patches, error message +func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured, policies []kyverno.ClusterPolicy, roles, clusterRoles []string) (bool, []byte, string) { glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) @@ -80,7 +82,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation) policyContext.Policy = policy - // TODO: this can be engineResponse := engine.Mutate(policyContext) engineResponses = append(engineResponses, engineResponse) // Gather policy application statistics @@ -101,7 +102,19 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou patches = append(patches, annPatches) } - // generate violation when referenced path does not exist + // report time + reportTime := time.Now() + + // ENFORCE - block resource creation + blocked := toBlockResource(engineResponses) + if blocked { + glog.V(4).Infof("resource %s/%s/%s is blocked\n", resource.GetKind(), resource.GetNamespace(), resource.GetName()) + sendStat(blocked) + return true, nil, getEnforceFailureErrorMsg(engineResponses) + } + + // AUDIT + // generate violation when response fails pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses) ws.pvGenerator.Add(pvInfos...) @@ -109,20 +122,24 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou events := generateEvents(engineResponses, (request.Operation == v1beta1.Update)) ws.eventGen.Add(events...) - sendStat(false) + sendStat(blocked) // debug info - if len(patches) != 0 { - glog.V(3).Infof("Patches generated for %s/%s/%s, operation=%v:\n %v", - resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.Operation, string(engineutils.JoinPatches(patches))) - } + func() { + if len(patches) != 0 { + glog.V(3).Infof("Patches generated for %s/%s/%s, operation=%v:\n %v", + resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.Operation, string(engineutils.JoinPatches(patches))) + } - // if any of the policies fails, print out the error - if !isResponseSuccesful(engineResponses) { - glog.Errorf("Failed to mutate the resource, report as violation: %s\n", getErrorMsg(engineResponses)) - } + // if any of the policies fails, print out the error + if !isResponseSuccesful(engineResponses) { + glog.Errorf("Failed to mutate the resource, report as violation: %s\n", getErrorMsg(engineResponses)) + } + }() - // patches holds all the successful patches - // if no patch is created, it returns nil - return engineutils.JoinPatches(patches) + // report time end + glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), resource.GetKind(), resource.GetNamespace(), resource.GetName()) + + // patches holds all the successful patches, if no patch is created, it returns nil + return false, engineutils.JoinPatches(patches), "" } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index c9704ff160..676bc3d20d 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -237,7 +237,17 @@ func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionReques // MUTATION // mutation failure should not block the resource creation // any mutation failure is reported as the violation - patches := ws.HandleMutation(request, resource, policies, roles, clusterRoles) + blocked, patches, errMsg := ws.HandleMutation(request, resource, policies, roles, clusterRoles) + if blocked { + glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name) + return &v1beta1.AdmissionResponse{ + Allowed: false, + Result: &metav1.Status{ + Status: "Failure", + Message: errMsg, + }, + } + } // patch the resource with patches before handling validation rules patchedResource := processResourceWithPatches(patches, request.Object.Raw) diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 040241c7e3..c33e30147a 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -107,7 +107,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol if blocked { glog.V(4).Infof("resource %s/%s/%s is blocked\n", newR.GetKind(), newR.GetNamespace(), newR.GetName()) sendStat(true) - return false, getErrorMsg(engineResponses) + return false, getEnforceFailureErrorMsg(engineResponses) } // ADD POLICY VIOLATIONS