mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-15 00:36:28 +00:00
Changes to dynamically configure webhooks (#8437)
* Changes to dynamically configure webhooks Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add unit tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add kuttl tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Refactoring Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Correct unit test Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Change way of webhooks configured Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Correct tests with new changes Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add delete operation by default Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Correct tests with new changes Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Correct order for operations Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add corrections Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add mutatingwebhookconfiguration test Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Correct unit test Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Added policy.yaml in mutate webhook test Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add corrections in kuttl test and code Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Change name of test Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Changes to update webhooks manifest Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add corrections for dynamic-op-mutate kuttl test Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Add minor changes; remove unnecessary file Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Correct adding operations for MutatingWebhookConf Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * dynamic op mutate and validate added Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Resolve conflicts Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Filter rules for mutatingwebhookconf correctly Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * replace TestStep with Test in chainsaw tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * converted to new chainsaw-test format Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * minor corrections Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * remove isMutationEmpty() Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * initial changes for dynamic opn enhancements Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * rename variables Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * resolve lint errors Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * refactor code Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * add changes for exclude operations Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * add conformance tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * add unit tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * corrections in conformance tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * modification in unit tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * correction in conformance tests Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * Update .vscode/launch.json Signed-off-by: shuting <shuting@nirmata.com> * update variable usage Signed-off-by: anushkamittal2001 <anushka@nirmata.com> * remove testresults Signed-off-by: anushkamittal2001 <anushka@nirmata.com> --------- Signed-off-by: anushkamittal2001 <anushka@nirmata.com> Signed-off-by: shuting <shuting@nirmata.com> Co-authored-by: Vishal Choudhary <sendtovishalchoudhary@gmail.com> Co-authored-by: shuting <shuting@nirmata.com> Co-authored-by: Jim Bugwadia <jim@nirmata.com> Co-authored-by: Vishal Choudhary <vishal.choudhary@nirmata.com> Co-authored-by: shuting <shutting06@gmail.com>
This commit is contained in:
parent
141e7d056f
commit
cfc9683033
34 changed files with 1143 additions and 8 deletions
|
@ -51,6 +51,10 @@ const (
|
|||
IdleDeadline = tickerInterval * 10
|
||||
maxRetries = 10
|
||||
tickerInterval = 10 * time.Second
|
||||
webhookCreate = "CREATE"
|
||||
webhookUpdate = "UPDATE"
|
||||
webhookDelete = "DELETE"
|
||||
webhookConnect = "CONNECT"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -633,6 +637,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte
|
|||
ObjectMeta: objectMeta(config.MutatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.MutatingWebhook{},
|
||||
}
|
||||
var mapResourceToOpnType map[string][]admissionregistrationv1.OperationType
|
||||
if c.watchdogCheck() {
|
||||
webhookCfg := config.WebhookConfig{}
|
||||
webhookCfgs := cfg.GetWebhooks()
|
||||
|
@ -670,6 +675,8 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte
|
|||
} else {
|
||||
c.mergeWebhook(failWebhook, p, false)
|
||||
}
|
||||
rules := p.GetSpec().Rules
|
||||
mapResourceToOpnType = addOpnForMutatingWebhookConf(rules, mapResourceToOpnType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -677,14 +684,14 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte
|
|||
webhooks := []*webhook{ignoreWebhook, failWebhook}
|
||||
webhooks = append(webhooks, fineGrainedIgnoreList...)
|
||||
webhooks = append(webhooks, fineGrainedFailList...)
|
||||
result.Webhooks = c.buildResourceMutatingWebhookRules(caBundle, webhookCfg, &noneOnDryRun, webhooks)
|
||||
result.Webhooks = c.buildResourceMutatingWebhookRules(caBundle, webhookCfg, &noneOnDryRun, webhooks, mapResourceToOpnType)
|
||||
} else {
|
||||
c.recordPolicyState(config.MutatingWebhookConfigurationName)
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (c *controller) buildResourceMutatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook) []admissionregistrationv1.MutatingWebhook {
|
||||
func (c *controller) buildResourceMutatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook, mapResourceToOpnType map[string][]admissionregistrationv1.OperationType) []admissionregistrationv1.MutatingWebhook {
|
||||
var mutatingWebhooks []admissionregistrationv1.MutatingWebhook
|
||||
for _, webhook := range webhooks {
|
||||
if webhook.isEmpty() {
|
||||
|
@ -698,7 +705,7 @@ func (c *controller) buildResourceMutatingWebhookRules(caBundle []byte, webhookC
|
|||
admissionregistrationv1.MutatingWebhook{
|
||||
Name: name,
|
||||
ClientConfig: c.clientConfig(caBundle, path),
|
||||
Rules: webhook.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update),
|
||||
Rules: webhook.buildRulesWithOperations(mapResourceToOpnType, []admissionregistrationv1.OperationType{"CREATE", "UPDATE"}),
|
||||
FailurePolicy: &failurePolicy,
|
||||
SideEffects: sideEffects,
|
||||
AdmissionReviewVersions: []string{"v1"},
|
||||
|
@ -765,11 +772,40 @@ func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(_ contex
|
|||
nil
|
||||
}
|
||||
|
||||
func addOpnForMutatingWebhookConf(rules []kyvernov1.Rule, mapResourceToOpnType map[string][]admissionregistrationv1.OperationType) map[string][]admissionregistrationv1.OperationType {
|
||||
var mapResourceToOpn map[string]map[string]bool
|
||||
for _, r := range rules {
|
||||
var resources []string
|
||||
operationStatusMap := getOperationStatusMap()
|
||||
operationStatusMap = computeOperationsForMutatingWebhookConf(r, operationStatusMap)
|
||||
resources = computeResourcesOfRule(r)
|
||||
for _, r := range resources {
|
||||
mapResourceToOpn, mapResourceToOpnType = appendResource(r, mapResourceToOpn, operationStatusMap, mapResourceToOpnType)
|
||||
}
|
||||
}
|
||||
return mapResourceToOpnType
|
||||
}
|
||||
|
||||
func addOpnForValidatingWebhookConf(rules []kyvernov1.Rule, mapResourceToOpnType map[string][]admissionregistrationv1.OperationType) map[string][]admissionregistrationv1.OperationType {
|
||||
var mapResourceToOpn map[string]map[string]bool
|
||||
for _, r := range rules {
|
||||
var resources []string
|
||||
operationStatusMap := getOperationStatusMap()
|
||||
operationStatusMap = computeOperationsForValidatingWebhookConf(r, operationStatusMap)
|
||||
resources = computeResourcesOfRule(r)
|
||||
for _, r := range resources {
|
||||
mapResourceToOpn, mapResourceToOpnType = appendResource(r, mapResourceToOpn, operationStatusMap, mapResourceToOpnType)
|
||||
}
|
||||
}
|
||||
return mapResourceToOpnType
|
||||
}
|
||||
|
||||
func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) {
|
||||
result := admissionregistrationv1.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: objectMeta(config.ValidatingWebhookConfigurationName, cfg.GetWebhookAnnotations(), cfg.GetWebhookLabels(), c.buildOwner()...),
|
||||
Webhooks: []admissionregistrationv1.ValidatingWebhook{},
|
||||
}
|
||||
var mapResourceToOpnType map[string][]admissionregistrationv1.OperationType
|
||||
if c.watchdogCheck() {
|
||||
webhookCfg := config.WebhookConfig{}
|
||||
webhookCfgs := cfg.GetWebhooks()
|
||||
|
@ -810,6 +846,8 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con
|
|||
}
|
||||
}
|
||||
}
|
||||
rules := p.GetSpec().Rules
|
||||
mapResourceToOpnType = addOpnForValidatingWebhookConf(rules, mapResourceToOpnType)
|
||||
}
|
||||
|
||||
sideEffects := &none
|
||||
|
@ -820,14 +858,14 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con
|
|||
webhooks := []*webhook{ignoreWebhook, failWebhook}
|
||||
webhooks = append(webhooks, fineGrainedIgnoreList...)
|
||||
webhooks = append(webhooks, fineGrainedFailList...)
|
||||
result.Webhooks = c.buildResourceValidatingWebhookRules(caBundle, webhookCfg, sideEffects, webhooks)
|
||||
result.Webhooks = c.buildResourceValidatingWebhookRules(caBundle, webhookCfg, sideEffects, webhooks, mapResourceToOpnType)
|
||||
} else {
|
||||
c.recordPolicyState(config.MutatingWebhookConfigurationName)
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (c *controller) buildResourceValidatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook) []admissionregistrationv1.ValidatingWebhook {
|
||||
func (c *controller) buildResourceValidatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook, mapResourceToOpnType map[string][]admissionregistrationv1.OperationType) []admissionregistrationv1.ValidatingWebhook {
|
||||
var validatingWebhooks []admissionregistrationv1.ValidatingWebhook
|
||||
for _, webhook := range webhooks {
|
||||
if webhook.isEmpty() {
|
||||
|
@ -841,7 +879,7 @@ func (c *controller) buildResourceValidatingWebhookRules(caBundle []byte, webhoo
|
|||
admissionregistrationv1.ValidatingWebhook{
|
||||
Name: name,
|
||||
ClientConfig: c.clientConfig(caBundle, path),
|
||||
Rules: webhook.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect),
|
||||
Rules: webhook.buildRulesWithOperations(mapResourceToOpnType, []admissionregistrationv1.OperationType{"CREATE", "UPDATE", "DELETE", "CONNECT"}),
|
||||
FailurePolicy: &failurePolicy,
|
||||
SideEffects: sideEffects,
|
||||
AdmissionReviewVersions: []string{"v1"},
|
||||
|
|
129
pkg/controllers/webhook/controller_test.go
Normal file
129
pkg/controllers/webhook/controller_test.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
package webhook
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
)
|
||||
|
||||
func TestAddOperationsForValidatingWebhookConf(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
rules []kyverno.Rule
|
||||
expectedResult map[string][]admissionregistrationv1.OperationType
|
||||
}{
|
||||
{
|
||||
name: "Test Case 1",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Operations: []kyverno.AdmissionOperation{"CREATE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string][]admissionregistrationv1.OperationType{
|
||||
"ConfigMap": {"CREATE"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Case 1",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
},
|
||||
},
|
||||
ExcludeResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []kyverno.AdmissionOperation{"DELETE", "CONNECT", "CREATE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string][]admissionregistrationv1.OperationType{
|
||||
"ConfigMap": {"UPDATE"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
var result map[string][]admissionregistrationv1.OperationType
|
||||
var mapResourceToOpnType map[string][]admissionregistrationv1.OperationType
|
||||
result = addOpnForValidatingWebhookConf(testCase.rules, mapResourceToOpnType)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Errorf("Expected %v, but got %v", testCase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddOperationsForMutatingtingWebhookConf(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
rules []kyverno.Rule
|
||||
expectedResult map[string][]admissionregistrationv1.OperationType
|
||||
}{
|
||||
{
|
||||
name: "Test Case 1",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
Mutation: kyverno.Mutation{
|
||||
PatchesJSON6902: "add",
|
||||
},
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Operations: []kyverno.AdmissionOperation{"CREATE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string][]admissionregistrationv1.OperationType{
|
||||
"ConfigMap": {"CREATE"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Case 1",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
Mutation: kyverno.Mutation{
|
||||
PatchesJSON6902: "add",
|
||||
},
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Kinds: []string{"Secret"},
|
||||
},
|
||||
},
|
||||
ExcludeResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []kyverno.AdmissionOperation{"UPDATE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string][]admissionregistrationv1.OperationType{
|
||||
"Secret": {"CREATE"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
var result map[string][]admissionregistrationv1.OperationType
|
||||
var mapResourceToOpnType map[string][]admissionregistrationv1.OperationType
|
||||
result = addOpnForMutatingWebhookConf(testCase.rules, mapResourceToOpnType)
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Errorf("Expected %v, but got %v", testCase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -50,6 +50,15 @@ func newWebhook(timeout int32, failurePolicy admissionregistrationv1.FailurePoli
|
|||
}
|
||||
}
|
||||
|
||||
func findKeyContainingSubstring(m map[string][]admissionregistrationv1.OperationType, substring string, defaultOpn []admissionregistrationv1.OperationType) []admissionregistrationv1.OperationType {
|
||||
for key, value := range m {
|
||||
if strings.Contains(strings.ToLower(key), strings.ToLower(substring)) || strings.Contains(strings.ToLower(substring), strings.ToLower(key)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return defaultOpn
|
||||
}
|
||||
|
||||
func newWebhookPerPolicy(timeout int32, failurePolicy admissionregistrationv1.FailurePolicyType, matchConditions []admissionregistrationv1.MatchCondition, policy kyvernov1.PolicyInterface) *webhook {
|
||||
webhook := newWebhook(timeout, failurePolicy, matchConditions)
|
||||
webhook.policyMeta = objectmeta.ObjectName{
|
||||
|
@ -62,10 +71,11 @@ func newWebhookPerPolicy(timeout int32, failurePolicy admissionregistrationv1.Fa
|
|||
return webhook
|
||||
}
|
||||
|
||||
func (wh *webhook) buildRulesWithOperations(ops ...admissionregistrationv1.OperationType) []admissionregistrationv1.RuleWithOperations {
|
||||
func (wh *webhook) buildRulesWithOperations(final map[string][]admissionregistrationv1.OperationType, defaultOpn []admissionregistrationv1.OperationType) []admissionregistrationv1.RuleWithOperations {
|
||||
var rules []admissionregistrationv1.RuleWithOperations
|
||||
|
||||
for gv, resources := range wh.rules {
|
||||
firstResource := sets.List(resources)[0]
|
||||
// if we have pods, we add pods/ephemeralcontainers by default
|
||||
if (gv.Group == "" || gv.Group == "*") && (gv.Version == "v1" || gv.Version == "*") && (resources.Has("pods") || resources.Has("*")) {
|
||||
resources.Insert("pods/ephemeralcontainers")
|
||||
|
@ -77,7 +87,7 @@ func (wh *webhook) buildRulesWithOperations(ops ...admissionregistrationv1.Opera
|
|||
Resources: sets.List(resources),
|
||||
Scope: ptr.To(gv.scopeType),
|
||||
},
|
||||
Operations: ops,
|
||||
Operations: findKeyContainingSubstring(final, firstResource, defaultOpn),
|
||||
})
|
||||
}
|
||||
less := func(a []string, b []string) (int, bool) {
|
||||
|
@ -109,6 +119,42 @@ func (wh *webhook) buildRulesWithOperations(ops ...admissionregistrationv1.Opera
|
|||
return rules
|
||||
}
|
||||
|
||||
func scanResourceFilterForResources(resFilter kyvernov1.ResourceFilters) []string {
|
||||
var resources []string
|
||||
for _, rf := range resFilter {
|
||||
if rf.ResourceDescription.Kinds != nil {
|
||||
resources = append(resources, rf.ResourceDescription.Kinds...)
|
||||
}
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func scanResourceFilter(resFilter kyvernov1.ResourceFilters, operationStatusMap map[string]bool) (bool, map[string]bool) {
|
||||
opFound := false
|
||||
for _, rf := range resFilter {
|
||||
if rf.ResourceDescription.Operations != nil {
|
||||
for _, o := range rf.ResourceDescription.Operations {
|
||||
opFound = true
|
||||
operationStatusMap[string(o)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return opFound, operationStatusMap
|
||||
}
|
||||
|
||||
func scanResourceFilterForExclude(resFilter kyvernov1.ResourceFilters, operationStatusMap map[string]bool) (bool, map[string]bool) {
|
||||
opFound := false
|
||||
for _, rf := range resFilter {
|
||||
if rf.ResourceDescription.Operations != nil {
|
||||
for _, o := range rf.ResourceDescription.Operations {
|
||||
opFound = true
|
||||
operationStatusMap[string(o)] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return opFound, operationStatusMap
|
||||
}
|
||||
|
||||
func (wh *webhook) set(gvrs GroupVersionResourceScope) {
|
||||
gvs := groupVersionScope{
|
||||
GroupVersion: gvrs.GroupVersion(),
|
||||
|
@ -165,6 +211,156 @@ func objectMeta(name string, annotations map[string]string, labels map[string]st
|
|||
}
|
||||
}
|
||||
|
||||
func computeOperationsForValidatingWebhookConf(r kyvernov1.Rule, operationStatusMap map[string]bool) map[string]bool {
|
||||
var opFound bool
|
||||
opFoundCount := 0
|
||||
if len(r.MatchResources.Any) != 0 {
|
||||
opFound, operationStatusMap = scanResourceFilter(r.MatchResources.Any, operationStatusMap)
|
||||
opFoundCount = opFoundCountIncrement(opFound, opFoundCount)
|
||||
}
|
||||
if len(r.MatchResources.All) != 0 {
|
||||
opFound, operationStatusMap = scanResourceFilter(r.MatchResources.All, operationStatusMap)
|
||||
opFoundCount = opFoundCountIncrement(opFound, opFoundCount)
|
||||
}
|
||||
if r.MatchResources.ResourceDescription.Operations != nil {
|
||||
for _, o := range r.MatchResources.ResourceDescription.Operations {
|
||||
opFound = true
|
||||
operationStatusMap[string(o)] = true
|
||||
opFoundCount = opFoundCountIncrement(opFound, opFoundCount)
|
||||
}
|
||||
}
|
||||
if !opFound {
|
||||
operationStatusMap[webhookCreate] = true
|
||||
operationStatusMap[webhookUpdate] = true
|
||||
operationStatusMap[webhookConnect] = true
|
||||
operationStatusMap[webhookDelete] = true
|
||||
}
|
||||
if r.ExcludeResources.ResourceDescription.Operations != nil {
|
||||
for _, o := range r.ExcludeResources.ResourceDescription.Operations {
|
||||
operationStatusMap[string(o)] = false
|
||||
}
|
||||
}
|
||||
if len(r.ExcludeResources.Any) != 0 {
|
||||
_, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.Any, operationStatusMap)
|
||||
}
|
||||
if len(r.ExcludeResources.All) != 0 {
|
||||
_, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap)
|
||||
}
|
||||
return operationStatusMap
|
||||
}
|
||||
|
||||
func opFoundCountIncrement(opFound bool, opFoundCount int) int {
|
||||
if opFound {
|
||||
opFoundCount++
|
||||
}
|
||||
return opFoundCount
|
||||
}
|
||||
|
||||
func computeOperationsForMutatingWebhookConf(r kyvernov1.Rule, operationStatusMap map[string]bool) map[string]bool {
|
||||
if r.HasMutate() || r.HasVerifyImages() {
|
||||
var opFound bool
|
||||
opFoundCount := 0
|
||||
if len(r.MatchResources.Any) != 0 {
|
||||
opFound, operationStatusMap = scanResourceFilter(r.MatchResources.Any, operationStatusMap)
|
||||
opFoundCount = opFoundCountIncrement(opFound, opFoundCount)
|
||||
}
|
||||
if len(r.MatchResources.All) != 0 {
|
||||
opFound, operationStatusMap = scanResourceFilter(r.MatchResources.All, operationStatusMap)
|
||||
opFoundCount = opFoundCountIncrement(opFound, opFoundCount)
|
||||
}
|
||||
if r.MatchResources.ResourceDescription.Operations != nil {
|
||||
for _, o := range r.MatchResources.ResourceDescription.Operations {
|
||||
opFound = true
|
||||
operationStatusMap[string(o)] = true
|
||||
opFoundCount = opFoundCountIncrement(opFound, opFoundCount)
|
||||
}
|
||||
}
|
||||
if opFoundCount == 0 {
|
||||
operationStatusMap[webhookCreate] = true
|
||||
operationStatusMap[webhookUpdate] = true
|
||||
}
|
||||
if r.ExcludeResources.ResourceDescription.Operations != nil {
|
||||
for _, o := range r.ExcludeResources.ResourceDescription.Operations {
|
||||
operationStatusMap[string(o)] = false
|
||||
}
|
||||
}
|
||||
if len(r.ExcludeResources.Any) != 0 {
|
||||
_, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.Any, operationStatusMap)
|
||||
}
|
||||
if len(r.ExcludeResources.All) != 0 {
|
||||
_, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap)
|
||||
}
|
||||
}
|
||||
return operationStatusMap
|
||||
}
|
||||
|
||||
func getMinimumOperations(operationStatusMap map[string]bool) []admissionregistrationv1.OperationType {
|
||||
operationReq := make([]admissionregistrationv1.OperationType, 0, 4)
|
||||
for k, v := range operationStatusMap {
|
||||
if v {
|
||||
var oper admissionregistrationv1.OperationType = admissionregistrationv1.OperationType(k)
|
||||
operationReq = append(operationReq, oper)
|
||||
}
|
||||
}
|
||||
return operationReq
|
||||
}
|
||||
|
||||
func getOperationStatusMap() map[string]bool {
|
||||
operationStatusMap := make(map[string]bool)
|
||||
operationStatusMap[webhookCreate] = false
|
||||
operationStatusMap[webhookUpdate] = false
|
||||
operationStatusMap[webhookDelete] = false
|
||||
operationStatusMap[webhookConnect] = false
|
||||
return operationStatusMap
|
||||
}
|
||||
|
||||
func appendResource(r string, mapResourceToOpn map[string]map[string]bool, opnStatusMap map[string]bool, mapResourceToOpnType map[string][]admissionregistrationv1.OperationType) (map[string]map[string]bool, map[string][]admissionregistrationv1.OperationType) {
|
||||
if _, exists := mapResourceToOpn[r]; exists {
|
||||
opnStatMap1 := opnStatusMap
|
||||
opnStatMap2 := mapResourceToOpn[r]
|
||||
for opn := range opnStatusMap {
|
||||
if opnStatMap1[opn] || opnStatMap2[opn] {
|
||||
opnStatusMap[opn] = true
|
||||
}
|
||||
}
|
||||
mapResourceToOpn[r] = opnStatusMap
|
||||
mapResourceToOpnType[r] = getMinimumOperations(opnStatusMap)
|
||||
} else {
|
||||
if mapResourceToOpn == nil {
|
||||
mapResourceToOpn = make(map[string]map[string]bool)
|
||||
}
|
||||
mapResourceToOpn[r] = opnStatusMap
|
||||
if mapResourceToOpnType == nil {
|
||||
mapResourceToOpnType = make(map[string][]admissionregistrationv1.OperationType)
|
||||
}
|
||||
mapResourceToOpnType[r] = getMinimumOperations(opnStatusMap)
|
||||
}
|
||||
return mapResourceToOpn, mapResourceToOpnType
|
||||
}
|
||||
|
||||
func computeResourcesOfRule(r kyvernov1.Rule) []string {
|
||||
var resources []string
|
||||
if len(r.MatchResources.Any) != 0 {
|
||||
resources = scanResourceFilterForResources(r.MatchResources.Any)
|
||||
}
|
||||
if len(r.MatchResources.All) != 0 {
|
||||
resources = scanResourceFilterForResources(r.MatchResources.Any)
|
||||
}
|
||||
if len(r.ExcludeResources.Any) != 0 {
|
||||
resources = scanResourceFilterForResources(r.MatchResources.Any)
|
||||
}
|
||||
if len(r.ExcludeResources.All) != 0 {
|
||||
resources = scanResourceFilterForResources(r.MatchResources.Any)
|
||||
}
|
||||
if r.MatchResources.ResourceDescription.Kinds != nil {
|
||||
resources = append(resources, r.MatchResources.ResourceDescription.Kinds...)
|
||||
}
|
||||
if r.ExcludeResources.ResourceDescription.Kinds != nil {
|
||||
resources = append(resources, r.ExcludeResources.ResourceDescription.Kinds...)
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func setRuleCount(rules []kyvernov1.Rule, status *kyvernov1.PolicyStatus) {
|
||||
validateCount, generateCount, mutateCount, verifyImagesCount := 0, 0, 0, 0
|
||||
for _, rule := range rules {
|
||||
|
|
|
@ -2,9 +2,12 @@ package webhook
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
v1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
"gotest.tools/assert"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
|
@ -160,3 +163,202 @@ func Test_RuleCount(t *testing.T) {
|
|||
assert.Equal(t, status.RuleCount.Mutate, 1)
|
||||
assert.Equal(t, status.RuleCount.VerifyImages, 2)
|
||||
}
|
||||
|
||||
func TestGetMinimumOperations(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputMap map[string]bool
|
||||
expectedResult []admissionregistrationv1.OperationType
|
||||
}{
|
||||
{
|
||||
name: "Test Case 1",
|
||||
inputMap: map[string]bool{
|
||||
webhookCreate: true,
|
||||
webhookUpdate: false,
|
||||
webhookDelete: true,
|
||||
},
|
||||
expectedResult: []admissionregistrationv1.OperationType{webhookCreate, webhookDelete},
|
||||
},
|
||||
{
|
||||
name: "Test Case 2",
|
||||
inputMap: map[string]bool{
|
||||
webhookCreate: false,
|
||||
webhookUpdate: false,
|
||||
webhookDelete: false,
|
||||
webhookConnect: true,
|
||||
},
|
||||
expectedResult: []admissionregistrationv1.OperationType{webhookConnect},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
result := getMinimumOperations(testCase.inputMap)
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i] < result[j]
|
||||
})
|
||||
sort.Slice(testCase.expectedResult, func(i, j int) bool {
|
||||
return testCase.expectedResult[i] < testCase.expectedResult[j]
|
||||
})
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Errorf("Expected %v, but got %v", testCase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeOperationsForMutatingWebhookConf(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
rules []kyverno.Rule
|
||||
expectedResult map[string]bool
|
||||
}{
|
||||
{
|
||||
name: "Test Case 1",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
Mutation: kyverno.Mutation{
|
||||
PatchesJSON6902: "add",
|
||||
},
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []v1.AdmissionOperation{webhookCreate},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]bool{
|
||||
webhookCreate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Case 2",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
Mutation: kyverno.Mutation{
|
||||
PatchesJSON6902: "add",
|
||||
},
|
||||
MatchResources: kyverno.MatchResources{},
|
||||
ExcludeResources: kyverno.MatchResources{},
|
||||
},
|
||||
{
|
||||
Mutation: kyverno.Mutation{
|
||||
PatchesJSON6902: "add",
|
||||
},
|
||||
MatchResources: kyverno.MatchResources{},
|
||||
ExcludeResources: kyverno.MatchResources{},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]bool{
|
||||
webhookCreate: true,
|
||||
webhookUpdate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Case 2",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
Mutation: kyverno.Mutation{
|
||||
PatchesJSON6902: "add",
|
||||
},
|
||||
MatchResources: kyverno.MatchResources{},
|
||||
ExcludeResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []v1.AdmissionOperation{webhookCreate},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]bool{
|
||||
webhookCreate: false,
|
||||
webhookUpdate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
var result map[string]bool
|
||||
for _, r := range testCase.rules {
|
||||
result = computeOperationsForMutatingWebhookConf(r, make(map[string]bool))
|
||||
}
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Errorf("Expected %v, but got %v", testCase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeOperationsForValidatingWebhookConf(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
rules []kyverno.Rule
|
||||
expectedResult map[string]bool
|
||||
}{
|
||||
{
|
||||
name: "Test Case 1",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []v1.AdmissionOperation{webhookCreate},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]bool{
|
||||
webhookCreate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Case 2",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
MatchResources: kyverno.MatchResources{},
|
||||
ExcludeResources: kyverno.MatchResources{},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]bool{
|
||||
webhookCreate: true,
|
||||
webhookUpdate: true,
|
||||
webhookConnect: true,
|
||||
webhookDelete: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Case 3",
|
||||
rules: []kyverno.Rule{
|
||||
{
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []v1.AdmissionOperation{webhookCreate, webhookUpdate},
|
||||
},
|
||||
},
|
||||
ExcludeResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Operations: []v1.AdmissionOperation{webhookDelete},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResult: map[string]bool{
|
||||
webhookCreate: true,
|
||||
webhookUpdate: true,
|
||||
webhookDelete: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
var result map[string]bool
|
||||
for _, r := range testCase.rules {
|
||||
result = computeOperationsForValidatingWebhookConf(r, make(map[string]bool))
|
||||
}
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Errorf("Expected %v, but got %v", testCase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
## Description
|
||||
|
||||
This test verifies that operations configured dynamically are correct in mutatingwebhookconfiguration with multiple policies
|
||||
## Steps
|
||||
|
||||
1. - Create 2 policies with mutate
|
||||
- Assert policy gets ready
|
||||
2. - Assert that the resource mutation webhook is configured correctly
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dyn-op-mutate-multiple
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: webhooks.yaml
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-apparmor-annotations
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,53 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-apparmor-annotations
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add AppArmor Annotations
|
||||
policies.kyverno.io/category: PSP Migration
|
||||
policies.kyverno.io/subject: Pod,Annotation
|
||||
kyverno.io/kyverno-version: 1.10.0
|
||||
spec:
|
||||
rules:
|
||||
- name: apparmor-runtime-default
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
mutate:
|
||||
foreach:
|
||||
- list: request.object.spec.containers[]
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
annotations:
|
||||
"container.apparmor.security.beta.kubernetes.io/{{element.name}}": runtime/default
|
||||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-annotation
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add AppArmor Annotations
|
||||
policies.kyverno.io/category: PSP Migration
|
||||
policies.kyverno.io/subject: Pod,Annotation
|
||||
kyverno.io/kyverno-version: 1.10.0
|
||||
spec:
|
||||
rules:
|
||||
- name: add-annotation
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Secret
|
||||
operations:
|
||||
- UPDATE
|
||||
mutate:
|
||||
foreach:
|
||||
- list: request.object.spec.containers[]
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
annotations:
|
||||
"container/{{element.name}}": runtime
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-mutating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- ''
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- UPDATE
|
||||
resources:
|
||||
- secrets
|
||||
scope: Namespaced
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies that operations configured dynmically are correct in mutatingwebhookconfiguration
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy with mutate
|
||||
- Assert policy gets ready
|
||||
2. - Assert that the resource mutation webhook is configured correctly
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dyn-op-mutate
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: webhooks.yaml
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-apparmor-annotations
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
26
test/conformance/chainsaw/webhooks/dyn-op-mutate/policy.yaml
Normal file
26
test/conformance/chainsaw/webhooks/dyn-op-mutate/policy.yaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-apparmor-annotations
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add AppArmor Annotations
|
||||
policies.kyverno.io/category: PSP Migration
|
||||
policies.kyverno.io/subject: Pod,Annotation
|
||||
kyverno.io/kyverno-version: 1.10.0
|
||||
spec:
|
||||
rules:
|
||||
- name: apparmor-runtime-default
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
mutate:
|
||||
foreach:
|
||||
- list: request.object.spec.containers[]
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
annotations:
|
||||
"container.apparmor.security.beta.kubernetes.io/{{element.name}}": runtime/default
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-mutating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies that operations configured dynmically are correct in both validatingadmissionwebhooks and mutating webhooks
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create policies with validate block and mutate block
|
||||
- Assert policies get ready
|
||||
2. - Assert that the resource validation and mutation webhook are configured correctly
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dyn-op-validate-and-mutate
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy-01.yaml
|
||||
- assert:
|
||||
file: policy-assert1.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: webhooks-02.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: policy-03.yaml
|
||||
- assert:
|
||||
file: policy-assert2.yaml
|
||||
- name: step-04
|
||||
try:
|
||||
- assert:
|
||||
file: webhooks-04.yaml
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-apparmor-annotations
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add AppArmor Annotations
|
||||
policies.kyverno.io/category: PSP Migration
|
||||
policies.kyverno.io/subject: Pod,Annotation
|
||||
kyverno.io/kyverno-version: 1.10.0
|
||||
spec:
|
||||
rules:
|
||||
- name: apparmor-runtime-default
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
mutate:
|
||||
foreach:
|
||||
- list: request.object.spec.containers[]
|
||||
patchStrategicMerge:
|
||||
metadata:
|
||||
annotations:
|
||||
"container.apparmor.security.beta.kubernetes.io/{{element.name}}": runtime/default
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: add-apparmor-annotations
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-mutating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies that operations configured dynamically are correct in validatingadmissionwebhooks with multiple policies
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy with validate block
|
||||
- Assert policy gets ready
|
||||
2. - Assert that the resource validation webhook is configured correctly
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dyn-op-validate-multiple
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: webhooks.yaml
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,53 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- ConfigMap
|
||||
exclude:
|
||||
any:
|
||||
- resources:
|
||||
operations:
|
||||
- DELETE
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
||||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-match
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-match
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
validate:
|
||||
message: 'The label `match` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
match: '?*'
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- ''
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- CONNECT
|
||||
resources:
|
||||
- configmaps
|
||||
scope: Namespaced
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
|
@ -0,0 +1,9 @@
|
|||
## Description
|
||||
|
||||
This test verifies that operations configured dynmically are correct in validatingadmissionwebhooks
|
||||
|
||||
## Steps
|
||||
|
||||
1. - Create a policy with validate block
|
||||
- Assert policy gets ready
|
||||
2. - Assert that the resource validation webhook is configured correctly
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dyn-op-validate
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- assert:
|
||||
file: webhooks.yaml
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: require-team
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- '*/scale'
|
||||
operations:
|
||||
- CREATE
|
||||
validate:
|
||||
message: 'The label `team` is required.'
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
team: '?*'
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
webhook.kyverno.io/managed-by: kyverno
|
||||
name: kyverno-resource-validating-webhook-cfg
|
||||
webhooks:
|
||||
- rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- '*/scale'
|
||||
scope: '*'
|
Loading…
Add table
Reference in a new issue