1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-30 19:35:06 +00:00

filter resources excluded in config (#1404)

This commit is contained in:
Jim Bugwadia 2020-12-16 12:29:16 -08:00 committed by GitHub
parent df7006f768
commit c77944ddef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 147 additions and 124 deletions

View file

@ -23,26 +23,15 @@ var defaultExcludeGroupRole []string = []string{"system:serviceaccounts:kube-sys
// ConfigData stores the configuration
type ConfigData struct {
client kubernetes.Interface
// configMap Name
cmName string
// lock configuration
mux sync.RWMutex
// configuration data
filters []k8Resource
// excludeGroupRole Role
excludeGroupRole []string
//excludeUsername exclude username
excludeUsername []string
//restrictDevelopmentUsername exclude dev username like minikube and kind
client kubernetes.Interface
cmName string
mux sync.RWMutex
filters []k8Resource
excludeGroupRole []string
excludeUsername []string
restrictDevelopmentUsername []string
// hasynced
cmSycned cache.InformerSynced
log logr.Logger
cmSycned cache.InformerSynced
log logr.Logger
}
// ToFilter checks if the given resource is set to be filtered in the configuration
@ -92,12 +81,14 @@ func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapI
if cmNameEnv == "" {
log.Info("ConfigMap name not defined in env:INIT_CONFIG: loading no default configuration")
}
cd := ConfigData{
client: rclient,
cmName: os.Getenv(cmNameEnv),
cmSycned: cmInformer.Informer().HasSynced,
log: log,
}
cd.restrictDevelopmentUsername = []string{"minikube-user", "kubernetes-admin"}
//TODO: this has been added to backward support command line arguments
@ -124,6 +115,7 @@ func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapI
UpdateFunc: cd.updateCM,
DeleteFunc: cd.deleteCM,
})
return &cd
}

View file

@ -1,17 +1,11 @@
package engine
import (
"time"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/resourcecache"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"
"time"
)
// Generate checks for validity of generate rule on the resource
@ -19,30 +13,62 @@ import (
// - the caller has to check the ruleResponse to determine whether the path exist
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
func Generate(policyContext PolicyContext) (resp response.EngineResponse) {
policy := policyContext.Policy
new := policyContext.NewResource
old := policyContext.OldResource
admissionInfo := policyContext.AdmissionInfo
ctx := policyContext.Context
resCache := policyContext.ResourceCache
jsonContext := policyContext.JSONContext
logger := log.Log.WithName("Generate").WithValues("policy", policy.Name, "kind", new.GetKind(), "namespace", new.GetNamespace(), "name", new.GetName())
return filterRules(policy, new, old, admissionInfo, ctx, logger, policyContext.ExcludeGroupRole, resCache, jsonContext)
return filterRules(policyContext)
}
// filterRule checks if a rule matches the rule selection criteria.
//
func filterRule(rule kyverno.Rule, new, old unstructured.Unstructured, admissionInfo kyverno.RequestInfo, ctx context.EvalInterface, log logr.Logger, excludeGroupRole []string, resCache resourcecache.ResourceCacheIface, jsonContext *context.Context) *response.RuleResponse {
func filterRules(policyContext PolicyContext) response.EngineResponse {
kind := policyContext.NewResource.GetKind()
name := policyContext.NewResource.GetName()
namespace := policyContext.NewResource.GetNamespace()
resp := response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: policyContext.Policy.Name,
Resource: response.ResourceSpec{
Kind: kind,
Name: name,
Namespace: namespace,
},
},
}
if policyContext.ExcludeResourceFunc(kind, namespace, name) {
log.Log.WithName("Generate").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
return resp
}
for _, rule := range policyContext.Policy.Spec.Rules {
if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
}
}
return resp
}
func filterRule(rule kyverno.Rule, policyContext PolicyContext) *response.RuleResponse {
if !rule.HasGenerate() {
return nil
}
startTime := time.Now()
if err := MatchesResourceDescription(new, rule, admissionInfo, excludeGroupRole); err != nil {
if err := MatchesResourceDescription(old, rule, admissionInfo, excludeGroupRole); err == nil {
policy := policyContext.Policy
newResource := policyContext.NewResource
oldResource := policyContext.OldResource
admissionInfo := policyContext.AdmissionInfo
ctx := policyContext.Context
resCache := policyContext.ResourceCache
jsonContext := policyContext.JSONContext
excludeGroupRole := policyContext.ExcludeGroupRole
logger := log.Log.WithName("Generate").WithValues("policy", policy.Name,
"kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
if err := MatchesResourceDescription(newResource, rule, admissionInfo, excludeGroupRole); err != nil {
// if the oldResource matched, return "false" to delete GR for it
if err := MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole); err == nil {
return &response.RuleResponse{
Name: rule.Name,
Type: "Generation",
@ -52,12 +78,13 @@ func filterRule(rule kyverno.Rule, new, old unstructured.Unstructured, admission
},
}
}
return nil
}
// add configmap json data to context
if err := AddResourceToContext(log, rule.Context, resCache, jsonContext); err != nil {
log.V(4).Info("cannot add configmaps to context", "reason", err.Error())
if err := AddResourceToContext(logger, rule.Context, resCache, jsonContext); err != nil {
logger.V(4).Info("cannot add configmaps to context", "reason", err.Error())
return nil
}
@ -65,8 +92,8 @@ func filterRule(rule kyverno.Rule, new, old unstructured.Unstructured, admission
copyConditions := copyConditions(rule.Conditions)
// evaluate pre-conditions
if !variables.EvaluateConditions(log, ctx, copyConditions) {
log.V(4).Info("preconditions not satisfied, skipping rule", "rule", rule.Name)
if !variables.EvaluateConditions(logger, ctx, copyConditions) {
logger.V(4).Info("preconditions not satisfied, skipping rule", "rule", rule.Name)
return nil
}
@ -80,24 +107,3 @@ func filterRule(rule kyverno.Rule, new, old unstructured.Unstructured, admission
},
}
}
func filterRules(policy kyverno.ClusterPolicy, new, old unstructured.Unstructured, admissionInfo kyverno.RequestInfo, ctx context.EvalInterface, log logr.Logger, excludeGroupRole []string, resCache resourcecache.ResourceCacheIface, jsonContext *context.Context) response.EngineResponse {
resp := response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: policy.Name,
Resource: response.ResourceSpec{
Kind: new.GetKind(),
Name: new.GetName(),
Namespace: new.GetNamespace(),
},
},
}
for _, rule := range policy.Spec.Rules {
if ruleResp := filterRule(rule, new, old, admissionInfo, ctx, log, excludeGroupRole, resCache, jsonContext); ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
}
}
return resp
}

View file

@ -10,23 +10,33 @@ import (
// PolicyContext contains the contexts for engine to process
type PolicyContext struct {
// policy to be processed
// Policy is the policy to be processed
Policy kyverno.ClusterPolicy
// resource to be processed
// NewResource is the resource to be processed
NewResource unstructured.Unstructured
// old Resource - Update operations
OldResource unstructured.Unstructured
// OldResource is the prior resource for an update, or nil
OldResource unstructured.Unstructured
// AdmissionInfo contains the admission request information
AdmissionInfo kyverno.RequestInfo
// Dynamic client - used by generate
Client *client.Client
// Contexts to store resources
Context context.EvalInterface
// Config handler
ExcludeGroupRole []string
// ResourceCache provides listers to resources
// Currently Supports Configmap
ExcludeResourceFunc func(kind, namespace, name string) bool
// ResourceCache provides listers to resources. Currently Supports Configmap
ResourceCache resourcecache.ResourceCacheIface
// JSONContext ...
// JSONContext is the variable context
JSONContext *context.Context
}

View file

@ -109,7 +109,7 @@ func checkSelector(labelSelector *metav1.LabelSelector, resourceLabels map[strin
// ClusterRoles []string
// Subjects []rbacv1.Subject
// To filter out the targeted resources with ResourceDescription, the check
// should be: AND across attibutes but an OR inside attributes that of type list
// should be: AND across attributes but an OR inside attributes that of type list
// To filter out the targeted resources with UserInfo, the check
// should be: OR (across & inside) attributes
func doesResourceMatchConditionBlock(conditionBlock kyverno.ResourceDescription, userInfo kyverno.UserInfo, admissionInfo kyverno.RequestInfo, resource unstructured.Unstructured, dynamicConfig []string) []error {

View file

@ -117,13 +117,14 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
}
policyContext := engine.PolicyContext{
NewResource: resource,
Policy: *policyObj,
Context: ctx,
AdmissionInfo: gr.Spec.Context.UserRequestInfo,
ExcludeGroupRole: c.Config.GetExcludeGroupRole(),
ResourceCache: c.resCache,
JSONContext: ctx,
NewResource: resource,
Policy: *policyObj,
Context: ctx,
AdmissionInfo: gr.Spec.Context.UserRequestInfo,
ExcludeGroupRole: c.Config.GetExcludeGroupRole(),
ExcludeResourceFunc: c.Config.ToFilter,
ResourceCache: c.resCache,
JSONContext: ctx,
}
// check if the policy still applies to the resource

View file

@ -157,6 +157,9 @@ func runTestCase(t *testing.T, tc scaseT) bool {
Policy: *policy,
Client: client,
ExcludeGroupRole: []string{},
ExcludeResourceFunc: func(s1, s2, s3 string) bool {
return false
},
}
er = engine.Generate(policyContext)

View file

@ -3,6 +3,7 @@ package webhooks
import (
contextdefault "context"
"fmt"
"github.com/go-logr/logr"
"reflect"
"sort"
"strings"
@ -39,44 +40,30 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
}
policyContext := engine.PolicyContext{
NewResource: new,
OldResource: old,
AdmissionInfo: userRequestInfo,
Context: ctx,
ExcludeGroupRole: dynamicConfig.GetExcludeGroupRole(),
ResourceCache: ws.resCache,
JSONContext: ctx,
NewResource: new,
OldResource: old,
AdmissionInfo: userRequestInfo,
Context: ctx,
ExcludeGroupRole: dynamicConfig.GetExcludeGroupRole(),
ExcludeResourceFunc: ws.configHandler.ToFilter,
ResourceCache: ws.resCache,
JSONContext: ctx,
}
// engine.Generate returns a list of rules that are applicable on this resource
var rules []response.RuleResponse
for _, policy := range policies {
policyContext.Policy = *policy
engineResponse := engine.Generate(policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if !rule.Success {
ws.log.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"policyName": engineResponse.PolicyResponse.Policy,
"resourceName": engineResponse.PolicyResponse.Resource.Name,
"resourceKind": engineResponse.PolicyResponse.Resource.Kind,
"ResourceNamespace": engineResponse.PolicyResponse.Resource.Namespace,
}))
grList, err := ws.grLister.List(selector)
if err != nil {
logger.Error(err, "failed to get generate request for the resource", "kind", engineResponse.PolicyResponse.Resource.Kind, "name", engineResponse.PolicyResponse.Resource.Name, "namespace", engineResponse.PolicyResponse.Resource.Namespace)
continue
}
for _, v := range grList {
err := ws.kyvernoClient.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Delete(contextdefault.TODO(), v.GetName(), metav1.DeleteOptions{})
if err != nil {
logger.Error(err, "failed to update gr")
}
}
} else {
rules = append(rules, rule)
ws.deleteGR(logger, engineResponse)
continue
}
rules = append(rules, rule)
}
if len(rules) > 0 {
@ -87,7 +74,6 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
resp: engineResponse,
})
}
}
// Adds Generate Request to a channel(queue size 1000) to generators
@ -102,6 +88,29 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
return
}
func (ws *WebhookServer) deleteGR(logger logr.Logger, engineResponse response.EngineResponse) {
logger.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"policyName": engineResponse.PolicyResponse.Policy,
"resourceName": engineResponse.PolicyResponse.Resource.Name,
"resourceKind": engineResponse.PolicyResponse.Resource.Kind,
"ResourceNamespace": engineResponse.PolicyResponse.Resource.Namespace,
}))
grList, err := ws.grLister.List(selector)
if err != nil {
logger.Error(err, "failed to get generate request for the resource", "kind", engineResponse.PolicyResponse.Resource.Kind, "name", engineResponse.PolicyResponse.Resource.Name, "namespace", engineResponse.PolicyResponse.Resource.Namespace)
}
for _, v := range grList {
err := ws.kyvernoClient.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Delete(contextdefault.TODO(), v.GetName(), metav1.DeleteOptions{})
if err != nil {
logger.Error(err, "failed to update gr")
}
}
}
func applyGenerateRequest(gnGenerator generate.GenerateRequests, userRequestInfo kyverno.RequestInfo,
action v1beta1.Operation, engineResponses ...response.EngineResponse) (failedGenerateRequest []generateRequestResponse) {

View file

@ -38,12 +38,13 @@ func (ws *WebhookServer) HandleMutation(
var patches [][]byte
var engineResponses []response.EngineResponse
policyContext := engine.PolicyContext{
NewResource: resource,
AdmissionInfo: userRequestInfo,
Context: ctx,
ExcludeGroupRole: ws.configHandler.GetExcludeGroupRole(),
ResourceCache: ws.resCache,
JSONContext: ctx,
NewResource: resource,
AdmissionInfo: userRequestInfo,
Context: ctx,
ExcludeGroupRole: ws.configHandler.GetExcludeGroupRole(),
ExcludeResourceFunc: ws.configHandler.ToFilter,
ResourceCache: ws.resCache,
JSONContext: ctx,
}
if request.Operation == v1beta1.Update {

View file

@ -69,13 +69,14 @@ func HandleValidation(
}
policyContext := engine.PolicyContext{
NewResource: newR,
OldResource: oldR,
Context: ctx,
AdmissionInfo: userRequestInfo,
ExcludeGroupRole: dynamicConfig.GetExcludeGroupRole(),
ResourceCache: resCache,
JSONContext: ctx,
NewResource: newR,
OldResource: oldR,
Context: ctx,
AdmissionInfo: userRequestInfo,
ExcludeGroupRole: dynamicConfig.GetExcludeGroupRole(),
ExcludeResourceFunc: dynamicConfig.ToFilter,
ResourceCache: resCache,
JSONContext: ctx,
}
var engineResponses []response.EngineResponse