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:
parent
df7006f768
commit
c77944ddef
9 changed files with 147 additions and 124 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue