1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 10:28:36 +00:00

feat: webhook labels (#9015)

Signed-off-by: Zadkiel Aharonian <hello@zadkiel.fr>
Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
Zadkiel Aharonian 2023-12-07 13:58:31 +01:00 committed by GitHub
parent fd45cc5eee
commit 5e96b26a48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 108 additions and 18 deletions

View file

@ -273,6 +273,7 @@ The chart values are organised per component.
| config.resourceFilters | list | See [values.yaml](values.yaml) | Resource types to be skipped by the Kyverno policy engine. Make sure to surround each entry in quotes so that it doesn't get parsed as a nested YAML list. These are joined together without spaces, run through `tpl`, and the result is set in the config map. |
| config.webhooks | list | `[]` | Defines the `namespaceSelector` in the webhook configurations. Note that it takes a list of `namespaceSelector` and/or `objectSelector` in the JSON format, and only the first element will be forwarded to the webhook configurations. The Kyverno namespace is excluded if `excludeKyvernoNamespace` is `true` (default) |
| config.webhookAnnotations | object | `{}` | Defines annotations to set on webhook configurations. |
| config.webhookLabels | object | `{}` | Defines labels to set on webhook configurations. |
| config.matchConditions | list | `[]` | Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+). |
| config.excludeKyvernoNamespace | bool | `true` | Exclude Kyverno namespace Determines if default Kyverno namespace exclusion is enabled for webhooks and resourceFilters |
| config.resourceFiltersExcludeNamespaces | list | `[]` | resourceFilter namespace exclude Namespaces to exclude from the default resourceFilters |

View file

@ -42,6 +42,9 @@ data:
{{- with .Values.config.webhookAnnotations }}
webhookAnnotations: {{ toJson . | quote }}
{{- end }}
{{- with .Values.config.webhookLabels }}
webhookLabels: {{ toJson . | quote }}
{{- end }}
{{- with .Values.config.matchConditions }}
matchConditions: {{ toJson . | quote }}
{{- end }}

View file

@ -232,6 +232,11 @@ config:
# Example to disable admission enforcer on AKS:
# 'admissions.enforcer/disabled': 'true'
# -- Defines labels to set on webhook configurations.
webhookLabels: {}
# Example to adopt webhook resources in ArgoCD:
# 'argocd.argoproj.io/instance': 'kyverno'
# -- Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+).
matchConditions: []

View file

@ -88,6 +88,7 @@ const (
generateSuccessEvents = "generateSuccessEvents"
webhooks = "webhooks"
webhookAnnotations = "webhookAnnotations"
webhookLabels = "webhookLabels"
matchConditions = "matchConditions"
)
@ -162,6 +163,8 @@ type Configuration interface {
GetWebhooks() []WebhookConfig
// GetWebhookAnnotations returns annotations to set on webhook configs
GetWebhookAnnotations() map[string]string
// GetWebhookLabels returns labels to set on webhook configs
GetWebhookLabels() map[string]string
// GetMatchConditions returns match conditions to set on webhook configs
GetMatchConditions() []admissionregistrationv1.MatchCondition
// Load loads configuration from a configmap
@ -181,6 +184,7 @@ type configuration struct {
generateSuccessEvents bool
webhooks []WebhookConfig
webhookAnnotations map[string]string
webhookLabels map[string]string
matchConditions []admissionregistrationv1.MatchCondition
mux sync.RWMutex
callbacks []func()
@ -300,6 +304,12 @@ func (cd *configuration) GetWebhookAnnotations() map[string]string {
return cd.webhookAnnotations
}
func (cd *configuration) GetWebhookLabels() map[string]string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.webhookLabels
}
func (cd *configuration) GetMatchConditions() []admissionregistrationv1.MatchCondition {
cd.mux.RLock()
defer cd.mux.RUnlock()
@ -332,6 +342,7 @@ func (cd *configuration) load(cm *corev1.ConfigMap) {
cd.generateSuccessEvents = false
cd.webhooks = nil
cd.webhookAnnotations = nil
cd.webhookLabels = nil
cd.matchConditions = nil
// load filters
cd.filters = parseKinds(data[resourceFilters])
@ -437,6 +448,20 @@ func (cd *configuration) load(cm *corev1.ConfigMap) {
logger.Info("webhookAnnotations configured")
}
}
// load webhook annotations
webhookLabels, ok := data[webhookLabels]
if !ok {
logger.Info("webhookLabels not set")
} else {
logger := logger.WithValues("webhookLabels", webhookLabels)
webhookLabels, err := parseWebhookLabels(webhookLabels)
if err != nil {
logger.Error(err, "failed to parse webhook labels")
} else {
cd.webhookLabels = webhookLabels
logger.Info("webhookLabels configured")
}
}
// load match conditions
matchConditions, ok := data[matchConditions]
if !ok {
@ -465,6 +490,7 @@ func (cd *configuration) unload() {
cd.generateSuccessEvents = false
cd.webhooks = nil
cd.webhookAnnotations = nil
cd.webhookLabels = nil
logger.Info("configuration unloaded")
}

View file

@ -55,6 +55,14 @@ func parseWebhookAnnotations(in string) (map[string]string, error) {
return out, nil
}
func parseWebhookLabels(in string) (map[string]string, error) {
var out map[string]string
if err := json.Unmarshal([]byte(in), &out); err != nil {
return nil, err
}
return out, nil
}
func parseMatchConditions(in string) ([]admissionregistrationv1.MatchCondition, error) {
var out []admissionregistrationv1.MatchCondition
if err := json.Unmarshal([]byte(in), &out); err != nil {

View file

@ -258,6 +258,43 @@ func Test_parseWebhookAnnotations(t *testing.T) {
}
}
func Test_parseWebhookLabels(t *testing.T) {
type args struct {
in string
}
tests := []struct {
name string
args args
want map[string]string
wantErr bool
}{{
args: args{"hello"},
wantErr: true,
}, {
args: args{""},
wantErr: true,
}, {
args: args{"null"},
}, {
args: args{`{"a": "b"}`},
want: map[string]string{
"a": "b",
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseWebhookLabels(tt.args.in)
if (err != nil) != tt.wantErr {
t.Errorf("parseWebhookLabels() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseWebhookLabels() = %v, want %v", got, tt.want)
}
})
}
}
func Test_parseBucketBoundariesConfig(t *testing.T) {
var emptyBoundaries []float64

View file

@ -12,6 +12,7 @@ import (
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/tls"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
"golang.org/x/exp/maps"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -164,12 +165,16 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, _,
return err
}
func objectMeta(name string, annotations map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
func objectMeta(name string, annotations map[string]string, labels map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
desiredLabels := make(map[string]string)
defaultLabels := map[string]string{
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
}
maps.Copy(desiredLabels, labels)
maps.Copy(desiredLabels, defaultLabels)
return metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
},
Name: name,
Labels: desiredLabels,
Annotations: annotations,
OwnerReferences: owner,
}
@ -177,7 +182,7 @@ func objectMeta(name string, annotations map[string]string, owner ...metav1.Owne
func (c *controller) build(cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
return &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: objectMeta(c.webhookName, cfg.GetWebhookAnnotations()),
ObjectMeta: objectMeta(c.webhookName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels()),
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
Name: fmt.Sprintf("%s.%s.svc", config.KyvernoServiceName(), config.KyvernoNamespace()),
ClientConfig: c.clientConfig(caBundle),

View file

@ -512,7 +512,7 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
func (c *controller) buildVerifyMutatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
return &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: objectMeta(config.VerifyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.VerifyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.MutatingWebhook{{
Name: config.VerifyMutatingWebhookName,
ClientConfig: c.clientConfig(caBundle, config.VerifyMutatingWebhookServicePath),
@ -539,7 +539,7 @@ func (c *controller) buildVerifyMutatingWebhookConfiguration(_ context.Context,
func (c *controller) buildPolicyMutatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
return &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: objectMeta(config.PolicyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.PolicyMutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.MutatingWebhook{{
Name: config.PolicyMutatingWebhookName,
ClientConfig: c.clientConfig(caBundle, config.PolicyMutatingWebhookServicePath),
@ -562,7 +562,7 @@ func (c *controller) buildPolicyMutatingWebhookConfiguration(_ context.Context,
func (c *controller) buildPolicyValidatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
return &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: objectMeta(config.PolicyValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.PolicyValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
Name: config.PolicyValidatingWebhookName,
ClientConfig: c.clientConfig(caBundle, config.PolicyValidatingWebhookServicePath),
@ -584,7 +584,7 @@ func (c *controller) buildPolicyValidatingWebhookConfiguration(_ context.Context
func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
return &admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.MutatingWebhook{{
Name: config.MutatingWebhookName + "-ignore",
ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/ignore"),
@ -630,7 +630,7 @@ func (c *controller) buildDefaultResourceMutatingWebhookConfiguration(_ context.
func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.MutatingWebhookConfiguration, error) {
result := admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.MutatingWebhook{},
}
if c.watchdogCheck() {
@ -708,7 +708,7 @@ func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(_ contex
sideEffects = &noneOnDryRun
}
return &admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.ValidatingWebhook{{
Name: config.ValidatingWebhookName + "-ignore",
ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/ignore"),
@ -756,7 +756,7 @@ func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(_ contex
func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
result := admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), c.buildOwner()...),
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
Webhooks: []admissionregistrationv1.ValidatingWebhook{},
}
if c.watchdogCheck() {

View file

@ -7,6 +7,7 @@ import (
"github.com/kyverno/kyverno/api/kyverno"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"golang.org/x/exp/maps"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -85,12 +86,16 @@ func (wh *webhook) isEmpty() bool {
return len(wh.rules) == 0
}
func objectMeta(name string, annotations map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
func objectMeta(name string, annotations map[string]string, labels map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta {
desiredLabels := make(map[string]string)
defaultLabels := map[string]string{
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
}
maps.Copy(desiredLabels, labels)
maps.Copy(desiredLabels, defaultLabels)
return metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
kyverno.LabelWebhookManagedBy: kyverno.ValueKyvernoApp,
},
Name: name,
Labels: desiredLabels,
Annotations: annotations,
OwnerReferences: owner,
}