diff --git a/pkg/webhooks/policyvalidation.go b/pkg/webhooks/policyvalidation.go index 8c36ecc953..4fa833568b 100644 --- a/pkg/webhooks/policyvalidation.go +++ b/pkg/webhooks/policyvalidation.go @@ -14,13 +14,27 @@ import ( //HandlePolicyValidation performs the validation check on policy resource func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { var policy *policyv1.Policy - json.Unmarshal(request.Object.Raw, &policy) + admissionResp := &v1beta1.AdmissionResponse{ + Allowed: true, + } - admissionResp := ws.validateUniqueRuleName(policy) + raw := request.Object.Raw + if request.Operation == v1beta1.Delete { + raw = request.OldObject.Raw + } + if err := json.Unmarshal(raw, &policy); err != nil { + glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err) + return &v1beta1.AdmissionResponse{Allowed: false} + } + + if request.Operation != v1beta1.Delete { + admissionResp = ws.validateUniqueRuleName(policy) + } if admissionResp.Allowed { - ws.registerWebhookConfigurations(*policy) + ws.manageWebhookConfigurations(*policy, request.Operation) } + return admissionResp } diff --git a/pkg/webhooks/registration.go b/pkg/webhooks/registration.go index c1f09c840f..e0248fa86f 100644 --- a/pkg/webhooks/registration.go +++ b/pkg/webhooks/registration.go @@ -55,7 +55,7 @@ func (wrc *WebhookRegistrationClient) Register() error { } // For the case if cluster already has this configs - wrc.Deregister() + wrc.DeregisterAll() // register policy validating webhook during inital start return wrc.RegisterPolicyValidatingWebhook() @@ -71,6 +71,7 @@ func (wrc *WebhookRegistrationClient) RegisterMutatingWebhook() error { return err } + wrc.MutationRegistered.Set() return nil } @@ -84,6 +85,7 @@ func (wrc *WebhookRegistrationClient) RegisterValidatingWebhook() error { return err } + wrc.ValidationRegistered.Set() return nil } @@ -101,31 +103,66 @@ func (wrc *WebhookRegistrationClient) RegisterPolicyValidatingWebhook() error { return nil } -// Deregister deletes webhook configs from cluster +// DeregisterAll deletes webhook configs from cluster // This function does not fail on error: // Register will fail if the config exists, so there is no need to fail on error -func (wrc *WebhookRegistrationClient) Deregister() { - listOpt := v1.ListOptions{LabelSelector: "app=kyverno"} +func (wrc *WebhookRegistrationClient) DeregisterAll() { + wrc.deregisterMutatingWebhook() + wrc.deregisterValidatingWebhook() - // cleanup MutatingWebhookConfigurations - mutatingWebhookConfigList, _ := wrc.registrationClient.MutatingWebhookConfigurations().List(listOpt) - - for _, mwc := range mutatingWebhookConfigList.Items { - if err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(mwc.ObjectMeta.Name, &v1.DeleteOptions{}); err != nil { - glog.Errorf("Failed to delete mutatingWebhookConfiguration, %s, err: %v\n", mwc.ObjectMeta.Name, err) + if wrc.serverIP != "" { + err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationDebug, &v1.DeleteOptions{}) + if err != nil { + glog.Error(err) } } - - // cleanup validatingWebhookConfiguratinos - validatingWebhookConfigList, _ := wrc.registrationClient.ValidatingWebhookConfigurations().List(listOpt) - - for _, mwc := range validatingWebhookConfigList.Items { - if err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(mwc.ObjectMeta.Name, &v1.DeleteOptions{}); err != nil { - glog.Errorf("Failed to delete validatingWebhookConfiguration, %s, err: %v\n", mwc.ObjectMeta.Name, err) - } + err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationName, &v1.DeleteOptions{}) + if err != nil { + glog.Error(err) } } +func (wrc *WebhookRegistrationClient) deregister() { + wrc.deregisterMutatingWebhook() + wrc.deregisterValidatingWebhook() +} + +func (wrc *WebhookRegistrationClient) deregisterMutatingWebhook() { + if wrc.serverIP != "" { + err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &v1.DeleteOptions{}) + if err != nil { + glog.Error(err) + } else { + wrc.MutationRegistered.UnSet() + } + return + } + + err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationName, &v1.DeleteOptions{}) + if err != nil { + glog.Error(err) + } else { + wrc.MutationRegistered.UnSet() + } +} + +func (wrc *WebhookRegistrationClient) deregisterValidatingWebhook() { + if wrc.serverIP != "" { + err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationDebug, &v1.DeleteOptions{}) + if err != nil { + glog.Error(err) + } + wrc.ValidationRegistered.UnSet() + return + } + + err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationName, &v1.DeleteOptions{}) + if err != nil { + glog.Error(err) + } + wrc.ValidationRegistered.UnSet() +} + func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configuration *rest.Config) (*admregapi.MutatingWebhookConfiguration, error) { var caData []byte // Check if ca is defined in the secret tls-ca diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 2bc05d0535..a0e309234b 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -99,7 +99,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { if !utils.SkipFilteredResourcesReq(admissionReview.Request, ws.filterK8Resources) { // if the resource is being deleted we need to clear any existing Policy Violations // TODO: can report to the user that we clear the violation corresponding to this resource - if admissionReview.Request.Operation == v1beta1.Delete { + if admissionReview.Request.Operation == v1beta1.Delete && admissionReview.Request.Kind.Kind != policyKind { // Resource DELETE err := ws.removePolicyViolation(admissionReview.Request) if err != nil { diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index 7101f930ed..94f2eece36 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -3,17 +3,34 @@ package webhooks import ( "github.com/golang/glog" v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + v1beta1 "k8s.io/api/admission/v1beta1" + "k8s.io/apimachinery/pkg/labels" ) -func (ws *WebhookServer) registerWebhookConfigurations(policy v1alpha1.Policy) error { +type policyType int +const ( + none policyType = iota + mutate + validate + all +) + +func (ws *WebhookServer) manageWebhookConfigurations(policy v1alpha1.Policy, op v1beta1.Operation) { + switch op { + case v1beta1.Create: + ws.registerWebhookConfigurations(policy) + case v1beta1.Delete: + ws.deregisterWebhookConfigurations(policy) + } +} + +func (ws *WebhookServer) registerWebhookConfigurations(policy v1alpha1.Policy) error { for _, rule := range policy.Spec.Rules { if rule.Mutation != nil && !ws.webhookRegistrationClient.MutationRegistered.IsSet() { if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { return err } - - ws.webhookRegistrationClient.MutationRegistered.Set() glog.Infof("Mutating webhook registered") } @@ -21,10 +38,76 @@ func (ws *WebhookServer) registerWebhookConfigurations(policy v1alpha1.Policy) e if err := ws.webhookRegistrationClient.RegisterValidatingWebhook(); err != nil { return err } - - ws.webhookRegistrationClient.ValidationRegistered.Set() glog.Infof("Validating webhook registered") } } return nil } + +func (ws *WebhookServer) deregisterWebhookConfigurations(policy v1alpha1.Policy) error { + pt := none + glog.V(3).Infof("Retreiving policy type for %s\n", policy.Name) + + for _, rule := range policy.Spec.Rules { + if rule.Validation != nil { + pt = pt | validate + } + + if rule.Mutation != nil { + pt = pt | mutate + } + } + + glog.V(3).Infof("Scanning policy type==%v\n", pt) + + existPolicyType := ws.isPolicyTypeExist(pt, policy.Name) + glog.V(3).Infof("Found existing policy type==%v\n", existPolicyType) + + switch existPolicyType { + case none: + ws.webhookRegistrationClient.deregister() + glog.Infoln("All webhook deregistered") + case mutate: + if pt != mutate { + ws.webhookRegistrationClient.deregisterValidatingWebhook() + glog.Infoln("Validating webhook deregistered") + } + case validate: + if pt != validate { + ws.webhookRegistrationClient.deregisterMutatingWebhook() + glog.Infoln("Mutating webhook deregistered") + } + case all: + return nil + } + + return nil +} + +func (ws *WebhookServer) isPolicyTypeExist(pt policyType, policyName string) policyType { + ptype := none + + policies, err := ws.policyLister.List(labels.NewSelector()) + if err != nil { + glog.Errorf("Failed to get policy list") + } + + for _, p := range policies { + if p.Name == policyName { + glog.Infof("Skipping policy type check on %s\n", policyName) + continue + } + + for _, rule := range p.Spec.Rules { + if rule.Mutation != nil { + ptype = ptype | mutate + } + + if rule.Validation != nil { + ptype = ptype | validate + } + } + } + + return ptype +}