1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-13 19:28:55 +00:00

Add PolicyEngine

This commit is contained in:
shuting 2019-05-09 22:26:22 -07:00
parent e8e33732cf
commit 7c82ea4284
13 changed files with 626 additions and 13 deletions

View file

@ -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
View 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
}

View file

@ -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
}

View 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
}

View file

@ -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)

View 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
}

View 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) {}

View file

@ -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)
}

View file

@ -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
}