2019-10-18 17:45:24 -07:00
package policy
2019-09-27 16:31:27 -07:00
import (
2020-03-24 23:12:45 +05:30
"encoding/json"
2019-09-27 16:31:27 -07:00
"errors"
"fmt"
"reflect"
2019-12-04 18:50:51 -08:00
"strings"
2019-09-27 16:31:27 -07:00
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/kyverno/common"
2020-10-07 02:57:40 +05:30
2020-04-04 12:46:51 +05:30
"github.com/minio/minio/pkg/wildcard"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/openapi"
2020-03-04 19:16:26 +05:30
2020-10-07 11:12:31 -07:00
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
dclient "github.com/kyverno/kyverno/pkg/dclient"
2019-12-04 18:50:51 -08:00
rbacv1 "k8s.io/api/rbac/v1"
2019-09-27 16:31:27 -07:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
2019-10-21 14:22:31 -07:00
// Validate does some initial check to verify some conditions
// - One operation per rule
// - ResourceDescription mandatory checks
2020-03-29 09:09:26 +05:30
func Validate ( policyRaw [ ] byte , client * dclient . Client , mock bool , openAPIController * openapi . Controller ) error {
2020-09-03 22:14:54 +05:30
// check for invalid fields
err := checkInvalidFields ( policyRaw )
if err != nil {
return err
}
2020-03-24 23:12:45 +05:30
var p kyverno . ClusterPolicy
2020-09-03 22:14:54 +05:30
err = json . Unmarshal ( policyRaw , & p )
2020-03-24 23:12:45 +05:30
if err != nil {
2020-10-14 17:39:45 -07:00
return fmt . Errorf ( "failed to unmarshal policy: %v" , err )
2020-03-24 23:12:45 +05:30
}
2020-09-01 21:42:05 +05:30
if common . PolicyHasVariables ( p ) && common . PolicyHasNonAllowedVariables ( p ) {
2020-10-19 12:36:55 -07:00
return fmt . Errorf ( "policy contains unknown variables" )
2020-08-24 03:41:03 +05:30
}
2019-10-21 14:22:31 -07:00
if path , err := validateUniqueRuleName ( p ) ; err != nil {
return fmt . Errorf ( "path: spec.%s: %v" , path , err )
2019-10-03 16:49:41 -07:00
}
2020-05-06 19:46:32 +05:30
if p . Spec . Background == nil || ( p . Spec . Background != nil && * p . Spec . Background ) {
2020-05-06 00:29:40 +05:30
if err := ContainsVariablesOtherThanObject ( p ) ; err != nil {
2020-05-06 19:46:32 +05:30
return fmt . Errorf ( "only variables referring request.object are allowed in background mode. Set spec.background=false to disable background mode for this policy rule. %s " , err )
2019-12-30 17:08:50 -08:00
}
}
2019-10-03 16:49:41 -07:00
2019-10-21 14:22:31 -07:00
for i , rule := range p . Spec . Rules {
2020-10-14 17:39:45 -07:00
2019-10-21 14:22:31 -07:00
// validate resource description
if path , err := validateResources ( rule ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d].%s: %v" , i , path , err )
}
2020-10-14 17:39:45 -07:00
2019-10-21 14:22:31 -07:00
// validate rule types
// only one type of rule is allowed per rule
if err := validateRuleType ( rule ) ; err != nil {
// 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 )
2019-09-27 19:03:55 -07:00
}
2020-10-14 17:39:45 -07:00
if err := validateRuleContext ( rule ) ; err != nil {
return fmt . Errorf ( "path: spec.rules[%d]: %v" , i , err )
}
2020-08-19 21:37:23 +05:30
// validate Cluster Resources in namespaced cluster policy
// For namespaced cluster policy, ClusterResource type field and values are not allowed in match and exclude
2020-08-21 23:07:54 +05:30
if ! mock && p . ObjectMeta . Namespace != "" {
2020-09-01 21:42:05 +05:30
var Empty struct { }
clusterResourcesMap := make ( map [ string ] * struct { } )
// Get all the cluster type kind supported by cluster
2020-11-09 11:26:12 -08:00
2020-09-01 21:42:05 +05:30
res , err := client . GetDiscoveryCache ( ) . ServerPreferredResources ( )
if err != nil {
return err
}
for _ , resList := range res {
for _ , r := range resList . APIResources {
2020-09-19 00:48:13 +05:30
if ! r . Namespaced {
if _ , ok := clusterResourcesMap [ r . Kind ] ; ! ok {
2020-09-01 21:42:05 +05:30
clusterResourcesMap [ r . Kind ] = & Empty
2020-08-19 21:37:23 +05:30
}
}
}
2020-09-01 21:42:05 +05:30
}
2020-08-21 23:07:54 +05:30
2020-09-01 21:42:05 +05:30
clusterResources := make ( [ ] string , 0 , len ( clusterResourcesMap ) )
for k := range clusterResourcesMap {
clusterResources = append ( clusterResources , k )
2020-08-19 21:37:23 +05:30
}
2020-09-01 21:42:05 +05:30
return checkClusterResourceInMatchAndExclude ( rule , clusterResources )
}
2019-09-27 16:31:27 -07:00
2020-10-14 17:39:45 -07:00
if doMatchAndExcludeConflict ( rule ) {
2020-04-27 22:01:33 +05:30
return fmt . Errorf ( "path: spec.rules[%v]: rule is matching an empty set" , rule . Name )
}
2020-03-20 20:23:34 +05:30
2020-03-11 18:14:23 -07:00
// validate rule actions
// - Mutate
// - Validate
// - Generate
2020-03-17 17:23:18 -07:00
if err := validateActions ( i , rule , client , mock ) ; err != nil {
2020-03-11 18:14:23 -07:00
return err
2019-09-27 19:03:55 -07:00
}
2020-02-26 16:08:56 +05:30
// If a rules match block does not match any kind,
// we should only allow such rules to have metadata in its overlay
if len ( rule . MatchResources . Kinds ) == 0 {
if ! ruleOnlyDealsWithResourceMetaData ( rule ) {
return fmt . Errorf ( "policy can only deal with the metadata field of the resource if" +
" the rule does not match an kind" )
}
}
2020-08-29 06:52:22 +05:30
// Validate string values in labels
if ! isLabelAndAnnotationsString ( rule ) {
return fmt . Errorf ( "labels and annotations supports only string values, \"use double quotes around the non string values\"" )
}
2019-09-27 19:03:55 -07:00
}
2020-01-25 14:53:12 +05:30
2020-04-01 19:06:13 +05:30
if ! mock {
if err := openAPIController . ValidatePolicyFields ( policyRaw ) ; err != nil {
return err
}
} else {
if err := openAPIController . ValidatePolicyMutation ( p ) ; err != nil {
return err
}
2020-03-04 19:16:26 +05:30
}
2019-09-27 19:03:55 -07:00
return nil
}
2020-09-03 22:14:54 +05:30
// checkInvalidFields - checks invalid fields in webhook policy request
// policy supports 5 json fields in types.go i.e. "apiVersion", "kind", "metadata", "spec", "status"
// If the webhook request policy contains new fields then block creation of policy
func checkInvalidFields ( policyRaw [ ] byte ) error {
// hardcoded supported fields by policy
var allowedKeys = [ ] string { "apiVersion" , "kind" , "metadata" , "spec" , "status" }
var data interface { }
err := json . Unmarshal ( policyRaw , & data )
if err != nil {
return fmt . Errorf ( "failed to unmarshal policy admission request err %v" , err )
}
mapData := data . ( map [ string ] interface { } )
// validate any new fields in the admission request against the supported fields and block the request with any new fields
for requestField , _ := range mapData {
ok := false
for _ , allowedField := range allowedKeys {
if requestField == allowedField {
ok = true
break
}
}
2020-10-14 17:39:45 -07:00
2020-09-03 22:14:54 +05:30
if ! ok {
2020-10-14 17:39:45 -07:00
return fmt . Errorf ( "unknown field \"%s\" in policy" , requestField )
2020-09-03 22:14:54 +05:30
}
}
return nil
}
2020-10-14 17:39:45 -07:00
// doMatchAndExcludeConflict checks if the resultant
2020-04-04 12:46:51 +05:30
// of match and exclude block is not an empty set
2020-10-14 17:39:45 -07:00
func doMatchAndExcludeConflict ( rule kyverno . Rule ) bool {
2020-03-20 20:23:34 +05:30
2020-04-04 16:18:36 +05:30
if reflect . DeepEqual ( rule . ExcludeResources , kyverno . ExcludeResources { } ) {
return false
}
2020-03-20 20:23:34 +05:30
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
}
}
2020-04-04 12:46:51 +05:30
if len ( excludeRoles ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . UserInfo . Roles ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for _ , role := range rule . MatchResources . UserInfo . Roles {
if ! excludeRoles [ role ] {
return false
}
2020-03-20 20:23:34 +05:30
}
}
2020-04-04 12:46:51 +05:30
if len ( excludeClusterRoles ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . UserInfo . ClusterRoles ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for _ , clusterRole := range rule . MatchResources . UserInfo . ClusterRoles {
if ! excludeClusterRoles [ clusterRole ] {
return false
}
2020-03-20 20:23:34 +05:30
}
}
2020-04-04 12:46:51 +05:30
if len ( excludeSubjects ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . UserInfo . Subjects ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for _ , subject := range rule . MatchResources . UserInfo . Subjects {
subjectRaw , _ := json . Marshal ( subject )
if ! excludeSubjects [ string ( subjectRaw ) ] {
return false
}
2020-03-20 20:23:34 +05:30
}
}
2020-04-04 12:46:51 +05:30
if rule . ExcludeResources . ResourceDescription . Name != "" {
if ! wildcard . Match ( rule . ExcludeResources . ResourceDescription . Name , rule . MatchResources . ResourceDescription . Name ) {
return false
2020-03-20 20:23:34 +05:30
}
}
2020-04-04 14:49:50 +05:30
if len ( excludeNamespaces ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . ResourceDescription . Namespaces ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for _ , namespace := range rule . MatchResources . ResourceDescription . Namespaces {
if ! excludeNamespaces [ namespace ] {
return false
}
2020-03-20 20:23:34 +05:30
}
}
2020-04-04 14:49:50 +05:30
if len ( excludeKinds ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . ResourceDescription . Kinds ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for _ , kind := range rule . MatchResources . ResourceDescription . Kinds {
if ! excludeKinds [ kind ] {
return false
}
2020-03-20 20:23:34 +05:30
}
}
if rule . MatchResources . ResourceDescription . Selector != nil && rule . ExcludeResources . ResourceDescription . Selector != nil {
2020-04-04 14:49:50 +05:30
if len ( excludeMatchExpressions ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . ResourceDescription . Selector . MatchExpressions ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for _ , matchExpression := range rule . MatchResources . ResourceDescription . Selector . MatchExpressions {
matchExpressionRaw , _ := json . Marshal ( matchExpression )
2020-04-04 14:49:50 +05:30
if ! excludeMatchExpressions [ string ( matchExpressionRaw ) ] {
2020-04-04 12:46:51 +05:30
return false
}
2020-03-20 20:23:34 +05:30
}
}
2020-04-04 14:49:50 +05:30
if len ( rule . ExcludeResources . ResourceDescription . Selector . MatchLabels ) > 0 {
2020-04-27 15:05:10 +05:30
if len ( rule . MatchResources . ResourceDescription . Selector . MatchLabels ) == 0 {
return false
}
2020-04-04 12:46:51 +05:30
for label , value := range rule . MatchResources . ResourceDescription . Selector . MatchLabels {
if rule . ExcludeResources . ResourceDescription . Selector . MatchLabels [ label ] != value {
return false
}
2020-03-20 20:23:34 +05:30
}
}
}
2020-10-07 02:57:40 +05:30
if ( rule . MatchResources . ResourceDescription . Selector == nil && rule . ExcludeResources . ResourceDescription . Selector != nil ) ||
( rule . MatchResources . ResourceDescription . Selector != nil && rule . ExcludeResources . ResourceDescription . Selector == nil ) {
return false
}
2020-04-04 12:46:51 +05:30
return true
2020-03-20 20:23:34 +05:30
}
2020-08-29 06:52:22 +05:30
// isLabelAndAnnotationsString :- Validate if labels and annotations contains only string values
func isLabelAndAnnotationsString ( rule kyverno . Rule ) bool {
// checkMetadata - Verify if the labels and annotations contains string value inside metadata
checkMetadata := func ( patternMap map [ string ] interface { } ) bool {
for k := range patternMap {
if k == "metadata" {
metaKey , ok := patternMap [ k ] . ( map [ string ] interface { } )
if ok {
// range over metadata
for mk := range metaKey {
if mk == "labels" {
labelKey , ok := metaKey [ mk ] . ( map [ string ] interface { } )
if ok {
// range over labels
for _ , val := range labelKey {
if reflect . TypeOf ( val ) . String ( ) != "string" {
return false
}
}
}
} else if mk == "annotations" {
annotationKey , ok := metaKey [ mk ] . ( map [ string ] interface { } )
if ok {
// range over annotations
for _ , val := range annotationKey {
if reflect . TypeOf ( val ) . String ( ) != "string" {
return false
}
}
}
}
}
}
}
}
return true
}
patternMap , ok := rule . Validation . Pattern . ( map [ string ] interface { } )
if ok {
return checkMetadata ( patternMap )
} else if len ( rule . Validation . AnyPattern ) > 0 {
anyPatterns := rule . Validation . AnyPattern
for _ , pattern := range anyPatterns {
patternMap , ok := pattern . ( map [ string ] interface { } )
if ok {
ret := checkMetadata ( patternMap )
if ret == false {
return ret
}
}
}
}
return true
}
2020-02-26 16:08:56 +05:30
func ruleOnlyDealsWithResourceMetaData ( rule kyverno . Rule ) bool {
overlayMap , _ := rule . Mutation . Overlay . ( map [ string ] interface { } )
for k := range overlayMap {
if k != "metadata" {
return false
}
}
for _ , patch := range rule . Mutation . Patches {
if ! strings . HasPrefix ( patch . Path , "/metadata" ) {
return false
}
}
patternMap , _ := rule . Validation . Pattern . ( map [ string ] interface { } )
for k := range patternMap {
if k != "metadata" {
return false
}
}
for _ , pattern := range rule . Validation . AnyPattern {
patternMap , _ := pattern . ( map [ string ] interface { } )
for k := range patternMap {
if k != "metadata" {
return false
}
}
}
return true
}
2019-10-21 14:22:31 -07:00
func validateResources ( rule kyverno . Rule ) ( string , error ) {
2019-12-04 18:50:51 -08:00
// validate userInfo in match and exclude
if path , err := validateUserInfo ( rule ) ; err != nil {
return fmt . Sprintf ( "resources.%s" , path ) , err
}
2019-10-21 14:22:31 -07:00
// matched resources
if path , err := validateMatchedResourceDescription ( rule . MatchResources . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "resources.%s" , path ) , err
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
// exclude resources
if path , err := validateExcludeResourceDescription ( rule . ExcludeResources . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "resources.%s" , path ) , err
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
return "" , nil
}
2019-09-27 16:31:27 -07:00
2019-10-21 14:22:31 -07:00
// ValidateUniqueRuleName checks if the rule names are unique across a policy
func validateUniqueRuleName ( p kyverno . ClusterPolicy ) ( string , error ) {
var ruleNames [ ] string
2019-09-27 19:03:55 -07:00
2019-10-21 14:22:31 -07:00
for i , rule := range p . Spec . Rules {
if containString ( ruleNames , rule . Name ) {
return fmt . Sprintf ( "rule[%d]" , i ) , fmt . Errorf ( ` duplicate rule name: '%s' ` , rule . Name )
}
ruleNames = append ( ruleNames , rule . Name )
2019-10-03 14:47:50 -07:00
}
2019-10-21 14:22:31 -07:00
return "" , nil
2019-09-27 16:31:27 -07:00
}
// validateRuleType checks only one type of rule is defined per rule
2019-10-18 17:45:24 -07:00
func validateRuleType ( r kyverno . Rule ) error {
2020-04-14 19:06:48 +05:30
ruleTypes := [ ] bool { r . HasMutate ( ) , r . HasValidate ( ) , r . HasGenerate ( ) }
2019-09-27 16:31:27 -07:00
2019-10-03 16:49:41 -07:00
operationCount := func ( ) int {
count := 0
for _ , v := range ruleTypes {
if v {
count ++
}
}
return count
} ( )
2019-09-27 16:31:27 -07:00
2019-10-03 16:49:41 -07:00
if operationCount == 0 {
2019-10-21 14:22:31 -07:00
return fmt . Errorf ( "no operation defined in the rule '%s'.(supported operations: mutation,validation,generation)" , r . Name )
2019-10-03 16:49:41 -07:00
} else if operationCount != 1 {
return fmt . Errorf ( "multiple operations defined in the rule '%s', only one type of operation is allowed per rule" , r . Name )
2019-09-27 16:31:27 -07:00
}
2019-10-03 16:49:41 -07:00
return nil
2019-09-27 16:31:27 -07:00
}
2020-11-09 11:26:12 -08:00
func validateRuleContext ( rule kyverno . Rule ) error {
2020-10-14 17:39:45 -07:00
if rule . Context == nil || len ( rule . Context ) == 0 {
return nil
}
for _ , entry := range rule . Context {
2020-11-09 11:26:12 -08:00
if entry . Name == "" {
2020-10-14 17:39:45 -07:00
return fmt . Errorf ( "a name is required for context entries" )
}
2020-10-19 12:36:55 -07:00
2020-10-14 17:39:45 -07:00
if entry . ConfigMap != nil {
if entry . ConfigMap . Name == "" {
return fmt . Errorf ( "a name is required for configMap context entry" )
}
if entry . ConfigMap . Namespace == "" {
return fmt . Errorf ( "a namespace is required for configMap context entry" )
}
}
}
return nil
}
2019-10-03 18:19:47 -07:00
// validateResourceDescription checks if all necesarry fields are present and have values. Also checks a Selector.
2019-09-27 16:31:27 -07:00
// field type is checked through openapi
// Returns error if
2019-10-01 15:01:24 -07:00
// - kinds is empty array in matched resource block, i.e. kinds: []
2019-09-27 16:31:27 -07:00
// - selector is invalid
2019-10-21 14:22:31 -07:00
func validateMatchedResourceDescription ( rd kyverno . ResourceDescription ) ( string , error ) {
2019-10-18 17:45:24 -07:00
if reflect . DeepEqual ( rd , kyverno . ResourceDescription { } ) {
2019-10-21 14:22:31 -07:00
return "" , fmt . Errorf ( "match resources not specified" )
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
if err := validateResourceDescription ( rd ) ; err != nil {
return "match" , err
}
return "" , nil
}
2019-12-04 18:50:51 -08:00
func validateUserInfo ( rule kyverno . Rule ) ( string , error ) {
if err := validateRoles ( rule . MatchResources . Roles ) ; err != nil {
return "match.roles" , err
}
if err := validateSubjects ( rule . MatchResources . Subjects ) ; err != nil {
return "match.subjects" , err
}
if err := validateRoles ( rule . ExcludeResources . Roles ) ; err != nil {
return "exclude.roles" , err
}
if err := validateSubjects ( rule . ExcludeResources . Subjects ) ; err != nil {
return "exclude.subjects" , err
}
return "" , nil
}
// a role must in format namespace:name
func validateRoles ( roles [ ] string ) error {
if len ( roles ) == 0 {
return nil
}
for _ , r := range roles {
role := strings . Split ( r , ":" )
if len ( role ) != 2 {
return fmt . Errorf ( "invalid role %s, expect namespace:name" , r )
}
}
return nil
}
2019-12-05 11:55:00 -08:00
// a namespace should be set in kind ServiceAccount of a subject
2019-12-04 18:50:51 -08:00
func validateSubjects ( subjects [ ] rbacv1 . Subject ) error {
if len ( subjects ) == 0 {
return nil
}
for _ , subject := range subjects {
2019-12-05 11:55:00 -08:00
if subject . Kind == "ServiceAccount" {
2019-12-04 18:50:51 -08:00
if subject . Namespace == "" {
2019-12-05 11:57:34 -08:00
return fmt . Errorf ( "service account %s in subject expects a namespace" , subject . Name )
2019-12-04 18:50:51 -08:00
}
}
}
return nil
}
2019-10-21 14:22:31 -07:00
func validateExcludeResourceDescription ( rd kyverno . ResourceDescription ) ( string , error ) {
if reflect . DeepEqual ( rd , kyverno . ResourceDescription { } ) {
// exclude is not mandatory
return "" , nil
}
if err := validateResourceDescription ( rd ) ; err != nil {
return "exclude" , err
}
return "" , nil
2019-10-03 18:19:47 -07:00
}
// validateResourceDescription returns error if selector is invalid
// field type is checked through openapi
2019-10-18 17:45:24 -07:00
func validateResourceDescription ( rd kyverno . ResourceDescription ) error {
2019-09-27 16:31:27 -07:00
if rd . Selector != nil {
selector , err := metav1 . LabelSelectorAsSelector ( rd . Selector )
if err != nil {
return err
}
requirements , _ := selector . Requirements ( )
if len ( requirements ) == 0 {
return errors . New ( "the requirements are not specified in selector" )
}
}
return nil
}
2020-08-19 21:37:23 +05:30
// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
// Match and Exclude block
func checkClusterResourceInMatchAndExclude ( rule kyverno . Rule , clusterResources [ ] string ) error {
// Contains Namespaces in Match->ResourceDescription
if len ( rule . MatchResources . ResourceDescription . Namespaces ) > 0 {
return fmt . Errorf ( "namespaced cluster policy : field namespaces not allowed in match.resources" )
}
// Contains Namespaces in Exclude->ResourceDescription
if len ( rule . ExcludeResources . ResourceDescription . Namespaces ) > 0 {
return fmt . Errorf ( "namespaced cluster policy : field namespaces not allowed in exclude.resources" )
}
// Contains "Cluster Wide Resources" in Match->ResourceDescription->Kinds
for _ , kind := range rule . MatchResources . ResourceDescription . Kinds {
for _ , k := range clusterResources {
if kind == k {
return fmt . Errorf ( "namespaced policy : cluster type value '%s' not allowed in match.resources.kinds" , kind )
}
}
}
// Contains "Cluster Wide Resources" in Exclude->ResourceDescription->Kinds
for _ , kind := range rule . ExcludeResources . ResourceDescription . Kinds {
for _ , k := range clusterResources {
if kind == k {
return fmt . Errorf ( "namespaced policy : cluster type value '%s' not allowed in exclude.resources.kinds" , kind )
}
}
}
return nil
}