Skip to content

Commit 3ee156d

Browse files
author
Per Goncalves da Silva
committed
Add webhook rule checker
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 27b05e7 commit 3ee156d

File tree

2 files changed

+352
-0
lines changed

2 files changed

+352
-0
lines changed

internal/operator-controller/rukpak/render/registryv1/validators/validator.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,45 @@ func CheckWebhookNameIsDNS1123SubDomain(rv1 *bundle.RegistryV1) []error {
264264
}
265265
return errs
266266
}
267+
268+
// unsupportedWebhookRuleAPIGroups contain the API groups that are unsupported for webhook configuration rules in OLMv1
269+
var unsupportedWebhookRuleAPIGroups = sets.New("olm.operatorframework.io", "*")
270+
271+
// unsupportedAdmissionRegistrationResources contain the resources that are unsupported for webhook configuration rules
272+
// for the admissionregistration.k8s.io api group
273+
var unsupportedAdmissionRegistrationResources = sets.New(
274+
"*",
275+
"mutatingwebhookconfiguration",
276+
"mutatingwebhookconfigurations",
277+
"validatingwebhookconfiguration",
278+
"validatingwebhookconfigurations",
279+
)
280+
281+
// CheckWebhookRules ensures webhook rules do not reference forbidden API groups or resources in line with OLMv0 behavior
282+
// See https://github.com/operator-framework/operator-lifecycle-manager/blob/ccf0c4c91f1e7673e87f3a18947f9a1f88d48438/pkg/controller/install/webhook.go#L19
283+
// for more details
284+
func CheckWebhookRules(rv1 *bundle.RegistryV1) []error {
285+
var errs []error
286+
for _, wh := range rv1.CSV.Spec.WebhookDefinitions {
287+
// Rules are not used for conversion webhooks
288+
if wh.Type == v1alpha1.ConversionWebhook {
289+
continue
290+
}
291+
webhookName := wh.GenerateName
292+
for _, rule := range wh.Rules {
293+
for _, apiGroup := range rule.APIGroups {
294+
if unsupportedWebhookRuleAPIGroups.Has(apiGroup) {
295+
errs = append(errs, fmt.Errorf("webhook %q contains forbidden rule: admission webhook rules cannot reference API group %q", webhookName, apiGroup))
296+
}
297+
if apiGroup == "admissionregistration.k8s.io" {
298+
for _, resource := range rule.Resources {
299+
if unsupportedAdmissionRegistrationResources.Has(strings.ToLower(resource)) {
300+
errs = append(errs, fmt.Errorf("webhook %q contains forbidden rule: admission webhook rules cannot reference resource %q for API group %q", webhookName, resource, apiGroup))
301+
}
302+
}
303+
}
304+
}
305+
}
306+
}
307+
return errs
308+
}

internal/operator-controller/rukpak/render/registryv1/validators/validator_test.go

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66

77
"github.com/stretchr/testify/require"
8+
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
89
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011

@@ -338,6 +339,315 @@ func Test_CheckWebhookSupport(t *testing.T) {
338339
}
339340
}
340341

342+
func Test_CheckWebhookRules(t *testing.T) {
343+
for _, tc := range []struct {
344+
name string
345+
bundle *bundle.RegistryV1
346+
expectedErrs []error
347+
}{
348+
{
349+
name: "accepts bundles with webhook definitions without rules",
350+
bundle: &bundle.RegistryV1{
351+
CSV: MakeCSV(
352+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
353+
WithWebhookDefinitions(
354+
v1alpha1.WebhookDescription{
355+
Type: v1alpha1.ValidatingAdmissionWebhook,
356+
},
357+
v1alpha1.WebhookDescription{
358+
Type: v1alpha1.MutatingAdmissionWebhook,
359+
},
360+
),
361+
),
362+
},
363+
},
364+
{
365+
name: "accepts bundles with webhook definitions with supported rules",
366+
bundle: &bundle.RegistryV1{
367+
CSV: MakeCSV(
368+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
369+
WithWebhookDefinitions(
370+
v1alpha1.WebhookDescription{
371+
Type: v1alpha1.ValidatingAdmissionWebhook,
372+
Rules: []admissionregistrationv1.RuleWithOperations{
373+
{
374+
Rule: admissionregistrationv1.Rule{
375+
APIGroups: []string{"appsv1"},
376+
Resources: []string{"deployments", "replicasets", "statefulsets"},
377+
},
378+
},
379+
},
380+
},
381+
v1alpha1.WebhookDescription{
382+
Type: v1alpha1.MutatingAdmissionWebhook,
383+
Rules: []admissionregistrationv1.RuleWithOperations{
384+
{
385+
Rule: admissionregistrationv1.Rule{
386+
APIGroups: []string{""},
387+
Resources: []string{"services"},
388+
},
389+
},
390+
},
391+
},
392+
),
393+
),
394+
},
395+
},
396+
{
397+
name: "reject bundles with webhook definitions with rules containing '*' api group",
398+
bundle: &bundle.RegistryV1{
399+
CSV: MakeCSV(
400+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
401+
WithWebhookDefinitions(
402+
v1alpha1.WebhookDescription{
403+
Type: v1alpha1.ValidatingAdmissionWebhook,
404+
GenerateName: "webhook-z",
405+
Rules: []admissionregistrationv1.RuleWithOperations{
406+
{
407+
Rule: admissionregistrationv1.Rule{
408+
APIGroups: []string{"*"},
409+
},
410+
},
411+
},
412+
},
413+
v1alpha1.WebhookDescription{
414+
Type: v1alpha1.MutatingAdmissionWebhook,
415+
GenerateName: "webhook-a",
416+
Rules: []admissionregistrationv1.RuleWithOperations{
417+
{
418+
Rule: admissionregistrationv1.Rule{
419+
APIGroups: []string{"*"},
420+
},
421+
},
422+
},
423+
},
424+
),
425+
),
426+
},
427+
expectedErrs: []error{
428+
errors.New("webhook \"webhook-z\" contains forbidden rule: admission webhook rules cannot reference API group \"*\""),
429+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference API group \"*\""),
430+
},
431+
},
432+
{
433+
name: "reject bundles with webhook definitions with rules containing 'olm.operatorframework.io' api group",
434+
bundle: &bundle.RegistryV1{
435+
CSV: MakeCSV(
436+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
437+
WithWebhookDefinitions(
438+
v1alpha1.WebhookDescription{
439+
Type: v1alpha1.ValidatingAdmissionWebhook,
440+
GenerateName: "webhook-z",
441+
Rules: []admissionregistrationv1.RuleWithOperations{
442+
{
443+
Rule: admissionregistrationv1.Rule{
444+
APIGroups: []string{"olm.operatorframework.io"},
445+
},
446+
},
447+
},
448+
},
449+
v1alpha1.WebhookDescription{
450+
Type: v1alpha1.MutatingAdmissionWebhook,
451+
GenerateName: "webhook-a",
452+
Rules: []admissionregistrationv1.RuleWithOperations{
453+
{
454+
Rule: admissionregistrationv1.Rule{
455+
APIGroups: []string{"olm.operatorframework.io"},
456+
},
457+
},
458+
},
459+
},
460+
),
461+
),
462+
},
463+
expectedErrs: []error{
464+
errors.New("webhook \"webhook-z\" contains forbidden rule: admission webhook rules cannot reference API group \"olm.operatorframework.io\""),
465+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference API group \"olm.operatorframework.io\""),
466+
},
467+
},
468+
{
469+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and '*' resource",
470+
bundle: &bundle.RegistryV1{
471+
CSV: MakeCSV(
472+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
473+
WithWebhookDefinitions(
474+
v1alpha1.WebhookDescription{
475+
Type: v1alpha1.ValidatingAdmissionWebhook,
476+
GenerateName: "webhook-a",
477+
Rules: []admissionregistrationv1.RuleWithOperations{
478+
{
479+
Rule: admissionregistrationv1.Rule{
480+
APIGroups: []string{"admissionregistration.k8s.io"},
481+
Resources: []string{"*"},
482+
},
483+
},
484+
},
485+
},
486+
),
487+
),
488+
},
489+
expectedErrs: []error{
490+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"*\" for API group \"admissionregistration.k8s.io\""),
491+
},
492+
},
493+
{
494+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and 'MutatingWebhookConfiguration' resource",
495+
bundle: &bundle.RegistryV1{
496+
CSV: MakeCSV(
497+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
498+
WithWebhookDefinitions(
499+
v1alpha1.WebhookDescription{
500+
Type: v1alpha1.ValidatingAdmissionWebhook,
501+
GenerateName: "webhook-a",
502+
Rules: []admissionregistrationv1.RuleWithOperations{
503+
{
504+
Rule: admissionregistrationv1.Rule{
505+
APIGroups: []string{"admissionregistration.k8s.io"},
506+
Resources: []string{"MutatingWebhookConfiguration"},
507+
},
508+
},
509+
},
510+
},
511+
),
512+
),
513+
},
514+
expectedErrs: []error{
515+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"MutatingWebhookConfiguration\" for API group \"admissionregistration.k8s.io\""),
516+
},
517+
},
518+
{
519+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and 'mutatingwebhookconfiguration' resource",
520+
bundle: &bundle.RegistryV1{
521+
CSV: MakeCSV(
522+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
523+
WithWebhookDefinitions(
524+
v1alpha1.WebhookDescription{
525+
Type: v1alpha1.ValidatingAdmissionWebhook,
526+
GenerateName: "webhook-a",
527+
Rules: []admissionregistrationv1.RuleWithOperations{
528+
{
529+
Rule: admissionregistrationv1.Rule{
530+
APIGroups: []string{"admissionregistration.k8s.io"},
531+
Resources: []string{"mutatingwebhookconfiguration"},
532+
},
533+
},
534+
},
535+
},
536+
),
537+
),
538+
},
539+
expectedErrs: []error{
540+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"mutatingwebhookconfiguration\" for API group \"admissionregistration.k8s.io\""),
541+
},
542+
},
543+
{
544+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and 'mutatingwebhookconfigurations' resource",
545+
bundle: &bundle.RegistryV1{
546+
CSV: MakeCSV(
547+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
548+
WithWebhookDefinitions(
549+
v1alpha1.WebhookDescription{
550+
Type: v1alpha1.ValidatingAdmissionWebhook,
551+
GenerateName: "webhook-a",
552+
Rules: []admissionregistrationv1.RuleWithOperations{
553+
{
554+
Rule: admissionregistrationv1.Rule{
555+
APIGroups: []string{"admissionregistration.k8s.io"},
556+
Resources: []string{"mutatingwebhookconfigurations"},
557+
},
558+
},
559+
},
560+
},
561+
),
562+
),
563+
},
564+
expectedErrs: []error{
565+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"mutatingwebhookconfigurations\" for API group \"admissionregistration.k8s.io\""),
566+
},
567+
},
568+
{
569+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and 'ValidatingWebhookConfiguration' resource",
570+
bundle: &bundle.RegistryV1{
571+
CSV: MakeCSV(
572+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
573+
WithWebhookDefinitions(
574+
v1alpha1.WebhookDescription{
575+
Type: v1alpha1.ValidatingAdmissionWebhook,
576+
GenerateName: "webhook-a",
577+
Rules: []admissionregistrationv1.RuleWithOperations{
578+
{
579+
Rule: admissionregistrationv1.Rule{
580+
APIGroups: []string{"admissionregistration.k8s.io"},
581+
Resources: []string{"ValidatingWebhookConfiguration"},
582+
},
583+
},
584+
},
585+
},
586+
),
587+
),
588+
},
589+
expectedErrs: []error{
590+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"ValidatingWebhookConfiguration\" for API group \"admissionregistration.k8s.io\""),
591+
},
592+
},
593+
{
594+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and 'validatingwebhookconfiguration' resource",
595+
bundle: &bundle.RegistryV1{
596+
CSV: MakeCSV(
597+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
598+
WithWebhookDefinitions(
599+
v1alpha1.WebhookDescription{
600+
Type: v1alpha1.ValidatingAdmissionWebhook,
601+
GenerateName: "webhook-a",
602+
Rules: []admissionregistrationv1.RuleWithOperations{
603+
{
604+
Rule: admissionregistrationv1.Rule{
605+
APIGroups: []string{"admissionregistration.k8s.io"},
606+
Resources: []string{"validatingwebhookconfiguration"},
607+
},
608+
},
609+
},
610+
},
611+
),
612+
),
613+
},
614+
expectedErrs: []error{
615+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"validatingwebhookconfiguration\" for API group \"admissionregistration.k8s.io\""),
616+
},
617+
},
618+
{
619+
name: "reject bundles with webhook definitions with rules containing 'admissionregistration.k8s.io' api group and 'validatingwebhookconfigurations' resource",
620+
bundle: &bundle.RegistryV1{
621+
CSV: MakeCSV(
622+
WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces),
623+
WithWebhookDefinitions(
624+
v1alpha1.WebhookDescription{
625+
Type: v1alpha1.ValidatingAdmissionWebhook,
626+
GenerateName: "webhook-a",
627+
Rules: []admissionregistrationv1.RuleWithOperations{
628+
{
629+
Rule: admissionregistrationv1.Rule{
630+
APIGroups: []string{"admissionregistration.k8s.io"},
631+
Resources: []string{"validatingwebhookconfigurations"},
632+
},
633+
},
634+
},
635+
},
636+
),
637+
),
638+
},
639+
expectedErrs: []error{
640+
errors.New("webhook \"webhook-a\" contains forbidden rule: admission webhook rules cannot reference resource \"validatingwebhookconfigurations\" for API group \"admissionregistration.k8s.io\""),
641+
},
642+
},
643+
} {
644+
t.Run(tc.name, func(t *testing.T) {
645+
errs := validators.CheckWebhookRules(tc.bundle)
646+
require.Equal(t, tc.expectedErrs, errs)
647+
})
648+
}
649+
}
650+
341651
func Test_CheckWebhookDeploymentReferentialIntegrity(t *testing.T) {
342652
for _, tc := range []struct {
343653
name string

0 commit comments

Comments
 (0)