mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
261 lines
6.2 KiB
Go
261 lines
6.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang/glog"
|
|
|
|
"github.com/minio/minio/pkg/wildcard"
|
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
|
"github.com/nirmata/kyverno/pkg/engine/variables"
|
|
"github.com/nirmata/kyverno/pkg/utils"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
)
|
|
|
|
//EngineStats stores in the statistics for a single application of resource
|
|
type EngineStats struct {
|
|
// average time required to process the policy rules on a resource
|
|
ExecutionTime time.Duration
|
|
// Count of rules that were applied successfully
|
|
RulesAppliedCount int
|
|
}
|
|
|
|
//MatchesResourceDescription checks if the resource matches resource desription of the rule or not
|
|
func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno.Rule) bool {
|
|
matches := rule.MatchResources.ResourceDescription
|
|
exclude := rule.ExcludeResources.ResourceDescription
|
|
|
|
if len(matches.Kinds) > 0 {
|
|
if !findKind(matches.Kinds, resource.GetKind()) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
name := resource.GetName()
|
|
|
|
namespace := resource.GetNamespace()
|
|
|
|
if matches.Name != "" {
|
|
// Matches
|
|
if !wildcard.Match(matches.Name, name) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Matches
|
|
// check if the resource namespace is defined in the list of namespace pattern
|
|
if len(matches.Namespaces) > 0 && !utils.ContainsNamepace(matches.Namespaces, namespace) {
|
|
return false
|
|
}
|
|
|
|
// Matches
|
|
if matches.Selector != nil {
|
|
selector, err := metav1.LabelSelectorAsSelector(matches.Selector)
|
|
if err != nil {
|
|
glog.Error(err)
|
|
return false
|
|
}
|
|
if !selector.Matches(labels.Set(resource.GetLabels())) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
excludeName := func(name string) Condition {
|
|
if exclude.Name == "" {
|
|
return NotEvaluate
|
|
}
|
|
if wildcard.Match(exclude.Name, name) {
|
|
return Skip
|
|
}
|
|
return Process
|
|
}
|
|
|
|
excludeNamespace := func(namespace string) Condition {
|
|
if len(exclude.Namespaces) == 0 {
|
|
return NotEvaluate
|
|
}
|
|
if utils.ContainsNamepace(exclude.Namespaces, namespace) {
|
|
return Skip
|
|
}
|
|
return Process
|
|
}
|
|
|
|
excludeSelector := func(labelsMap map[string]string) Condition {
|
|
if exclude.Selector == nil {
|
|
return NotEvaluate
|
|
}
|
|
selector, err := metav1.LabelSelectorAsSelector(exclude.Selector)
|
|
// if the label selector is incorrect, should be fail or
|
|
if err != nil {
|
|
glog.Error(err)
|
|
return Skip
|
|
}
|
|
if selector.Matches(labels.Set(labelsMap)) {
|
|
return Skip
|
|
}
|
|
return Process
|
|
}
|
|
|
|
excludeKind := func(kind string) Condition {
|
|
if len(exclude.Kinds) == 0 {
|
|
return NotEvaluate
|
|
}
|
|
|
|
if findKind(exclude.Kinds, kind) {
|
|
return Skip
|
|
}
|
|
|
|
return Process
|
|
}
|
|
|
|
// 0 -> dont check
|
|
// 1 -> is not to be exclude
|
|
// 2 -> to be exclude
|
|
excludeEval := []Condition{}
|
|
|
|
if ret := excludeName(resource.GetName()); ret != NotEvaluate {
|
|
excludeEval = append(excludeEval, ret)
|
|
}
|
|
if ret := excludeNamespace(resource.GetNamespace()); ret != NotEvaluate {
|
|
excludeEval = append(excludeEval, ret)
|
|
}
|
|
if ret := excludeSelector(resource.GetLabels()); ret != NotEvaluate {
|
|
excludeEval = append(excludeEval, ret)
|
|
}
|
|
if ret := excludeKind(resource.GetKind()); ret != NotEvaluate {
|
|
excludeEval = append(excludeEval, ret)
|
|
}
|
|
// Filtered NotEvaluate
|
|
|
|
if len(excludeEval) == 0 {
|
|
// nothing to exclude
|
|
return true
|
|
}
|
|
return func() bool {
|
|
for _, ret := range excludeEval {
|
|
if ret == Process {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}()
|
|
}
|
|
|
|
//Condition type for conditions
|
|
type Condition int
|
|
|
|
const (
|
|
// NotEvaluate to not-evaluate to condition
|
|
NotEvaluate Condition = 0
|
|
// Process to process the condition
|
|
Process Condition = 1
|
|
// Skip to skip the condition
|
|
Skip Condition = 2
|
|
)
|
|
|
|
// ParseResourceInfoFromObject get kind/namepace/name from resource
|
|
func ParseResourceInfoFromObject(rawResource []byte) string {
|
|
|
|
kind := ParseKindFromObject(rawResource)
|
|
namespace := ParseNamespaceFromObject(rawResource)
|
|
name := ParseNameFromObject(rawResource)
|
|
return strings.Join([]string{kind, namespace, name}, "/")
|
|
}
|
|
|
|
//ParseKindFromObject get kind from resource
|
|
func ParseKindFromObject(bytes []byte) string {
|
|
var objectJSON map[string]interface{}
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
return objectJSON["kind"].(string)
|
|
}
|
|
|
|
//ParseNameFromObject extracts resource name from JSON obj
|
|
func ParseNameFromObject(bytes []byte) string {
|
|
var objectJSON map[string]interface{}
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
meta, ok := objectJSON["metadata"]
|
|
if !ok {
|
|
return ""
|
|
}
|
|
|
|
metaMap, ok := meta.(map[string]interface{})
|
|
if !ok {
|
|
return ""
|
|
}
|
|
if name, ok := metaMap["name"].(string); ok {
|
|
return name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// ParseNamespaceFromObject extracts the namespace from the JSON obj
|
|
func ParseNamespaceFromObject(bytes []byte) string {
|
|
var objectJSON map[string]interface{}
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
meta, ok := objectJSON["metadata"]
|
|
if !ok {
|
|
return ""
|
|
}
|
|
metaMap, ok := meta.(map[string]interface{})
|
|
if !ok {
|
|
return ""
|
|
}
|
|
|
|
if name, ok := metaMap["namespace"].(string); ok {
|
|
return name
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func findKind(kinds []string, kindGVK string) bool {
|
|
for _, kind := range kinds {
|
|
if kind == kindGVK {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// validateGeneralRuleInfoVariables validate variable subtition defined in
|
|
// - MatchResources
|
|
// - ExcludeResources
|
|
// - Conditions
|
|
func validateGeneralRuleInfoVariables(ctx context.EvalInterface, rule kyverno.Rule) string {
|
|
var tempRule kyverno.Rule
|
|
var tempRulePattern interface{}
|
|
|
|
tempRule.MatchResources = rule.MatchResources
|
|
tempRule.ExcludeResources = rule.ExcludeResources
|
|
tempRule.Conditions = rule.Conditions
|
|
|
|
raw, err := json.Marshal(tempRule)
|
|
if err != nil {
|
|
glog.Infof("failed to serilize rule info while validating variable substitution: %v", err)
|
|
return ""
|
|
}
|
|
|
|
if err := json.Unmarshal(raw, &tempRulePattern); err != nil {
|
|
glog.Infof("failed to serilize rule info while validating variable substitution: %v", err)
|
|
return ""
|
|
}
|
|
|
|
return variables.ValidateVariables(ctx, tempRulePattern)
|
|
}
|
|
|
|
func newPathNotPresentRuleResponse(rname, rtype, msg string) response.RuleResponse {
|
|
return response.RuleResponse{
|
|
Name: rname,
|
|
Type: rtype,
|
|
Message: msg,
|
|
Success: true,
|
|
PathNotPresent: true,
|
|
}
|
|
}
|