1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00

refactor webhook configuration

This commit is contained in:
shivkumar dudhani 2019-09-04 13:43:12 -07:00
parent b66c1b7f0c
commit c2e822c887
8 changed files with 176 additions and 200 deletions

View file

@ -7,13 +7,13 @@ const (
KubePolicyNamespace = "kyverno"
WebhookServiceName = "kyverno-svc"
MutatingWebhookConfigurationName = "kyverno-mutating-webhook-cfg"
MutatingWebhookConfigurationDebug = "kyverno-mutating-webhook-cfg-debug"
MutatingWebhookName = "nirmata.kyverno.mutating-webhook"
MutatingWebhookConfigurationName = "kyverno-resource-mutating-webhook-cfg"
MutatingWebhookConfigurationDebugName = "kyverno-resource-mutating-webhook-cfg-debug"
MutatingWebhookName = "nirmata.kyverno.resource.mutating-webhook"
ValidatingWebhookConfigurationName = "kyverno-validating-webhook-cfg"
ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
ValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
// ValidatingWebhookConfigurationName = "kyverno-validating-webhook-cfg"
// ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
// ValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
@ -36,9 +36,9 @@ var (
ValidatingWebhookServicePath = "/validate"
PolicyValidatingWebhookServicePath = "/policyvalidate"
PolicyMutatingWebhookServicePath = "/policymutate"
KubePolicyAppLabels = map[string]string{
"app": "kyverno",
}
// KubePolicyAppLabels = map[string]string{
// "app": "kyverno",
// }
SupportedKinds = []string{
"ConfigMap",

View file

@ -13,7 +13,6 @@ import (
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1"
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
"github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/event"
"github.com/nirmata/kyverno/pkg/utils"
@ -409,17 +408,21 @@ func (pc *PolicyController) syncPolicy(key string) error {
// remove the recorded stats for the policy
pc.statusAggregator.RemovePolicyStats(key)
// remove webhook configurations if there are not policies
if err := pc.handleWebhookRegistration(true, nil); err != nil {
if err := pc.removeResourceWebhookConfiguration(); err != nil {
// do not fail, if unable to delete resource webhook config
glog.V(4).Info("failed to remove resource webhook configuration: %v", err)
glog.Errorln(err)
}
return nil
}
if err != nil {
glog.V(4).Info(err)
return err
}
if err := pc.handleWebhookRegistration(false, policy); err != nil {
if err := pc.createResourceMutatingWebhookConfigurationIfRequired(*policy); err != nil {
glog.V(4).Infof("failed to create resource mutating webhook configurations, policies wont be applied on resources: %v", err)
glog.Errorln(err)
}
@ -440,47 +443,6 @@ func (pc *PolicyController) syncPolicy(key string) error {
return pc.syncStatusOnly(p, pvList)
}
// TODO: here checks mutatingwebhook only
// as 'kubectl scale' is not funtional with validatingwebhook
// refer to https://github.com/nirmata/kyverno/issues/250
func (pc *PolicyController) handleWebhookRegistration(delete bool, policy *kyverno.ClusterPolicy) error {
policies, _ := pc.pLister.List(labels.NewSelector())
selector := &metav1.LabelSelector{MatchLabels: config.KubePolicyAppLabels}
webhookSelector, err := metav1.LabelSelectorAsSelector(selector)
if err != nil {
return fmt.Errorf("invalid label selector: %v", err)
}
webhookList, err := pc.mutationwebhookLister.List(webhookSelector)
if err != nil {
return fmt.Errorf("failed to list mutatingwebhookconfigurations, err %v", err)
}
if delete {
if webhookList == nil {
return nil
}
// webhook exist, deregister webhookconfigurations on condition
// 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.RemoveResourceMutatingWebhookConfiguration()
} else if !HasMutateOrValidatePolicies(policies) {
glog.V(3).Infoln("No muatate/validate policy found in the cluster, deregistering webhook")
pc.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration()
}
return nil
}
if webhookList == nil && HasMutateOrValidate(*policy) {
glog.V(3).Infoln("Found policy without mutatingwebhook, registering webhook")
pc.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration()
}
return nil
}
//syncStatusOnly updates the policy status subresource
// status:
// - violations : (count of the resources that violate this policy )
@ -930,22 +892,3 @@ func joinPatches(patches ...[]byte) []byte {
result = append(result, []byte("\n]")...)
return result
}
func HasMutateOrValidatePolicies(policies []*kyverno.ClusterPolicy) bool {
for _, policy := range policies {
if HasMutateOrValidate(*policy) {
return true
}
}
return false
}
func HasMutateOrValidate(policy kyverno.ClusterPolicy) bool {
for _, rule := range policy.Spec.Rules {
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
glog.Infoln(rule.Name)
return true
}
}
return false
}

View file

@ -0,0 +1,71 @@
package policy
import (
"reflect"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
"k8s.io/apimachinery/pkg/labels"
)
func (pc *PolicyController) removeResourceWebhookConfiguration() error {
removeWebhookConfig := func() error {
var err error
err = pc.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration()
if err != nil {
return err
}
glog.V(4).Info("removed resource webhook configuration")
return nil
}
var err error
// get all existing policies
policies, err := pc.pLister.List(labels.NewSelector())
if err != nil {
glog.V(4).Infof("failed to list policies: %v", err)
return err
}
if len(policies) == 0 {
glog.V(4).Info("no policies loaded, removing resource webhook configuration if one exists")
return removeWebhookConfig()
}
// if there are policies, check if they contain mutating or validating rule
if !hasMutateOrValidatePolicies(policies) {
glog.V(4).Info("no policies with mutating or validating webhook configurations, remove resource webhook configuration if one exists")
return removeWebhookConfig()
}
return nil
}
func (pc *PolicyController) createResourceMutatingWebhookConfigurationIfRequired(policy kyverno.ClusterPolicy) error {
// if the policy contains mutating & validation rules and it config does not exist we create one
if hasMutateOrValidate(policy) {
if err := pc.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil {
return err
}
}
return nil
}
func hasMutateOrValidatePolicies(policies []*kyverno.ClusterPolicy) bool {
for _, policy := range policies {
if hasMutateOrValidate(*policy) {
return true
}
}
return false
}
func hasMutateOrValidate(policy kyverno.ClusterPolicy) bool {
for _, rule := range policy.Spec.Rules {
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
glog.Infoln(rule.Name)
return true
}
}
return false
}

View file

@ -13,8 +13,7 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caDa
return &admregapi.ValidatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.PolicyValidatingWebhookConfigurationName,
Labels: config.KubePolicyAppLabels,
Name: config.PolicyValidatingWebhookConfigurationName,
OwnerReferences: []v1.OwnerReference{
wrc.constructOwner(),
},
@ -41,8 +40,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig
return &admregapi.ValidatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.PolicyValidatingWebhookConfigurationDebugName,
Labels: config.KubePolicyAppLabels,
Name: config.PolicyValidatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
@ -63,8 +61,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig
func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
return &admregapi.MutatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.PolicyMutatingWebhookConfigurationName,
Labels: config.KubePolicyAppLabels,
Name: config.PolicyMutatingWebhookConfigurationName,
OwnerReferences: []v1.OwnerReference{
wrc.constructOwner(),
},
@ -90,8 +87,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(c
return &admregapi.MutatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.PolicyMutatingWebhookConfigurationDebugName,
Labels: config.KubePolicyAppLabels,
Name: config.PolicyMutatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(

View file

@ -7,7 +7,6 @@ import (
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/tevino/abool"
admregapi "k8s.io/api/admissionregistration/v1beta1"
errorsapi "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -21,10 +20,8 @@ type WebhookRegistrationClient struct {
client *client.Client
clientConfig *rest.Config
// serverIP should be used if running Kyverno out of clutser
serverIP string
timeoutSeconds int32
MutationRegistered *abool.AtomicBool
ValidationRegistered *abool.AtomicBool
serverIP string
timeoutSeconds int32
}
// NewWebhookRegistrationClient creates new WebhookRegistrationClient instance
@ -34,16 +31,14 @@ func NewWebhookRegistrationClient(clientConfig *rest.Config, client *client.Clie
return nil, err
}
glog.V(3).Infof("Registering webhook client using serverIP %s\n", serverIP)
glog.V(4).Infof("Registering webhook client using serverIP %s\n", serverIP)
return &WebhookRegistrationClient{
registrationClient: registrationClient,
client: client,
clientConfig: clientConfig,
serverIP: serverIP,
timeoutSeconds: webhookTimeout,
MutationRegistered: abool.New(),
ValidationRegistered: abool.New(),
registrationClient: registrationClient,
client: client,
clientConfig: clientConfig,
serverIP: serverIP,
timeoutSeconds: webhookTimeout,
}, nil
}
@ -104,14 +99,30 @@ func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration
config = wrc.constructMutatingWebhookConfig(caData)
}
if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil {
_, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config)
if errorsapi.IsAlreadyExists(err) {
glog.V(4).Infof("resource mutating webhook configuration %s, already exists. not creating one", config.Name)
return nil
}
if err != nil {
glog.V(4).Infof("failed to create resource mutating webhook configuration %s: %v", config.Name, err)
return err
}
wrc.MutationRegistered.Set()
return nil
}
//GetResourceMutatingWebhookConfiguration returns the MutatingWebhookConfiguration
func (wrc *WebhookRegistrationClient) GetResourceMutatingWebhookConfiguration() (*admregapi.MutatingWebhookConfiguration, error) {
var name string
if wrc.serverIP != "" {
name = config.MutatingWebhookConfigurationDebugName
} else {
name = config.MutatingWebhookConfigurationName
}
return wrc.registrationClient.MutatingWebhookConfigurations().Get(name, v1.GetOptions{})
}
//registerPolicyValidatingWebhookConfiguration create a Validating webhook configuration for Policy CRD
func (wrc *WebhookRegistrationClient) createPolicyValidatingWebhookConfiguration() error {
var caData []byte
@ -193,6 +204,7 @@ func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() {
// webhookConfigurations are re-created later
func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
// Validating webhook configuration
var err error
var validatingConfig string
if wrc.serverIP != "" {
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
@ -200,9 +212,13 @@ func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
validatingConfig = config.PolicyValidatingWebhookConfigurationName
}
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)
err = wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{})
if errorsapi.IsNotFound(err) {
glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", validatingConfig)
} else if err != nil {
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
} else {
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
}
// Mutating webhook configuration
@ -214,24 +230,12 @@ func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
}
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)
}
}
//RemoveResourceMutatingWebhookConfiguration removes mutating webhook configuration for all resources
func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration() {
var configName string
if wrc.serverIP != "" {
configName = config.MutatingWebhookConfigurationDebug
} else {
configName = config.MutatingWebhookConfigurationName
}
// delete webhook configuration
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(configName, &v1.DeleteOptions{})
if err != nil && !errorsapi.IsNotFound(err) {
glog.Error(err)
} else {
wrc.MutationRegistered.UnSet()
err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
if errorsapi.IsNotFound(err) {
glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", mutatingConfig)
} else if err != nil {
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
} else {
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
}
}

View file

@ -6,6 +6,7 @@ import (
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
admregapi "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -15,8 +16,7 @@ func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData
return &admregapi.MutatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.MutatingWebhookConfigurationDebug,
Labels: config.KubePolicyAppLabels,
Name: config.MutatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
@ -28,7 +28,7 @@ func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData
"*/*",
"*",
"*",
[]admregapi.OperationType{admregapi.Create},
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
),
},
}
@ -37,8 +37,7 @@ func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData
func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
return &admregapi.MutatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.MutatingWebhookConfigurationName,
Labels: config.KubePolicyAppLabels,
Name: config.MutatingWebhookConfigurationName,
OwnerReferences: []v1.OwnerReference{
wrc.constructOwner(),
},
@ -53,8 +52,30 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []by
"*/*",
"*",
"*",
[]admregapi.OperationType{admregapi.Create},
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
),
},
}
}
//RemoveResourceMutatingWebhookConfiguration removes mutating webhook configuration for all resources
func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration() error {
var configName string
if wrc.serverIP != "" {
configName = config.MutatingWebhookConfigurationDebugName
} else {
configName = config.MutatingWebhookConfigurationName
}
// delete webhook configuration
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(configName, &v1.DeleteOptions{})
if errors.IsNotFound(err) {
glog.V(4).Infof("resource webhook configuration %s does not exits, so not deleting", configName)
return nil
}
if err != nil {
glog.V(4).Infof("failed to delete resource webhook configuration %s: %v", configName, err)
return err
}
glog.V(4).Infof("deleted resource webhook configuration %s", configName)
return nil
}

View file

@ -31,10 +31,25 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
// check for uniqueness of rule names while CREATE/DELET
admissionResp = ws.validateUniqueRuleName(policy)
if admissionResp.Allowed {
ws.manageWebhookConfigurations(*policy, request.Operation)
// helper function to evaluate if policy has validtion or mutation rules defined
hasMutateOrValidate := func() bool {
for _, rule := range policy.Spec.Rules {
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
glog.Infoln(rule.Name)
return true
}
}
return false
}
if admissionResp.Allowed {
if hasMutateOrValidate() {
// create mutating resource mutatingwebhookconfiguration if not present
if err := ws.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil {
glog.Error("failed to created resource mutating webhook configuration, policies wont be applied on the resource")
}
}
}
return admissionResp
}

View file

@ -1,74 +0,0 @@
package webhooks
import (
"reflect"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
v1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/labels"
)
type policyType int
const (
none policyType = iota
mutate
validate
all
)
func (ws *WebhookServer) manageWebhookConfigurations(policy kyverno.ClusterPolicy, op v1beta1.Operation) {
switch op {
case v1beta1.Create:
ws.registerWebhookConfigurations(policy)
case v1beta1.Delete:
ws.deregisterWebhookConfigurations(policy)
}
}
func (ws *WebhookServer) registerWebhookConfigurations(policy kyverno.ClusterPolicy) error {
if !HasMutateOrValidate(policy) {
return nil
}
if !ws.webhookRegistrationClient.MutationRegistered.IsSet() {
if err := ws.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil {
return err
}
glog.Infof("Mutating webhook registered")
}
return nil
}
func (ws *WebhookServer) deregisterWebhookConfigurations(policy kyverno.ClusterPolicy) error {
policies, _ := ws.pLister.List(labels.NewSelector())
// deregister webhook if no mutate/validate policy found in cluster
if !HasMutateOrValidatePolicies(policies) {
ws.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration()
glog.Infoln("Mutating webhook deregistered")
}
return nil
}
func HasMutateOrValidatePolicies(policies []*kyverno.ClusterPolicy) bool {
for _, policy := range policies {
if HasMutateOrValidate(*policy) {
return true
}
}
return false
}
func HasMutateOrValidate(policy kyverno.ClusterPolicy) bool {
for _, rule := range policy.Spec.Rules {
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
glog.V(4).Infoln(rule.Name)
return true
}
}
return false
}