1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Merge pull request #249 from nirmata/filter_resources

Filter resources
This commit is contained in:
shuting 2019-07-31 18:04:44 -07:00 committed by GitHub
commit 4d684fca51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 102 additions and 22 deletions

View file

@ -227,7 +227,7 @@ spec:
containers:
- name: kyverno
image: nirmata/kyverno:latest
args: ["--filterKind","Node,Event,APIService,Policy,TokenReview,SubjectAccessReview"]
args: ["--filterK8Resources","[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*]Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][Deployment,default,nginx-deployment]"]
ports:
- containerPort: 443
securityContext:

13
main.go
View file

@ -18,9 +18,9 @@ import (
)
var (
kubeconfig string
serverIP string
filterK8Kinds webhooks.ArrayFlags
kubeconfig string
serverIP string
filterK8Resources string
)
func main() {
@ -49,14 +49,15 @@ func main() {
policyInformerFactory,
violationBuilder,
eventController,
annotationsController)
annotationsController,
filterK8Resources)
genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces(), annotationsController)
tlsPair, err := initTLSPemPair(clientConfig, client)
if err != nil {
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
}
server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, violationBuilder, annotationsController, filterK8Kinds)
server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, violationBuilder, annotationsController, filterK8Resources)
if err != nil {
glog.Fatalf("Unable to create webhook server: %v\n", err)
}
@ -93,7 +94,7 @@ func main() {
func init() {
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
flag.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.")
flag.Var(&filterK8Kinds, "filterKind", "k8 kind where policy is not evaluated by the admission webhook. example --filterKind \"Event\" --filterKind \"TokenReview,ClusterRole\"")
flag.StringVar(&filterK8Resources, "filterK8Resources", "", "k8 resource in format [kind,namespace,name] where policy is not evaluated by the admission webhook. example --filterKind \"[Deployment, kyverno, kyverno]\" --filterKind \"[Deployment, kyverno, kyverno],[Events, *, *]\"")
config.LogDefaultFlags()
flag.Parse()
}

View file

@ -6,10 +6,11 @@ import (
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/utils"
"k8s.io/apimachinery/pkg/runtime"
)
func cleanAnnotations(client *client.Client, obj interface{}) {
func cleanAnnotations(client *client.Client, obj interface{}, filterK8Resources []utils.K8Resource) {
// get the policy struct from interface
unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
@ -22,7 +23,7 @@ func cleanAnnotations(client *client.Client, obj interface{}) {
return
}
// Get the resources that apply to the policy
resourceMap := engine.ListResourcesThatApplyToPolicy(client, &policy)
resourceMap := engine.ListResourcesThatApplyToPolicy(client, &policy, filterK8Resources)
// remove annotations for the resources
for _, obj := range resourceMap {
// get annotations

View file

@ -9,6 +9,7 @@ import (
"github.com/nirmata/kyverno/pkg/annotations"
"github.com/nirmata/kyverno/pkg/info"
"github.com/nirmata/kyverno/pkg/utils"
"github.com/nirmata/kyverno/pkg/engine"
@ -36,6 +37,7 @@ type PolicyController struct {
eventController event.Generator
annotationsController annotations.Controller
queue workqueue.RateLimitingInterface
filterK8Resources []utils.K8Resource
}
// NewPolicyController from cmd args
@ -43,7 +45,8 @@ func NewPolicyController(client *client.Client,
policyInformer sharedinformer.PolicyInformer,
violationBuilder violation.Generator,
eventController event.Generator,
annotationsController annotations.Controller) *PolicyController {
annotationsController annotations.Controller,
filterK8Resources string) *PolicyController {
controller := &PolicyController{
client: client,
@ -52,6 +55,7 @@ func NewPolicyController(client *client.Client,
violationBuilder: violationBuilder,
eventController: eventController,
annotationsController: annotationsController,
filterK8Resources: utils.ParseKinds(filterK8Resources),
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName),
}
@ -87,7 +91,7 @@ func (pc *PolicyController) deletePolicyHandler(resource interface{}) {
glog.Error("error decoding object, invalid type")
return
}
cleanAnnotations(pc.client, resource)
cleanAnnotations(pc.client, resource, pc.filterK8Resources)
glog.Infof("policy deleted: %s", object.GetName())
}
@ -187,7 +191,7 @@ func (pc *PolicyController) syncHandler(obj interface{}) error {
glog.Infof("process policy %s on existing resources", policy.GetName())
// Process policy on existing resources
policyInfos := engine.ProcessExisting(pc.client, policy)
policyInfos := engine.ProcessExisting(pc.client, policy, pc.filterK8Resources)
events, violations := pc.createEventsAndViolations(policyInfos)
// Events, Violations

View file

@ -42,7 +42,8 @@ func (f *fixture) runControler(policyName string) {
policyInformerFactory,
violationBuilder,
eventController,
nil)
nil,
"")
stopCh := signals.SetupSignalHandler()
// start informer & controller

View file

@ -6,14 +6,15 @@ import (
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/info"
"github.com/nirmata/kyverno/pkg/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ProcessExisting checks for mutation and validation violations of existing resources
func ProcessExisting(client *client.Client, policy *types.Policy) []*info.PolicyInfo {
func ProcessExisting(client *client.Client, policy *types.Policy, filterK8Resources []utils.K8Resource) []*info.PolicyInfo {
glog.Infof("Applying policy %s on existing resources", policy.Name)
// key uid
resourceMap := ListResourcesThatApplyToPolicy(client, policy)
resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources)
policyInfos := []*info.PolicyInfo{}
// for the filtered resource apply policy
for _, v := range resourceMap {

View file

@ -12,6 +12,7 @@ import (
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/utils"
v1helper "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -20,7 +21,7 @@ import (
)
//ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules
func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy) map[string]resourceInfo {
func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy, filterK8Resources []utils.K8Resource) map[string]resourceInfo {
// key uid
resourceMap := map[string]resourceInfo{}
for _, rule := range policy.Spec.Rules {
@ -45,7 +46,7 @@ func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy)
// If kind is namespace then namespace is "", override
// Get resources in the namespace
for _, ns := range namespaces {
rMap := getResourcesPerNamespace(k, client, ns, rule)
rMap := getResourcesPerNamespace(k, client, ns, rule, filterK8Resources)
mergeresources(resourceMap, rMap)
}
}
@ -53,7 +54,7 @@ func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy)
return resourceMap
}
func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule types.Rule) map[string]resourceInfo {
func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule types.Rule, filterK8Resources []utils.K8Resource) map[string]resourceInfo {
resourceMap := map[string]resourceInfo{}
// List resources
list, err := client.ListResource(kind, namespace, rule.MatchResources.Selector)
@ -104,6 +105,10 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri
ri := resourceInfo{Resource: res, Gvk: &metav1.GroupVersionKind{Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind}}
// Skip the filtered resources
if utils.SkipFilteredResources(gvk.Kind, res.GetNamespace(), res.GetName(), filterK8Resources) {
continue
}
resourceMap[string(res.GetUID())] = ri
}

View file

@ -1,5 +1,14 @@
package utils
import (
"fmt"
"regexp"
"strings"
"github.com/minio/minio/pkg/wildcard"
"k8s.io/api/admission/v1beta1"
)
func Contains(list []string, element string) bool {
for _, e := range list {
if e == element {
@ -8,3 +17,60 @@ func Contains(list []string, element string) bool {
}
return false
}
type K8Resource struct {
Kind string //TODO: as we currently only support one GVK version, we use the kind only. But if we support multiple GVK, then GV need to be added
Namespace string
Name string
}
func SkipFilteredResourcesReq(request *v1beta1.AdmissionRequest, filterK8Resources []K8Resource) bool {
kind := request.Kind.Kind
namespace := request.Namespace
name := request.Name
for _, r := range filterK8Resources {
if wildcard.Match(r.Kind, kind) && wildcard.Match(r.Namespace, namespace) && wildcard.Match(r.Name, name) {
return true
}
}
return false
}
func SkipFilteredResources(kind, namespace, name string, filterK8Resources []K8Resource) bool {
for _, r := range filterK8Resources {
if wildcard.Match(r.Kind, kind) && wildcard.Match(r.Namespace, namespace) && wildcard.Match(r.Name, name) {
return true
}
}
return false
}
//parseKinds parses the kinds if a single string contains comma seperated kinds
// {"1,2,3","4","5"} => {"1","2","3","4","5"}
func ParseKinds(list string) []K8Resource {
resources := []K8Resource{}
var resource K8Resource
re := regexp.MustCompile(`\[([^\[\]]*)\]`)
submatchall := re.FindAllString(list, -1)
for _, element := range submatchall {
element = strings.Trim(element, "[")
element = strings.Trim(element, "]")
elements := strings.Split(element, ",")
//TODO: wildcards for namespace and name
if len(elements) == 0 {
continue
}
if len(elements) == 3 {
resource = K8Resource{Kind: elements[0], Namespace: elements[1], Name: elements[2]}
}
if len(elements) == 2 {
resource = K8Resource{Kind: elements[0], Namespace: elements[1]}
}
if len(elements) == 1 {
resource = K8Resource{Kind: elements[0]}
}
fmt.Println(resource)
resources = append(resources, resource)
}
return resources
}

View file

@ -18,6 +18,7 @@ import (
"github.com/nirmata/kyverno/pkg/event"
"github.com/nirmata/kyverno/pkg/sharedinformer"
tlsutils "github.com/nirmata/kyverno/pkg/tls"
"github.com/nirmata/kyverno/pkg/utils"
"github.com/nirmata/kyverno/pkg/violation"
v1beta1 "k8s.io/api/admission/v1beta1"
)
@ -31,7 +32,7 @@ type WebhookServer struct {
eventController event.Generator
violationBuilder violation.Generator
annotationsController annotations.Controller
filterKinds []string
filterK8Resources []utils.K8Resource
}
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
@ -43,7 +44,7 @@ func NewWebhookServer(
eventController event.Generator,
violationBuilder violation.Generator,
annotationsController annotations.Controller,
filterKinds []string) (*WebhookServer, error) {
filterK8Resources string) (*WebhookServer, error) {
if tlsPair == nil {
return nil, errors.New("NewWebhookServer is not initialized properly")
@ -62,7 +63,7 @@ func NewWebhookServer(
eventController: eventController,
violationBuilder: violationBuilder,
annotationsController: annotationsController,
filterKinds: parseKinds(filterKinds),
filterK8Resources: utils.ParseKinds(filterK8Resources),
}
mux := http.NewServeMux()
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
@ -92,7 +93,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
}
// Do not process the admission requests for kinds that are in filterKinds for filtering
if !StringInSlice(admissionReview.Request.Kind.Kind, ws.filterKinds) {
if !utils.SkipFilteredResourcesReq(admissionReview.Request, ws.filterK8Resources) {
// if the resource is being deleted we need to clear any existing Policy Violations
// TODO: can report to the user that we clear the violation corresponding to this resource
if admissionReview.Request.Operation == v1beta1.Delete {