mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
Merge pull request #758 from shravanshetty1/753_conflicting_match_exclude
#753 - Validate conflicting match and exclude
This commit is contained in:
commit
b5c37f42ad
2 changed files with 195 additions and 5 deletions
|
@ -7,6 +7,8 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio/pkg/wildcard"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/openapi"
|
||||
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
|
@ -42,11 +44,6 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
|
|||
}
|
||||
|
||||
for i, rule := range p.Spec.Rules {
|
||||
// only one type of rule is allowed per rule
|
||||
if err := validateRuleType(rule); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
|
||||
}
|
||||
|
||||
// validate resource description
|
||||
if path, err := validateResources(rule); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].%s: %v", i, path, err)
|
||||
|
@ -57,6 +54,11 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
|
|||
// as there are more than 1 operation in rule, not need to evaluate it further
|
||||
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
|
||||
}
|
||||
|
||||
if doesMatchAndExcludeConflict(rule) {
|
||||
return fmt.Errorf("path: spec.rules[%v]: rule is matching an empty set", rule.Name)
|
||||
}
|
||||
|
||||
// validate rule actions
|
||||
// - Mutate
|
||||
// - Validate
|
||||
|
@ -88,6 +90,121 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
|
|||
return nil
|
||||
}
|
||||
|
||||
// doesMatchAndExcludeConflict checks if the resultant
|
||||
// of match and exclude block is not an empty set
|
||||
func doesMatchAndExcludeConflict(rule kyverno.Rule) bool {
|
||||
|
||||
if reflect.DeepEqual(rule.MatchResources, kyverno.MatchResources{}) {
|
||||
return true
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(rule.ExcludeResources, kyverno.ExcludeResources{}) {
|
||||
return false
|
||||
}
|
||||
|
||||
excludeRoles := make(map[string]bool)
|
||||
for _, role := range rule.ExcludeResources.UserInfo.Roles {
|
||||
excludeRoles[role] = true
|
||||
}
|
||||
|
||||
excludeClusterRoles := make(map[string]bool)
|
||||
for _, clusterRoles := range rule.ExcludeResources.UserInfo.ClusterRoles {
|
||||
excludeClusterRoles[clusterRoles] = true
|
||||
}
|
||||
|
||||
excludeSubjects := make(map[string]bool)
|
||||
for _, subject := range rule.ExcludeResources.UserInfo.Subjects {
|
||||
subjectRaw, _ := json.Marshal(subject)
|
||||
excludeSubjects[string(subjectRaw)] = true
|
||||
}
|
||||
|
||||
excludeKinds := make(map[string]bool)
|
||||
for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds {
|
||||
excludeKinds[kind] = true
|
||||
}
|
||||
|
||||
excludeNamespaces := make(map[string]bool)
|
||||
for _, namespace := range rule.ExcludeResources.ResourceDescription.Namespaces {
|
||||
excludeNamespaces[namespace] = true
|
||||
}
|
||||
|
||||
excludeMatchExpressions := make(map[string]bool)
|
||||
if rule.ExcludeResources.ResourceDescription.Selector != nil {
|
||||
for _, matchExpression := range rule.ExcludeResources.ResourceDescription.Selector.MatchExpressions {
|
||||
matchExpressionRaw, _ := json.Marshal(matchExpression)
|
||||
excludeMatchExpressions[string(matchExpressionRaw)] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(excludeRoles) > 0 {
|
||||
for _, role := range rule.MatchResources.UserInfo.Roles {
|
||||
if !excludeRoles[role] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(excludeClusterRoles) > 0 {
|
||||
for _, clusterRole := range rule.MatchResources.UserInfo.ClusterRoles {
|
||||
if !excludeClusterRoles[clusterRole] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(excludeSubjects) > 0 {
|
||||
for _, subject := range rule.MatchResources.UserInfo.Subjects {
|
||||
subjectRaw, _ := json.Marshal(subject)
|
||||
if !excludeSubjects[string(subjectRaw)] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rule.ExcludeResources.ResourceDescription.Name != "" {
|
||||
if !wildcard.Match(rule.ExcludeResources.ResourceDescription.Name, rule.MatchResources.ResourceDescription.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(excludeNamespaces) > 0 {
|
||||
for _, namespace := range rule.MatchResources.ResourceDescription.Namespaces {
|
||||
if !excludeNamespaces[namespace] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(excludeKinds) > 0 {
|
||||
for _, kind := range rule.MatchResources.ResourceDescription.Kinds {
|
||||
if !excludeKinds[kind] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rule.MatchResources.ResourceDescription.Selector != nil && rule.ExcludeResources.ResourceDescription.Selector != nil {
|
||||
if len(excludeMatchExpressions) > 0 {
|
||||
for _, matchExpression := range rule.MatchResources.ResourceDescription.Selector.MatchExpressions {
|
||||
matchExpressionRaw, _ := json.Marshal(matchExpression)
|
||||
if !excludeMatchExpressions[string(matchExpressionRaw)] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.ExcludeResources.ResourceDescription.Selector.MatchLabels) > 0 {
|
||||
for label, value := range rule.MatchResources.ResourceDescription.Selector.MatchLabels {
|
||||
if rule.ExcludeResources.ResourceDescription.Selector.MatchLabels[label] != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func ruleOnlyDealsWithResourceMetaData(rule kyverno.Rule) bool {
|
||||
overlayMap, _ := rule.Mutation.Overlay.(map[string]interface{})
|
||||
for k := range overlayMap {
|
||||
|
|
|
@ -960,3 +960,76 @@ func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_doesMatchExcludeConflict(t *testing.T) {
|
||||
testcases := []struct {
|
||||
description string
|
||||
rule []byte
|
||||
expectedOutput bool
|
||||
}{
|
||||
{
|
||||
description: "Same match and exclude",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: true,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude kind",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude name",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something-*","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude namespace",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something3","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude labels",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"higha"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude expression",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["databases"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude subjects",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something2","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude clusterroles",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something3","something1"],"roles":["something","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "Failed to exclude roles",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something3","something1"]}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
description: "simple",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
|
||||
expectedOutput: true,
|
||||
},
|
||||
{
|
||||
description: "simple - fail",
|
||||
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"somxething","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
|
||||
expectedOutput: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testcase := range testcases {
|
||||
var rule kyverno.Rule
|
||||
_ = json.Unmarshal(testcase.rule, &rule)
|
||||
|
||||
if doesMatchAndExcludeConflict(rule) != testcase.expectedOutput {
|
||||
t.Errorf("Testcase [%d] failed - description - %v", i+1, testcase.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue