mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-13 19:28:55 +00:00
Add PolicyEngine
This commit is contained in:
parent
e8e33732cf
commit
7c82ea4284
13 changed files with 626 additions and 13 deletions
|
@ -18,12 +18,12 @@ import (
|
|||
event "github.com/nirmata/kube-policy/pkg/event"
|
||||
eventinterfaces "github.com/nirmata/kube-policy/pkg/event/interfaces"
|
||||
eventutils "github.com/nirmata/kube-policy/pkg/event/utils"
|
||||
"github.com/nirmata/kube-policy/pkg/policyengine"
|
||||
violation "github.com/nirmata/kube-policy/pkg/violation"
|
||||
violationinterfaces "github.com/nirmata/kube-policy/pkg/violation/interfaces"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
mergetypes "k8s.io/apimachinery/pkg/types"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
@ -43,6 +43,9 @@ type policyController struct {
|
|||
logger *log.Logger
|
||||
violationBuilder violationinterfaces.ViolationGenerator
|
||||
eventBuilder eventinterfaces.BuilderInternal
|
||||
|
||||
policyEngine policyengine.PolicyEngine
|
||||
kubeClient *kubeClient.KubeClient
|
||||
}
|
||||
|
||||
// NewPolicyController from cmd args
|
||||
|
@ -71,6 +74,12 @@ func NewPolicyController(config *rest.Config, logger *log.Logger, kubeClient *ku
|
|||
|
||||
// generate Violation builer
|
||||
violationBuilder, err := violation.NewViolationBuilder(kubeClient, eventBuilder, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// generate Policy Engine
|
||||
policyEngine := policyengine.NewPolicyEngine(kubeClient, logger)
|
||||
|
||||
controller := &policyController{
|
||||
policyInformerFactory: policyInformerFactory,
|
||||
|
@ -79,15 +88,20 @@ func NewPolicyController(config *rest.Config, logger *log.Logger, kubeClient *ku
|
|||
logger: logger,
|
||||
violationBuilder: violationBuilder,
|
||||
eventBuilder: eventBuilder,
|
||||
policyEngine: policyEngine,
|
||||
kubeClient: kubeClient,
|
||||
}
|
||||
|
||||
policyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: controller.CreatePolicyHandler,
|
||||
UpdateFunc: controller.UpdatePolicyHandler,
|
||||
DeleteFunc: controller.DeletePolicyHandler,
|
||||
})
|
||||
|
||||
// Set the controller
|
||||
eventBuilder.SetController(controller)
|
||||
violationBuilder.SetController(controller)
|
||||
|
||||
return controller, nil
|
||||
}
|
||||
|
||||
|
@ -165,6 +179,7 @@ func (c *policyController) addPolicyLog(name, text string) {
|
|||
func (c *policyController) CreatePolicyHandler(resource interface{}) {
|
||||
key := c.GetResourceKey(resource)
|
||||
c.logger.Printf("Policy created: %s", key)
|
||||
// c.runForPolicy(key)
|
||||
}
|
||||
|
||||
func (c *policyController) UpdatePolicyHandler(oldResource, newResource interface{}) {
|
||||
|
@ -186,11 +201,11 @@ func (c *policyController) GetResourceKey(resource interface{}) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *policyController) GetPolicy(name string) (*types.Policy, error) {
|
||||
policyNamespace, policyName, err := cache.SplitMetaNamespaceKey(name)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", name))
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error when SplitMetaNamespaceKey: %s, err: %v", name, err)
|
||||
}
|
||||
return c.getPolicyInterface(policyNamespace).Get(policyName)
|
||||
}
|
||||
|
|
120
controller/processPolicy.go
Normal file
120
controller/processPolicy.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
eventutils "github.com/nirmata/kube-policy/pkg/event/utils"
|
||||
"github.com/nirmata/kube-policy/pkg/policyengine/mutation"
|
||||
violationutils "github.com/nirmata/kube-policy/pkg/violation/utils"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
)
|
||||
|
||||
func (c *policyController) runForPolicy(key string) {
|
||||
//policy, err := c.GetPolicy(key)
|
||||
policy, err := c.getPolicyByKey(key)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("invalid resource key: %s, err: %v", key, err))
|
||||
return
|
||||
}
|
||||
|
||||
if policy == nil {
|
||||
c.logger.Printf("Counld not find policy by key %s", key)
|
||||
return
|
||||
}
|
||||
|
||||
violations, events, err := c.processPolicy(*policy)
|
||||
if err != nil {
|
||||
// add Error processing policy event
|
||||
}
|
||||
|
||||
c.logger.Printf("%v, %v", violations, events)
|
||||
// TODO:
|
||||
// create violations
|
||||
// create events
|
||||
|
||||
}
|
||||
|
||||
// processPolicy process the policy to all the matched resources
|
||||
func (c *policyController) processPolicy(policy types.Policy) (
|
||||
violations []violationutils.ViolationInfo, events []eventutils.EventInfo, err error) {
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
resources, err := c.filterResourceByRule(rule)
|
||||
if err != nil {
|
||||
c.logger.Printf("Failed to filter resources by rule %s, err: %v\n", rule.Name, err)
|
||||
}
|
||||
|
||||
for _, resource := range resources {
|
||||
rawResource, err := json.Marshal(resource)
|
||||
if err != nil {
|
||||
c.logger.Printf("Failed to marshal resources map to rule %s, err: %v\n", rule.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
violation, eventInfos, err := c.policyEngine.ProcessExisting(policy, rawResource)
|
||||
if err != nil {
|
||||
c.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
violations = append(violations, violation...)
|
||||
events = append(events, eventInfos...)
|
||||
}
|
||||
}
|
||||
return violations, events, nil
|
||||
}
|
||||
|
||||
func (c *policyController) filterResourceByRule(rule types.PolicyRule) ([]runtime.Object, error) {
|
||||
var targetResources []runtime.Object
|
||||
// TODO: make this namespace all
|
||||
var namespace = "default"
|
||||
if err := rule.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid rule detected: %s, err: %v", rule.Name, err)
|
||||
}
|
||||
|
||||
// Get the resource list from kind
|
||||
resources, err := c.kubeClient.ListResource(rule.Resource.Kind, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, resource := range resources {
|
||||
// TODO:
|
||||
rawResource, err := json.Marshal(resource)
|
||||
// objKind := resource.GetObjectKind()
|
||||
// codecFactory := serializer.NewCodecFactory(runtime.NewScheme())
|
||||
// codecFactory.EncoderForVersion()
|
||||
|
||||
if err != nil {
|
||||
c.logger.Printf("failed to marshal object %v", resource)
|
||||
continue
|
||||
}
|
||||
|
||||
// filter the resource by name and label
|
||||
if ok, _ := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); ok {
|
||||
targetResources = append(targetResources, resource)
|
||||
}
|
||||
}
|
||||
return targetResources, nil
|
||||
}
|
||||
|
||||
func (c *policyController) getPolicyByKey(key string) (*types.Policy, error) {
|
||||
// Create nil Selector to grab all the policies
|
||||
selector := labels.NewSelector()
|
||||
cachedPolicies, err := c.policyLister.List(selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, elem := range cachedPolicies {
|
||||
if elem.Name == key {
|
||||
return elem, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -206,7 +206,34 @@ var rMapper = map[string]getter{
|
|||
"StatefulSet": statefulSetGetter,
|
||||
}
|
||||
|
||||
var lMapper = map[string]lister{
|
||||
"ConfigMap": configMapLister,
|
||||
"Pods": podLister,
|
||||
"Deployment": deploymentLister,
|
||||
"CronJob": cronJobLister,
|
||||
"Endpoints": endpointsLister,
|
||||
"HorizontalPodAutoscaler": horizontalPodAutoscalerLister,
|
||||
"Ingress": ingressLister,
|
||||
"Job": jobLister,
|
||||
"LimitRange": limitRangeLister,
|
||||
"Namespace": namespaceLister,
|
||||
"NetworkPolicy": networkPolicyLister,
|
||||
"PersistentVolumeClaim": persistentVolumeClaimLister,
|
||||
"PodDisruptionBudget": podDisruptionBudgetLister,
|
||||
"PodTemplate": podTemplateLister,
|
||||
"ResourceQuota": resourceQuotaLister,
|
||||
"Secret": secretLister,
|
||||
"Service": serviceLister,
|
||||
"StatefulSet": statefulSetLister,
|
||||
}
|
||||
|
||||
type getter func(*kubernetes.Clientset, string, string) (runtime.Object, error)
|
||||
type lister func(*kubernetes.Clientset, string) ([]runtime.Object, error)
|
||||
|
||||
//ListResource to return resource list
|
||||
func (kc *KubeClient) ListResource(kind string, namespace string) ([]runtime.Object, error) {
|
||||
return lMapper[kind](kc.client, namespace)
|
||||
}
|
||||
|
||||
//GetResource get the resource object
|
||||
func (kc *KubeClient) GetResource(kind string, resource string) (runtime.Object, error) {
|
||||
|
@ -233,6 +260,19 @@ func configMapGetter(clientSet *kubernetes.Clientset, namespace string, name str
|
|||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func configMapLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().ConfigMaps(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func podsGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -241,6 +281,18 @@ func podsGetter(clientSet *kubernetes.Clientset, namespace string, name string)
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func podLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().Pods(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func deploymentGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.AppsV1().Deployments(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -248,6 +300,17 @@ func deploymentGetter(clientSet *kubernetes.Clientset, namespace string, name st
|
|||
}
|
||||
return obj, nil
|
||||
}
|
||||
func deploymentLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.AppsV1().Deployments(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func cronJobGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.BatchV1beta1().CronJobs(namespace).Get(name, metav1.GetOptions{})
|
||||
|
@ -257,6 +320,18 @@ func cronJobGetter(clientSet *kubernetes.Clientset, namespace string, name strin
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func cronJobLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.BatchV1beta1().CronJobs(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func endpointsbGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().Endpoints(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -265,6 +340,18 @@ func endpointsbGetter(clientSet *kubernetes.Clientset, namespace string, name st
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func endpointsLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().Endpoints(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func horizontalPodAutoscalerGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -273,6 +360,18 @@ func horizontalPodAutoscalerGetter(clientSet *kubernetes.Clientset, namespace st
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func horizontalPodAutoscalerLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.AutoscalingV1().HorizontalPodAutoscalers(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func ingressGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.ExtensionsV1beta1().Ingresses(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -281,6 +380,18 @@ func ingressGetter(clientSet *kubernetes.Clientset, namespace string, name strin
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func ingressLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.ExtensionsV1beta1().Ingresses(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func jobGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.BatchV1().Jobs(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -289,6 +400,18 @@ func jobGetter(clientSet *kubernetes.Clientset, namespace string, name string) (
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func jobLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.BatchV1().Jobs(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func limitRangeGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().LimitRanges(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -296,6 +419,17 @@ func limitRangeGetter(clientSet *kubernetes.Clientset, namespace string, name st
|
|||
}
|
||||
return obj, nil
|
||||
}
|
||||
func limitRangeLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().LimitRanges(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func namespaceGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().Namespaces().Get(name, metav1.GetOptions{})
|
||||
|
@ -305,6 +439,18 @@ func namespaceGetter(clientSet *kubernetes.Clientset, namespace string, name str
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func namespaceLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().Namespaces().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func networkPolicyGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.NetworkingV1().NetworkPolicies(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -313,6 +459,18 @@ func networkPolicyGetter(clientSet *kubernetes.Clientset, namespace string, name
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func networkPolicyLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.NetworkingV1().NetworkPolicies(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func persistentVolumeClaimGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().PersistentVolumeClaims(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -321,6 +479,18 @@ func persistentVolumeClaimGetter(clientSet *kubernetes.Clientset, namespace stri
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func persistentVolumeClaimLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().PersistentVolumeClaims(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func podDisruptionBudgetGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -329,6 +499,18 @@ func podDisruptionBudgetGetter(clientSet *kubernetes.Clientset, namespace string
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func podDisruptionBudgetLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func podTemplateGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().PodTemplates(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -337,6 +519,18 @@ func podTemplateGetter(clientSet *kubernetes.Clientset, namespace string, name s
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func podTemplateLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().PodTemplates(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func resourceQuotaGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().ResourceQuotas(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -345,6 +539,18 @@ func resourceQuotaGetter(clientSet *kubernetes.Clientset, namespace string, name
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func resourceQuotaLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().ResourceQuotas(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func secretGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -353,6 +559,18 @@ func secretGetter(clientSet *kubernetes.Clientset, namespace string, name string
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func secretLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().Secrets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func serviceGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -361,6 +579,18 @@ func serviceGetter(clientSet *kubernetes.Clientset, namespace string, name strin
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
func serviceLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.CoreV1().Services(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
||||
func statefulSetGetter(clientSet *kubernetes.Clientset, namespace string, name string) (runtime.Object, error) {
|
||||
obj, err := clientSet.AppsV1().StatefulSets(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
@ -368,3 +598,15 @@ func statefulSetGetter(clientSet *kubernetes.Clientset, namespace string, name s
|
|||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func statefulSetLister(clientSet *kubernetes.Clientset, namespace string) ([]runtime.Object, error) {
|
||||
list, err := clientSet.AppsV1().StatefulSets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objList := []runtime.Object{}
|
||||
for _, obj := range list.Items {
|
||||
objList = append(objList, &obj)
|
||||
}
|
||||
return objList, nil
|
||||
}
|
||||
|
|
96
pkg/policyengine/mutation.go
Normal file
96
pkg/policyengine/mutation.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package policyengine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
"github.com/nirmata/kube-policy/pkg/policyengine/mutation"
|
||||
)
|
||||
|
||||
func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error) {
|
||||
patchingSets := mutation.GetPolicyPatchingSets(policy)
|
||||
var policyPatches []mutation.PatchBytes
|
||||
|
||||
for ruleIdx, rule := range policy.Spec.Rules {
|
||||
err := rule.Validate()
|
||||
if err != nil {
|
||||
p.logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); !ok {
|
||||
p.logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = p.applyRuleGenerators(rawResource, rule)
|
||||
if err != nil && patchingSets == mutation.PatchingSetsStopOnError {
|
||||
return nil, fmt.Errorf("Failed to apply generators from rule #%s: %v", rule.Name, err)
|
||||
}
|
||||
|
||||
rulePatchesProcessed, err := mutation.ProcessPatches(rule.Patches, rawResource, patchingSets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to process patches from rule #%s: %v", rule.Name, err)
|
||||
}
|
||||
|
||||
if rulePatchesProcessed != nil {
|
||||
policyPatches = append(policyPatches, rulePatchesProcessed...)
|
||||
p.logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed))
|
||||
// TODO: add PolicyApplied events per rule for policy and resource
|
||||
} else {
|
||||
p.logger.Printf("Rule %d: no patches prepared", ruleIdx)
|
||||
}
|
||||
}
|
||||
|
||||
// empty patch, return error to deny resource creation
|
||||
if policyPatches == nil {
|
||||
return nil, fmt.Errorf("no patches prepared")
|
||||
}
|
||||
|
||||
return policyPatches, nil
|
||||
}
|
||||
|
||||
// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule
|
||||
func (p *policyEngine) applyRuleGenerators(rawResource []byte, rule types.PolicyRule) error {
|
||||
kind := mutation.ParseKindFromObject(rawResource)
|
||||
|
||||
// configMapGenerator and secretGenerator can be applied only to namespaces
|
||||
if kind == "Namespace" {
|
||||
namespaceName := mutation.ParseNameFromObject(rawResource)
|
||||
|
||||
err := p.applyConfigGenerator(rule.ConfigMapGenerator, namespaceName, "ConfigMap")
|
||||
if err == nil {
|
||||
err = p.applyConfigGenerator(rule.SecretGenerator, namespaceName, "Secret")
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request.
|
||||
func (p *policyEngine) applyConfigGenerator(generator *types.PolicyConfigGenerator, namespace string, configKind string) error {
|
||||
if generator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := generator.Validate()
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Generator for '%s' is invalid: %s", configKind, err))
|
||||
}
|
||||
|
||||
switch configKind {
|
||||
case "ConfigMap":
|
||||
err = p.kubeClient.GenerateConfigMap(*generator, namespace)
|
||||
case "Secret":
|
||||
err = p.kubeClient.GenerateSecret(*generator, namespace)
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf("Unsupported config Kind '%s'", configKind))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Unable to apply generator for %s '%s/%s' : %s", configKind, namespace, generator.Name, err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -8,10 +8,11 @@ import (
|
|||
|
||||
// kind is the type of object being manipulated
|
||||
// Checks requests kind, name and labels to fit the policy
|
||||
func IsRuleApplicableToResource(kind string, resourceRaw []byte, policyResource types.PolicyResource) (bool, error) {
|
||||
if policyResource.Kind != kind {
|
||||
return false, nil
|
||||
}
|
||||
func IsRuleApplicableToResource(resourceRaw []byte, policyResource types.PolicyResource) (bool, error) {
|
||||
// kind := ParseKindFromObject(resourceRaw)
|
||||
// if policyResource.Kind != kind {
|
||||
// return false, nil
|
||||
// }
|
||||
|
||||
if resourceRaw != nil {
|
||||
meta := ParseMetadataFromObject(resourceRaw)
|
129
pkg/policyengine/policyengine.go
Normal file
129
pkg/policyengine/policyengine.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
package policyengine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
kubeClient "github.com/nirmata/kube-policy/kubeclient"
|
||||
policytype "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
eventutils "github.com/nirmata/kube-policy/pkg/event/utils"
|
||||
"github.com/nirmata/kube-policy/pkg/policyengine/mutation"
|
||||
violationutils "github.com/nirmata/kube-policy/pkg/violation/utils"
|
||||
)
|
||||
|
||||
type PolicyEngine interface {
|
||||
// ProcessMutation should be called from admission contoller
|
||||
// when there is an creation / update of the resource
|
||||
// ProcessMutation(policy types.Policy, rawResource []byte) (patchBytes []byte, events []Events, err error)
|
||||
ProcessMutation(policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error)
|
||||
|
||||
// ProcessValidation should be called from admission contoller
|
||||
// when there is an creation / update of the resource
|
||||
ProcessValidation(policy types.Policy, rawResource []byte)
|
||||
|
||||
// ProcessExisting should be called from policy controller
|
||||
// when there is an create / update of the policy
|
||||
// we should process the policy on matched resources, generate violations accordingly
|
||||
ProcessExisting(policy types.Policy, rawResource []byte) ([]violationutils.ViolationInfo, []eventutils.EventInfo, error)
|
||||
}
|
||||
|
||||
type policyEngine struct {
|
||||
kubeClient *kubeClient.KubeClient
|
||||
// controller controllerinterfaces.PolicyGetter
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewPolicyEngine(kubeClient *kubeClient.KubeClient, logger *log.Logger) PolicyEngine {
|
||||
return &policyEngine{
|
||||
kubeClient: kubeClient,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *policyEngine) ProcessExisting(policy types.Policy, rawResource []byte) ([]violationutils.ViolationInfo, []eventutils.EventInfo, error) {
|
||||
var violations []violationutils.ViolationInfo
|
||||
var events []eventutils.EventInfo
|
||||
|
||||
patchingSets := mutation.GetPolicyPatchingSets(policy)
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
err := rule.Validate()
|
||||
if err != nil {
|
||||
p.logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); !ok {
|
||||
p.logger.Printf("Rule %s of policy %s is not applicable to the request", rule.Name, policy.Name)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
violation, eventInfos, err := p.processRuleOnResource(policy.Name, rule, rawResource, patchingSets)
|
||||
if err != nil {
|
||||
p.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err)
|
||||
continue
|
||||
}
|
||||
// } else {
|
||||
// policyPatches = append(policyPatches, processedPatches...)
|
||||
// }
|
||||
violations = append(violations, violation)
|
||||
events = append(events, eventInfos...)
|
||||
}
|
||||
return violations, events, nil
|
||||
}
|
||||
|
||||
func (p *policyEngine) processRuleOnResource(policyName string, rule types.PolicyRule, rawResource []byte, patchingSets mutation.PatchingSets) (
|
||||
violationutils.ViolationInfo, []eventutils.EventInfo, error) {
|
||||
|
||||
var violationInfo violationutils.ViolationInfo
|
||||
var eventInfos []eventutils.EventInfo
|
||||
|
||||
resourceKind := mutation.ParseKindFromObject(rawResource)
|
||||
resourceName := mutation.ParseNameFromObject(rawResource)
|
||||
resourceNamespace := mutation.ParseNamespaceFromObject(rawResource)
|
||||
|
||||
rulePatchesProcessed, err := mutation.ProcessPatches(rule.Patches, nil, patchingSets)
|
||||
if err != nil {
|
||||
return violationInfo, eventInfos, fmt.Errorf("Failed to process patches from rule %s: %v", rule.Name, err)
|
||||
}
|
||||
|
||||
if rulePatchesProcessed != nil {
|
||||
log.Printf("Rule %s: prepared %d patches", rule.Name, len(rulePatchesProcessed))
|
||||
|
||||
// add a violation to queue
|
||||
violationInfo = violationutils.ViolationInfo{
|
||||
Policy: policyName,
|
||||
Violation: policytype.Violation{
|
||||
Kind: resourceKind,
|
||||
Resource: resourceNamespace + "/" + resourceName,
|
||||
Rule: rule.Name,
|
||||
// TODO:
|
||||
Reason: "testing violation reason",
|
||||
Message: "testing violation message",
|
||||
},
|
||||
}
|
||||
|
||||
// add an event to policy
|
||||
eventInfos = append(eventInfos, eventutils.EventInfo{
|
||||
Kind: "Policy",
|
||||
Resource: policyName,
|
||||
Rule: rule.Name,
|
||||
// TODO:
|
||||
Reason: "PolicyViolation",
|
||||
Message: "testing event message for policy",
|
||||
})
|
||||
|
||||
// add an event to resource
|
||||
eventInfos = append(eventInfos, eventutils.EventInfo{
|
||||
Kind: resourceKind,
|
||||
Resource: resourceNamespace + "/" + resourceName,
|
||||
Rule: rule.Name,
|
||||
// TODO:
|
||||
Reason: "PolicyViolation",
|
||||
Message: "testing event message for policy",
|
||||
})
|
||||
}
|
||||
|
||||
return violationInfo, eventInfos, nil
|
||||
}
|
5
pkg/policyengine/validation.go
Normal file
5
pkg/policyengine/validation.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package policyengine
|
||||
|
||||
import types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
|
||||
func (p *policyEngine) ProcessValidation(policy types.Policy, rawResource []byte) {}
|
|
@ -3,7 +3,7 @@ package webhooks
|
|||
import (
|
||||
kubeclient "github.com/nirmata/kube-policy/kubeclient"
|
||||
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
mutation "github.com/nirmata/kube-policy/pkg/mutation"
|
||||
mutation "github.com/nirmata/kube-policy/pkg/policyengine/mutation"
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
)
|
||||
|
||||
|
@ -24,5 +24,5 @@ func AdmissionIsRequired(request *v1beta1.AdmissionRequest) bool {
|
|||
|
||||
// Checks requests kind, name and labels to fit the policy
|
||||
func IsRuleApplicableToRequest(policyResource types.PolicyResource, request *v1beta1.AdmissionRequest) (bool, error) {
|
||||
return mutation.IsRuleApplicableToResource(request.Kind.Kind, request.Object.Raw, policyResource)
|
||||
return mutation.IsRuleApplicableToResource(request.Object.Raw, policyResource)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ import (
|
|||
controllerinterfaces "github.com/nirmata/kube-policy/controller/interfaces"
|
||||
kubeclient "github.com/nirmata/kube-policy/kubeclient"
|
||||
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
|
||||
mutation "github.com/nirmata/kube-policy/pkg/mutation"
|
||||
policyengine "github.com/nirmata/kube-policy/pkg/policyengine"
|
||||
mutation "github.com/nirmata/kube-policy/pkg/policyengine/mutation"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
|
@ -20,6 +21,7 @@ import (
|
|||
// business logic for resource mutation
|
||||
type MutationWebhook struct {
|
||||
kubeclient *kubeclient.KubeClient
|
||||
policyEngine policyengine.PolicyEngine
|
||||
controller controllerinterfaces.PolicyGetter
|
||||
registration *MutationWebhookRegistration
|
||||
logger *log.Logger
|
||||
|
@ -44,8 +46,11 @@ func CreateMutationWebhook(clientConfig *rest.Config, kubeclient *kubeclient.Kub
|
|||
if logger == nil {
|
||||
logger = log.New(os.Stdout, "Mutation WebHook: ", log.LstdFlags|log.Lshortfile)
|
||||
}
|
||||
policyengine := policyengine.NewPolicyEngine(kubeclient, logger)
|
||||
|
||||
return &MutationWebhook{
|
||||
kubeclient: kubeclient,
|
||||
policyEngine: policyengine,
|
||||
controller: controller,
|
||||
registration: registration,
|
||||
logger: logger,
|
||||
|
@ -101,10 +106,10 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad
|
|||
// May return nil patches if it is not necessary to create patches for requested object.
|
||||
// Returns error ONLY in case when creation of resource should be denied.
|
||||
func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]mutation.PatchBytes, error) {
|
||||
return mw.applyPolicyRulesOnResource(request.Kind.Kind, request.Object.Raw, policy)
|
||||
return mw.policyEngine.ProcessMutation(policy, request.Object.Raw)
|
||||
}
|
||||
|
||||
// kind is the type of object being manipulated
|
||||
// kind is the type of object being manipulated, e.g. request.Kind.kind
|
||||
func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource []byte, policy types.Policy) ([]mutation.PatchBytes, error) {
|
||||
patchingSets := mutation.GetPolicyPatchingSets(policy)
|
||||
var policyPatches []mutation.PatchBytes
|
||||
|
@ -116,7 +121,7 @@ func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource [
|
|||
continue
|
||||
}
|
||||
|
||||
if ok, err := mutation.IsRuleApplicableToResource(kind, rawResource, rule.Resource); !ok {
|
||||
if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); !ok {
|
||||
mw.logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name)
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue