diff --git a/documentation/writing-policies-validate.md b/documentation/writing-policies-validate.md index fca62a6e5e..68ec0b8c6f 100644 --- a/documentation/writing-policies-validate.md +++ b/documentation/writing-policies-validate.md @@ -96,9 +96,9 @@ spec : - ^(name): "*" resources: requests: - memory: "$(<=./../../lim(its/mem)ory)" - lim(its: - mem)ory: "2048Mi" + memory: "$(<=./../../limits/memory)" + limits: + memory: "2048Mi" ```` ### Allow OR across overlay pattern diff --git a/main.go b/main.go index 7a664b00e4..2b15e0d043 100644 --- a/main.go +++ b/main.go @@ -37,7 +37,8 @@ func main() { printVersionInfo() // profile cpu and memory consuption prof = enableProfiling(cpu, memory) - + // cleanUp Channel + cleanUp := make(chan struct{}) // CLIENT CONFIG clientConfig, err := createClientConfig(kubeconfig) if err != nil { @@ -136,7 +137,7 @@ func main() { // -- annotations on resources with update details on mutation JSON patches // -- generate policy violation resource // -- generate events on policy and resource - server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, pc.GetPolicyStatusAggregator(), filterK8Resources) + server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, pc.GetPolicyStatusAggregator(), filterK8Resources, cleanUp) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } @@ -157,6 +158,9 @@ func main() { <-stopCh disableProfiling(prof) server.Stop() + // resource cleanup + // remove webhook configurations + <-cleanUp } func init() { diff --git a/pkg/config/config.go b/pkg/config/config.go index d658df547c..da13c75248 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -15,9 +15,13 @@ const ( ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug" ValidatingWebhookName = "nirmata.kyverno.validating-webhook" - PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg" - PolicyValidatingWebhookConfigurationDebug = "kyverno-policy-validating-webhook-cfg-debug" - PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook" + PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg" + PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug" + PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook" + + PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg" + PolicyMutatingWebhookConfigurationDebugName = "kyverno-policy-mutating-webhook-cfg-debug" + PolicyMutatingWebhookName = "nirmata.kyverno.policy-mutating-webhook" // Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields // Issue: https://github.com/kubernetes/kubernetes/pull/63972 @@ -31,6 +35,7 @@ var ( MutatingWebhookServicePath = "/mutate" ValidatingWebhookServicePath = "/validate" PolicyValidatingWebhookServicePath = "/policyvalidate" + PolicyMutatingWebhookServicePath = "/policymutate" KubePolicyAppLabels = map[string]string{ "app": "kyverno", } diff --git a/pkg/engine/patches.go b/pkg/engine/patches.go index 1206ba6a0a..b140aa1033 100644 --- a/pkg/engine/patches.go +++ b/pkg/engine/patches.go @@ -34,8 +34,8 @@ func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, er errs = append(errs, err) continue } - - patchedDocument, err = ApplyPatchNew(patchedDocument, patchRaw) + patches := [][]byte{patchRaw} + patchedDocument, err = ApplyPatches(patchedDocument, patches) // TODO: continue on error if one of the patches fails, will add the failure event in such case if patch.Operation == "remove" { glog.Info(err) @@ -152,7 +152,7 @@ func processPatchesNew(rule kyverno.Rule, resource unstructured.Unstructured) (r // error while processing JSON patches if len(errs) > 0 { response.Success = false - response.Message = fmt.Sprint("failed to process JSON patches: %v", func() string { + response.Message = fmt.Sprintf("failed to process JSON patches: %v", func() string { var str []string for _, err := range errs { str = append(str, err.Error()) @@ -163,7 +163,7 @@ func processPatchesNew(rule kyverno.Rule, resource unstructured.Unstructured) (r } err = patchedResource.UnmarshalJSON(resourceRaw) if err != nil { - glog.Info("failed to unmarshall resource to undstructured: %v", err) + glog.Infof("failed to unmarshall resource to undstructured: %v", err) response.Success = false response.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return response, resource diff --git a/pkg/engine/pattern.go b/pkg/engine/pattern.go index 8327206b1c..e47c3dac3a 100644 --- a/pkg/engine/pattern.go +++ b/pkg/engine/pattern.go @@ -51,8 +51,10 @@ func ValidateValueWithPattern(value, pattern interface{}) bool { return validateValueWithStringPatterns(value, typedPattern) case nil: return validateValueWithNilPattern(value) - case map[string]interface{}, []interface{}: - glog.Warning("Maps and arrays as patterns are not supported") + case map[string]interface{}: + return validateValueWithMapPattern(value, typedPattern) + case []interface{}: + glog.Warning("Arrays as patterns are not supported") return false default: glog.Warningf("Unknown type as pattern: %T\n", pattern) @@ -60,6 +62,17 @@ func ValidateValueWithPattern(value, pattern interface{}) bool { } } +func validateValueWithMapPattern(value interface{}, typedPattern map[string]interface{}) bool { + // verify the type of the resource value is map[string]interface, + // we only check for existance of object, not the equality of content and value + _, ok := value.(map[string]interface{}) + if !ok { + glog.Warningf("Expected map[string]interface{}, found %T\n", value) + return false + } + return true +} + // Handler for int values during validation process func validateValueWithIntPattern(value interface{}, pattern int64) bool { switch typedValue := value.(type) { diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index 69e4dac447..17f3b76ba1 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -1571,8 +1571,8 @@ func TestValidate_ServiceTest(t *testing.T) { resourceUnstructured, err := ConvertToUnstructured(rawResource) assert.NilError(t, err) - res := Validate(policy, *resourceUnstructured) - assert.Assert(t, len(res.RuleInfos) == 0) + er := ValidateNew(policy, *resourceUnstructured) + assert.Assert(t, len(er.PolicyResponse.Rules) == 0) } func TestValidate_MapHasFloats(t *testing.T) { @@ -1668,6 +1668,6 @@ func TestValidate_MapHasFloats(t *testing.T) { resourceUnstructured, err := ConvertToUnstructured(rawResource) assert.NilError(t, err) - res := Validate(policy, *resourceUnstructured) - assert.Assert(t, len(res.RuleInfos) == 0) + er := ValidateNew(policy, *resourceUnstructured) + assert.Assert(t, len(er.PolicyResponse.Rules) == 0) } diff --git a/pkg/kyverno/apply/apply.go b/pkg/kyverno/apply/apply.go index e8b9ad2316..a329f7f9bc 100644 --- a/pkg/kyverno/apply/apply.go +++ b/pkg/kyverno/apply/apply.go @@ -10,7 +10,6 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/engine" - "github.com/nirmata/kyverno/pkg/info" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -100,42 +99,31 @@ func applyPolicyOnRaw(policy *kyverno.Policy, rawResource []byte, gvk *metav1.Gr rname := engine.ParseNameFromObject(rawResource) rns := engine.ParseNamespaceFromObject(rawResource) - policyInfo := info.NewPolicyInfo(policy.Name, - gvk.Kind, - rname, - rns, - policy.Spec.ValidationFailureAction) - resource, err := ConvertToUnstructured(rawResource) if err != nil { return nil, err } //TODO check if the kind information is present resource // Process Mutation - engineResponse := engine.Mutate(*policy, *resource) - policyInfo.AddRuleInfos(engineResponse.RuleInfos) - if !policyInfo.IsSuccessful() { + engineResponse := engine.MutateNew(*policy, *resource) + if !engineResponse.IsSuccesful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range engineResponse.RuleInfos { - glog.Warning(r.Msgs) + for _, r := range engineResponse.PolicyResponse.Rules { + glog.Warning(r.Message) } - } else if len(engineResponse.Patches) > 0 { + } else if len(engineResponse.PolicyResponse.Rules) > 0 { glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns) - patchedResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches) - if err != nil { - return nil, fmt.Errorf("Unable to apply mutation patches:\n%v", err) - } - // Process Validation - engineResponse := engine.Validate(*policy, *resource) - policyInfo.AddRuleInfos(engineResponse.RuleInfos) - if !policyInfo.IsSuccessful() { + // Process Validation + engineResponse := engine.ValidateNew(*policy, *resource) + + if !engineResponse.IsSuccesful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range engineResponse.RuleInfos { - glog.Warning(r.Msgs) + for _, r := range engineResponse.PolicyResponse.Rules { + glog.Warning(r.Message) } return patchedResource, fmt.Errorf("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - } else if len(engineResponse.RuleInfos) > 0 { + } else if len(engineResponse.PolicyResponse.Rules) > 0 { glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns) } } diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go index b438331c22..7e3ea2264e 100644 --- a/pkg/policy/apply.go +++ b/pkg/policy/apply.go @@ -47,7 +47,7 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, poli engineResponse, err = mutation(policy, resource, policyStatus) engineResponses = append(engineResponses, engineResponse) if err != nil { - glog.Error("unable to process mutation rules: %v", err) + glog.Errorf("unable to process mutation rules: %v", err) } gatherStat(policy.Name, engineResponse.PolicyResponse) //send stats diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 4ff81722a0..03d6770bca 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -465,17 +465,17 @@ func (pc *PolicyController) handleWebhookRegistration(delete bool, policy *kyver // check empty policy first, then rule type in terms of O(time) if policies == nil { glog.V(3).Infoln("No policy found in the cluster, deregistering webhook") - pc.webhookRegistrationClient.DeregisterMutatingWebhook() + pc.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration() } else if !HasMutateOrValidatePolicies(policies) { glog.V(3).Infoln("No muatate/validate policy found in the cluster, deregistering webhook") - pc.webhookRegistrationClient.DeregisterMutatingWebhook() + pc.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration() } return nil } if webhookList == nil && HasMutateOrValidate(*policy) { glog.V(3).Infoln("Found policy without mutatingwebhook, registering webhook") - pc.webhookRegistrationClient.RegisterMutatingWebhook() + pc.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration() } return nil diff --git a/pkg/webhookconfig/common.go b/pkg/webhookconfig/common.go new file mode 100644 index 0000000000..4486022dca --- /dev/null +++ b/pkg/webhookconfig/common.go @@ -0,0 +1,120 @@ +package webhookconfig + +import ( + "io/ioutil" + + "github.com/golang/glog" + "github.com/nirmata/kyverno/pkg/config" + admregapi "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rest "k8s.io/client-go/rest" +) + +func (wrc *WebhookRegistrationClient) readCaData() []byte { + var caData []byte + // Check if ca is defined in the secret tls-ca + // assume the key and signed cert have been defined in secret tls.kyverno + if caData = wrc.client.ReadRootCASecret(); len(caData) != 0 { + glog.V(4).Infof("read CA from secret") + return caData + } + glog.V(4).Infof("failed to read CA from secret, reading from kubeconfig") + // load the CA from kubeconfig + if caData = extractCA(wrc.clientConfig); len(caData) != 0 { + glog.V(4).Infof("read CA from kubeconfig") + return caData + } + glog.V(4).Infof("failed to read CA from kubeconfig") + return nil +} + +// ExtractCA used for extraction CA from config +func extractCA(config *rest.Config) (result []byte) { + fileName := config.TLSClientConfig.CAFile + + if fileName != "" { + result, err := ioutil.ReadFile(fileName) + + if err != nil { + return nil + } + + return result + } + + return config.TLSClientConfig.CAData +} + +func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference { + kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment() + + if err != nil { + glog.Errorf("Error when constructing OwnerReference, err: %v\n", err) + return v1.OwnerReference{} + } + + return v1.OwnerReference{ + APIVersion: config.DeploymentAPIVersion, + Kind: config.DeploymentKind, + Name: kubePolicyDeployment.ObjectMeta.Name, + UID: kubePolicyDeployment.ObjectMeta.UID, + } +} + +func generateDebugWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook { + return admregapi.Webhook{ + Name: name, + ClientConfig: admregapi.WebhookClientConfig{ + URL: &url, + CABundle: caData, + }, + Rules: []admregapi.RuleWithOperations{ + admregapi.RuleWithOperations{ + Operations: operationTypes, + Rule: admregapi.Rule{ + APIGroups: []string{ + apiGroups, + }, + APIVersions: []string{ + apiVersions, + }, + Resources: []string{ + resource, + }, + }, + }, + }, + TimeoutSeconds: &timeoutSeconds, + } +} + +func generateWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook { + return admregapi.Webhook{ + Name: name, + ClientConfig: admregapi.WebhookClientConfig{ + Service: &admregapi.ServiceReference{ + Namespace: config.KubePolicyNamespace, + Name: config.WebhookServiceName, + Path: &servicePath, + }, + CABundle: caData, + }, + Rules: []admregapi.RuleWithOperations{ + admregapi.RuleWithOperations{ + Operations: operationTypes, + Rule: admregapi.Rule{ + APIGroups: []string{ + apiGroups, + }, + APIVersions: []string{ + apiVersions, + }, + Resources: []string{ + resource, + }, + }, + }, + }, + TimeoutSeconds: &timeoutSeconds, + } +} diff --git a/pkg/webhookconfig/policy.go b/pkg/webhookconfig/policy.go new file mode 100644 index 0000000000..d7e4d2ab25 --- /dev/null +++ b/pkg/webhookconfig/policy.go @@ -0,0 +1,110 @@ +package webhookconfig + +import ( + "fmt" + + "github.com/golang/glog" + "github.com/nirmata/kyverno/pkg/config" + admregapi "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration { + + return &admregapi.ValidatingWebhookConfiguration{ + ObjectMeta: v1.ObjectMeta{ + Name: config.PolicyValidatingWebhookConfigurationName, + Labels: config.KubePolicyAppLabels, + OwnerReferences: []v1.OwnerReference{ + wrc.constructOwner(), + }, + }, + Webhooks: []admregapi.Webhook{ + generateWebhook( + config.PolicyValidatingWebhookName, + config.PolicyValidatingWebhookServicePath, + caData, + true, + wrc.timeoutSeconds, + "policies/*", + "kyverno.io", + "v1alpha1", + []admregapi.OperationType{admregapi.Create, admregapi.Update}, + ), + }, + } +} + +func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration { + url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyValidatingWebhookServicePath) + glog.V(4).Infof("Debug PolicyValidatingWebhookConfig is registered with url %s\n", url) + + return &admregapi.ValidatingWebhookConfiguration{ + ObjectMeta: v1.ObjectMeta{ + Name: config.PolicyValidatingWebhookConfigurationDebugName, + Labels: config.KubePolicyAppLabels, + }, + Webhooks: []admregapi.Webhook{ + generateDebugWebhook( + config.PolicyValidatingWebhookName, + url, + caData, + true, + wrc.timeoutSeconds, + "policies/*", + "kyverno.io", + "v1alpha1", + []admregapi.OperationType{admregapi.Create, admregapi.Update}, + ), + }, + } +} + +func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { + return &admregapi.MutatingWebhookConfiguration{ + ObjectMeta: v1.ObjectMeta{ + Name: config.PolicyMutatingWebhookConfigurationName, + Labels: config.KubePolicyAppLabels, + OwnerReferences: []v1.OwnerReference{ + wrc.constructOwner(), + }, + }, + Webhooks: []admregapi.Webhook{ + generateWebhook( + config.PolicyMutatingWebhookName, + config.PolicyMutatingWebhookServicePath, + caData, + true, + wrc.timeoutSeconds, + "policies/*", + "kyverno.io", + "v1alpha1", + []admregapi.OperationType{admregapi.Create, admregapi.Update}, + ), + }, + } +} +func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { + url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyMutatingWebhookServicePath) + glog.V(4).Infof("Debug PolicyMutatingWebhookConfig is registered with url %s\n", url) + + return &admregapi.MutatingWebhookConfiguration{ + ObjectMeta: v1.ObjectMeta{ + Name: config.PolicyMutatingWebhookConfigurationDebugName, + Labels: config.KubePolicyAppLabels, + }, + Webhooks: []admregapi.Webhook{ + generateDebugWebhook( + config.PolicyMutatingWebhookName, + url, + caData, + true, + wrc.timeoutSeconds, + "policies/*", + "kyverno.io", + "v1alpha1", + []admregapi.OperationType{admregapi.Create, admregapi.Update}, + ), + }, + } +} diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go index f4cdfa8154..a8f036484e 100644 --- a/pkg/webhookconfig/registration.go +++ b/pkg/webhookconfig/registration.go @@ -2,9 +2,7 @@ package webhookconfig import ( "errors" - "fmt" - "io/ioutil" - "strings" + "time" "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/config" @@ -56,19 +54,57 @@ func (wrc *WebhookRegistrationClient) Register() error { } // For the case if cluster already has this configs - wrc.DeregisterAll() + // remove previously create webhookconfigurations if any + // webhook configurations are created dynamically based on the policy resources + wrc.removeWebhookConfigurations() - // register policy validating webhook during inital start - return wrc.RegisterPolicyValidatingWebhook() -} - -func (wrc *WebhookRegistrationClient) RegisterMutatingWebhook() error { - mutatingWebhookConfig, err := wrc.constructMutatingWebhookConfig(wrc.clientConfig) - if err != nil { + // Static Webhook configuration on Policy CRD + // create Policy CRD validating webhook configuration resource + // used for validating Policy CR + if err := wrc.createPolicyValidatingWebhookConfiguration(); err != nil { return err } + // create Policy CRD validating webhook configuration resource + // used for defauling values in Policy CR + if err := wrc.createPolicyMutatingWebhookConfiguration(); err != nil { + return err + } + return nil +} - if _, err = wrc.registrationClient.MutatingWebhookConfigurations().Create(mutatingWebhookConfig); err != nil { +// RemovePolicyWebhookConfigurations removes webhook configurations for reosurces and policy +// called during webhook server shutdown +func (wrc *WebhookRegistrationClient) RemovePolicyWebhookConfigurations(cleanUp chan<- struct{}) { + //TODO: dupliate, but a placeholder to perform more error handlind during cleanup + wrc.removeWebhookConfigurations() + // close channel to notify cleanup is complete + close(cleanUp) +} + +//CreateResourceMutatingWebhookConfiguration create a Mutatingwebhookconfiguration resource for all resource type +// used to forward request to kyverno webhooks to apply policeis +// Mutationg webhook is be used for Mutating & Validating purpose +func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration() error { + var caData []byte + var config *admregapi.MutatingWebhookConfiguration + + // read CA data from + // 1) secret(config) + // 2) kubeconfig + if caData = wrc.readCaData(); caData == nil { + return errors.New("Unable to extract CA data from configuration") + } + // if serverIP is specified we assume its debug mode + if wrc.serverIP != "" { + // debug mode + // clientConfig - URL + config = wrc.contructDebugMutatingWebhookConfig(caData) + } else { + // clientConfig - service + config = wrc.constructMutatingWebhookConfig(caData) + } + + if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil { return err } @@ -76,384 +112,126 @@ func (wrc *WebhookRegistrationClient) RegisterMutatingWebhook() error { return nil } -func (wrc *WebhookRegistrationClient) RegisterValidatingWebhook() error { - validationWebhookConfig, err := wrc.constructValidatingWebhookConfig(wrc.clientConfig) - if err != nil { +//registerPolicyValidatingWebhookConfiguration create a Validating webhook configuration for Policy CRD +func (wrc *WebhookRegistrationClient) createPolicyValidatingWebhookConfiguration() error { + var caData []byte + var config *admregapi.ValidatingWebhookConfiguration + + // read CA data from + // 1) secret(config) + // 2) kubeconfig + if caData = wrc.readCaData(); caData == nil { + return errors.New("Unable to extract CA data from configuration") + } + + // if serverIP is specified we assume its debug mode + if wrc.serverIP != "" { + // debug mode + // clientConfig - URL + config = wrc.contructDebugPolicyValidatingWebhookConfig(caData) + } else { + // clientConfig - service + config = wrc.contructPolicyValidatingWebhookConfig(caData) + } + + // create validating webhook configuration resource + if _, err := wrc.registrationClient.ValidatingWebhookConfigurations().Create(config); err != nil { return err } - if _, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(validationWebhookConfig); err != nil { - return err - } - - wrc.ValidationRegistered.Set() + glog.V(4).Infof("created Validating Webhook Configuration %s ", config.Name) return nil } -func (wrc *WebhookRegistrationClient) RegisterPolicyValidatingWebhook() error { - policyValidationWebhookConfig, err := wrc.contructPolicyValidatingWebhookConfig() - if err != nil { +func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration() error { + var caData []byte + var config *admregapi.MutatingWebhookConfiguration + + // read CA data from + // 1) secret(config) + // 2) kubeconfig + if caData = wrc.readCaData(); caData == nil { + return errors.New("Unable to extract CA data from configuration") + } + + // if serverIP is specified we assume its debug mode + if wrc.serverIP != "" { + // debug mode + // clientConfig - URL + config = wrc.contructDebugPolicyMutatingWebhookConfig(caData) + } else { + // clientConfig - service + config = wrc.contructPolicyMutatingWebhookConfig(caData) + } + + // create mutating webhook configuration resource + if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil { return err } - if _, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(policyValidationWebhookConfig); err != nil { - return err - } - - glog.V(3).Infoln("Policy validating webhook registered") + glog.V(4).Infof("created Mutating Webhook Configuration %s ", config.Name) return nil } // 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) DeregisterAll() { - wrc.DeregisterMutatingWebhook() - wrc.deregisterValidatingWebhook() +func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() { + startTime := time.Now() + glog.V(4).Infof("Started cleaning up webhookconfigurations") + defer func() { + glog.V(4).Infof("Finished cleaning up webhookcongfigurations (%v)", time.Since(startTime)) + }() + // mutating and validating webhook configuration for Kubernetes resources + wrc.RemoveResourceMutatingWebhookConfiguration() + // mutating and validating webhook configurtion for Policy CRD resource + wrc.removePolicyWebhookConfigurations() +} + +// removePolicyWebhookConfigurations removes mutating and validating webhook configurations, if already presnt +// webhookConfigurations are re-created later +func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() { + // Validating webhook configuration + var validatingConfig string if wrc.serverIP != "" { - err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationDebug, &v1.DeleteOptions{}) - if err != nil && !errorsapi.IsNotFound(err) { - glog.Error(err) - } + validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName + } else { + validatingConfig = config.PolicyValidatingWebhookConfigurationName } - err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationName, &v1.DeleteOptions{}) + glog.V(4).Infof("removing webhook configuration %s", validatingConfig) + err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{}) if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } + + // Mutating webhook configuration + var mutatingConfig string + if wrc.serverIP != "" { + mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName + } else { + mutatingConfig = config.PolicyMutatingWebhookConfigurationName + } + + glog.V(4).Infof("removing webhook configuration %s", mutatingConfig) + if err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{}); err != nil && !errorsapi.IsNotFound(err) { + glog.Error(err) + } } -func (wrc *WebhookRegistrationClient) deregister() { - wrc.DeregisterMutatingWebhook() - wrc.deregisterValidatingWebhook() -} - -func (wrc *WebhookRegistrationClient) DeregisterMutatingWebhook() { +//RemoveResourceMutatingWebhookConfiguration removes mutating webhook configuration for all resources +func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration() { + var configName string if wrc.serverIP != "" { - err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &v1.DeleteOptions{}) - if err != nil && !errorsapi.IsNotFound(err) { - glog.Error(err) - } else { - wrc.MutationRegistered.UnSet() - } - return + configName = config.MutatingWebhookConfigurationDebug + } else { + configName = config.MutatingWebhookConfigurationName } - - err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationName, &v1.DeleteOptions{}) + // delete webhook configuration + err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(configName, &v1.DeleteOptions{}) if err != nil && !errorsapi.IsNotFound(err) { 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 && !errorsapi.IsNotFound(err) { - glog.Error(err) - } - wrc.ValidationRegistered.UnSet() - return - } - - err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationName, &v1.DeleteOptions{}) - if err != nil && !errorsapi.IsNotFound(err) { - 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 - // assume the key and signed cert have been defined in secret tls.kyverno - caData = wrc.client.ReadRootCASecret() - if len(caData) == 0 { - // load the CA from kubeconfig - caData = extractCA(configuration) - } - if len(caData) == 0 { - return nil, errors.New("Unable to extract CA data from configuration") - } - - if wrc.serverIP != "" { - return wrc.contructDebugMutatingWebhookConfig(caData), nil - } - - return &admregapi.MutatingWebhookConfiguration{ - ObjectMeta: v1.ObjectMeta{ - Name: config.MutatingWebhookConfigurationName, - Labels: config.KubePolicyAppLabels, - OwnerReferences: []v1.OwnerReference{ - wrc.constructOwner(), - }, - }, - Webhooks: []admregapi.Webhook{ - constructWebhook( - config.MutatingWebhookName, - config.MutatingWebhookServicePath, - caData, - false, - wrc.timeoutSeconds, - ), - }, - }, nil -} - -func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { - url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath) - glog.V(3).Infof("Debug MutatingWebhookConfig is registered with url %s\n", url) - - return &admregapi.MutatingWebhookConfiguration{ - ObjectMeta: v1.ObjectMeta{ - Name: config.MutatingWebhookConfigurationDebug, - Labels: config.KubePolicyAppLabels, - }, - Webhooks: []admregapi.Webhook{ - constructDebugWebhook( - config.MutatingWebhookName, - url, - caData, - false, - wrc.timeoutSeconds), - }, - } -} - -func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(configuration *rest.Config) (*admregapi.ValidatingWebhookConfiguration, error) { - // Check if ca is defined in the secret tls-ca - // assume the key and signed cert have been defined in secret tls.kyverno - caData := wrc.client.ReadRootCASecret() - if len(caData) == 0 { - // load the CA from kubeconfig - caData = extractCA(configuration) - } - if len(caData) == 0 { - return nil, errors.New("Unable to extract CA data from configuration") - } - - if wrc.serverIP != "" { - return wrc.contructDebugValidatingWebhookConfig(caData), nil - } - - return &admregapi.ValidatingWebhookConfiguration{ - ObjectMeta: v1.ObjectMeta{ - Name: config.ValidatingWebhookConfigurationName, - Labels: config.KubePolicyAppLabels, - OwnerReferences: []v1.OwnerReference{ - wrc.constructOwner(), - }, - }, - Webhooks: []admregapi.Webhook{ - constructWebhook( - config.ValidatingWebhookName, - config.ValidatingWebhookServicePath, - caData, - true, - wrc.timeoutSeconds), - }, - }, nil -} - -func (wrc *WebhookRegistrationClient) contructDebugValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration { - url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.ValidatingWebhookServicePath) - glog.V(3).Infof("Debug ValidatingWebhookConfig is registered with url %s\n", url) - - return &admregapi.ValidatingWebhookConfiguration{ - ObjectMeta: v1.ObjectMeta{ - Name: config.ValidatingWebhookConfigurationDebug, - Labels: config.KubePolicyAppLabels, - }, - Webhooks: []admregapi.Webhook{ - constructDebugWebhook( - config.ValidatingWebhookName, - url, - caData, - true, - wrc.timeoutSeconds), - }, - } -} - -func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig() (*admregapi.ValidatingWebhookConfiguration, error) { - // Check if ca is defined in the secret tls-ca - // assume the key and signed cert have been defined in secret tls.kyverno - caData := wrc.client.ReadRootCASecret() - if len(caData) == 0 { - // load the CA from kubeconfig - caData = extractCA(wrc.clientConfig) - } - if len(caData) == 0 { - return nil, errors.New("Unable to extract CA data from configuration") - } - - if wrc.serverIP != "" { - return wrc.contructDebugPolicyValidatingWebhookConfig(caData), nil - } - - return &admregapi.ValidatingWebhookConfiguration{ - ObjectMeta: v1.ObjectMeta{ - Name: config.PolicyValidatingWebhookConfigurationName, - Labels: config.KubePolicyAppLabels, - OwnerReferences: []v1.OwnerReference{ - wrc.constructOwner(), - }, - }, - Webhooks: []admregapi.Webhook{ - constructWebhook( - config.PolicyValidatingWebhookName, - config.PolicyValidatingWebhookServicePath, - caData, - true, - wrc.timeoutSeconds), - }, - }, nil -} - -func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration { - url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyValidatingWebhookServicePath) - glog.V(3).Infof("Debug PolicyValidatingWebhookConfig is registered with url %s\n", url) - - return &admregapi.ValidatingWebhookConfiguration{ - ObjectMeta: v1.ObjectMeta{ - Name: config.PolicyValidatingWebhookConfigurationDebug, - Labels: config.KubePolicyAppLabels, - }, - Webhooks: []admregapi.Webhook{ - constructDebugWebhook( - config.PolicyValidatingWebhookName, - url, - caData, - true, - wrc.timeoutSeconds), - }, - } -} - -func constructWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32) admregapi.Webhook { - resource := "*/*" - apiGroups := "*" - apiversions := "*" - if servicePath == config.PolicyValidatingWebhookServicePath { - resource = "policies/*" - apiGroups = "kyverno.io" - apiversions = "v1alpha1" - } - operationtypes := []admregapi.OperationType{ - admregapi.Create, - admregapi.Update, - } - // // Add operation DELETE for validation - // if validation { - // operationtypes = append(operationtypes, admregapi.Delete) - - // } - - return admregapi.Webhook{ - Name: name, - ClientConfig: admregapi.WebhookClientConfig{ - Service: &admregapi.ServiceReference{ - Namespace: config.KubePolicyNamespace, - Name: config.WebhookServiceName, - Path: &servicePath, - }, - CABundle: caData, - }, - Rules: []admregapi.RuleWithOperations{ - admregapi.RuleWithOperations{ - Operations: operationtypes, - Rule: admregapi.Rule{ - APIGroups: []string{ - apiGroups, - }, - APIVersions: []string{ - apiversions, - }, - Resources: []string{ - resource, - }, - }, - }, - }, - TimeoutSeconds: &timeoutSeconds, - } -} - -func constructDebugWebhook(name, url string, caData []byte, validation bool, timeoutSeconds int32) admregapi.Webhook { - resource := "*/*" - apiGroups := "*" - apiversions := "*" - - if strings.Contains(url, config.PolicyValidatingWebhookServicePath) { - resource = "policies/*" - apiGroups = "kyverno.io" - apiversions = "v1alpha1" - } - operationtypes := []admregapi.OperationType{ - admregapi.Create, - admregapi.Update, - } - // // Add operation DELETE for validation - // if validation { - // operationtypes = append(operationtypes, admregapi.Delete) - // } - - return admregapi.Webhook{ - Name: name, - ClientConfig: admregapi.WebhookClientConfig{ - URL: &url, - CABundle: caData, - }, - Rules: []admregapi.RuleWithOperations{ - admregapi.RuleWithOperations{ - Operations: operationtypes, - Rule: admregapi.Rule{ - APIGroups: []string{ - apiGroups, - }, - APIVersions: []string{ - apiversions, - }, - Resources: []string{ - resource, - }, - }, - }, - }, - TimeoutSeconds: &timeoutSeconds, - } -} - -func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference { - kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment() - - if err != nil { - glog.Errorf("Error when constructing OwnerReference, err: %v\n", err) - return v1.OwnerReference{} - } - - return v1.OwnerReference{ - APIVersion: config.DeploymentAPIVersion, - Kind: config.DeploymentKind, - Name: kubePolicyDeployment.ObjectMeta.Name, - UID: kubePolicyDeployment.ObjectMeta.UID, - } -} - -// ExtractCA used for extraction CA from config -func extractCA(config *rest.Config) (result []byte) { - fileName := config.TLSClientConfig.CAFile - - if fileName != "" { - result, err := ioutil.ReadFile(fileName) - - if err != nil { - return nil - } - - return result - } - - return config.TLSClientConfig.CAData -} diff --git a/pkg/webhookconfig/resource.go b/pkg/webhookconfig/resource.go new file mode 100644 index 0000000000..63d44d28d6 --- /dev/null +++ b/pkg/webhookconfig/resource.go @@ -0,0 +1,60 @@ +package webhookconfig + +import ( + "fmt" + + "github.com/golang/glog" + "github.com/nirmata/kyverno/pkg/config" + admregapi "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { + url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath) + glog.V(3).Infof("Debug MutatingWebhookConfig is registered with url %s\n", url) + + return &admregapi.MutatingWebhookConfiguration{ + ObjectMeta: v1.ObjectMeta{ + Name: config.MutatingWebhookConfigurationDebug, + Labels: config.KubePolicyAppLabels, + }, + Webhooks: []admregapi.Webhook{ + generateDebugWebhook( + config.MutatingWebhookName, + url, + caData, + true, + wrc.timeoutSeconds, + "*/*", + "*", + "*", + []admregapi.OperationType{admregapi.Create}, + ), + }, + } +} + +func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { + return &admregapi.MutatingWebhookConfiguration{ + ObjectMeta: v1.ObjectMeta{ + Name: config.MutatingWebhookConfigurationName, + Labels: config.KubePolicyAppLabels, + OwnerReferences: []v1.OwnerReference{ + wrc.constructOwner(), + }, + }, + Webhooks: []admregapi.Webhook{ + generateWebhook( + config.MutatingWebhookName, + config.MutatingWebhookServicePath, + caData, + false, + wrc.timeoutSeconds, + "*/*", + "*", + "*", + []admregapi.OperationType{admregapi.Create}, + ), + }, + } +} diff --git a/pkg/webhooks/annotations.go b/pkg/webhooks/annotations.go index e15addc3a4..cf1653e491 100644 --- a/pkg/webhooks/annotations.go +++ b/pkg/webhooks/annotations.go @@ -139,7 +139,7 @@ func generateAnnotationsFromPolicyResponse(policyResponse engine.PolicyResponse) } patch, err := json.Marshal(rulePatches) if err != nil { - glog.Info("failed to marshall: %v", err) + glog.Infof("failed to marshall: %v", err) return nil } return patch diff --git a/pkg/webhooks/policymutation.go b/pkg/webhooks/policymutation.go new file mode 100644 index 0000000000..58cd9a67c0 --- /dev/null +++ b/pkg/webhooks/policymutation.go @@ -0,0 +1,84 @@ +package webhooks + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/utils" + v1beta1 "k8s.io/api/admission/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { + var policy *kyverno.Policy + raw := request.Object.Raw + + //TODO: can this happen? wont this be picked by OpenAPI spec schema ? + if err := json.Unmarshal(raw, &policy); err != nil { + glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err) + return &v1beta1.AdmissionResponse{ + Allowed: true, + Result: &metav1.Status{ + Message: fmt.Sprintf("failed to default value, check kyverno controller logs for details: %v", err), + }, + } + } + // Generate JSON Patches for defaults + patches, updateMsgs := generateJSONPatchesForDefaults(policy) + if patches != nil { + patchType := v1beta1.PatchTypeJSONPatch + glog.V(4).Infof("defaulted values %v policy %s", updateMsgs, policy.Name) + return &v1beta1.AdmissionResponse{ + Allowed: true, + Result: &metav1.Status{ + Message: strings.Join(updateMsgs, "'"), + }, + Patch: patches, + PatchType: &patchType, + } + } + glog.V(4).Infof("nothing to default for policy %s", policy.Name) + return &v1beta1.AdmissionResponse{ + Allowed: true, + } +} + +func generateJSONPatchesForDefaults(policy *kyverno.Policy) ([]byte, []string) { + var patches [][]byte + var updateMsgs []string + + // default 'ValidationFailureAction' + if patch, updateMsg := defaultvalidationFailureAction(policy); patch != nil { + patches = append(patches, patch) + updateMsgs = append(updateMsgs, updateMsg) + } + + return utils.JoinPatches(patches), updateMsgs +} + +func defaultvalidationFailureAction(policy *kyverno.Policy) ([]byte, string) { + // default ValidationFailureAction to "enforce" if not specified + if policy.Spec.ValidationFailureAction == "" { + glog.V(4).Infof("defaulting policy %s 'ValidationFailureAction' to '%s'", policy.Name, BlockChanges) + jsonPatch := struct { + Path string `json:"path"` + Op string `json:"op"` + Value string `json:"value"` + }{ + "/spec/validationFailureAction", + "add", + BlockChanges, //enforce + } + patchByte, err := json.Marshal(jsonPatch) + if err != nil { + glog.Errorf("failed to set default 'ValidationFailureAction' to '%s' for policy %s", BlockChanges, policy.Name) + return nil, "" + } + glog.V(4).Infof("generate JSON Patch to set default 'ValidationFailureAction' to '%s' for policy %s", BlockChanges, policy.Name) + return patchByte, fmt.Sprintf("default 'ValidationFailureAction' to '%s'", BlockChanges) + } + return nil, "" +} diff --git a/pkg/webhooks/policyvalidation.go b/pkg/webhooks/policyvalidation.go index 8bb07d22b3..addcf08f1c 100644 --- a/pkg/webhooks/policyvalidation.go +++ b/pkg/webhooks/policyvalidation.go @@ -13,16 +13,13 @@ import ( ) //HandlePolicyValidation performs the validation check on policy resource -func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { +func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { var policy *kyverno.Policy admissionResp := &v1beta1.AdmissionResponse{ Allowed: true, } - // nothing to do on DELETE - if request.Operation == v1beta1.Delete { - return admissionResp - } + //TODO: can this happen? wont this be picked by OpenAPI spec schema ? raw := request.Object.Raw if err := json.Unmarshal(raw, &policy); err != nil { glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err) diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 66182ba3bd..7540ab1dec 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -41,6 +41,7 @@ type WebhookServer struct { // API to send policy stats for aggregation policyStatus policy.PolicyStatusInterface filterK8Resources []utils.K8Resource + cleanUp chan<- struct{} } // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration @@ -54,7 +55,8 @@ func NewWebhookServer( eventGen event.Interface, webhookRegistrationClient *webhookconfig.WebhookRegistrationClient, policyStatus policy.PolicyStatusInterface, - filterK8Resources string) (*WebhookServer, error) { + filterK8Resources string, + cleanUp chan<- struct{}) (*WebhookServer, error) { if tlsPair == nil { return nil, errors.New("NewWebhookServer is not initialized properly") @@ -79,11 +81,13 @@ func NewWebhookServer( webhookRegistrationClient: webhookRegistrationClient, policyStatus: policyStatus, filterK8Resources: utils.ParseKinds(filterK8Resources), + cleanUp: cleanUp, } mux := http.NewServeMux() mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve) mux.HandleFunc(config.ValidatingWebhookServicePath, ws.serve) mux.HandleFunc(config.PolicyValidatingWebhookServicePath, ws.serve) + mux.HandleFunc(config.PolicyMutatingWebhookServicePath, ws.serve) ws.server = http.Server{ Addr: ":443", // Listen on port for HTTPS requests @@ -113,9 +117,11 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { // Resource UPDATE switch r.URL.Path { case config.MutatingWebhookServicePath: - admissionReview.Response = ws.HandleAdmissionRequest(admissionReview.Request) + admissionReview.Response = ws.handleAdmissionRequest(admissionReview.Request) case config.PolicyValidatingWebhookServicePath: - admissionReview.Response = ws.HandlePolicyValidation(admissionReview.Request) + admissionReview.Response = ws.handlePolicyValidation(admissionReview.Request) + case config.PolicyMutatingWebhookServicePath: + admissionReview.Response = ws.handlePolicyMutation(admissionReview.Request) } } @@ -133,7 +139,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { } } -func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { +func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { // MUTATION ok, patches, msg := ws.HandleMutation(request) if !ok { @@ -177,9 +183,8 @@ func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionReques func (ws *WebhookServer) RunAsync() { go func(ws *WebhookServer) { glog.V(3).Infof("serving on %s\n", ws.server.Addr) - err := ws.server.ListenAndServeTLS("", "") - if err != nil { - glog.Fatalf("error serving TLS: %v\n", err) + if err := ws.server.ListenAndServeTLS("", ""); err != http.ErrServerClosed { + glog.Infof("HTTP server error: %v", err) } }(ws) glog.Info("Started Webhook Server") @@ -193,6 +198,10 @@ func (ws *WebhookServer) Stop() { glog.Info("Server Shutdown error: ", err) ws.server.Close() } + // cleanUp + // remove the static webhookconfigurations for policy CRD + ws.webhookRegistrationClient.RemovePolicyWebhookConfigurations(ws.cleanUp) + } // bodyToAdmissionReview creates AdmissionReview object from request body diff --git a/pkg/webhooks/utils.go b/pkg/webhooks/utils.go index aca779bde4..c64917f54f 100644 --- a/pkg/webhooks/utils.go +++ b/pkg/webhooks/utils.go @@ -52,7 +52,7 @@ func getErrorMsg(engineReponses []engine.EngineResponseNew) string { var str []string for _, er := range engineReponses { if !er.IsSuccesful() { - str = append(str, fmt.Sprintf("failed policy %s"), er.PolicyResponse.Policy) + 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()) diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index e808bb53aa..19d3e00345 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -33,7 +33,7 @@ func (ws *WebhookServer) registerWebhookConfigurations(policy kyverno.Policy) er } if !ws.webhookRegistrationClient.MutationRegistered.IsSet() { - if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { + if err := ws.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil { return err } glog.Infof("Mutating webhook registered") @@ -47,7 +47,7 @@ func (ws *WebhookServer) deregisterWebhookConfigurations(policy kyverno.Policy) // deregister webhook if no mutate/validate policy found in cluster if !HasMutateOrValidatePolicies(policies) { - ws.webhookRegistrationClient.DeregisterMutatingWebhook() + ws.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration() glog.Infoln("Mutating webhook deregistered") }