1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

inital commit: redesign the policyengine to make it stateless

This commit is contained in:
shivdudhani 2019-05-10 22:34:14 -07:00
parent 6d83aa6b9e
commit e4366c55be
8 changed files with 129 additions and 178 deletions

View file

@ -11,7 +11,6 @@ import (
policyclientset "github.com/nirmata/kube-policy/pkg/client/clientset/versioned" policyclientset "github.com/nirmata/kube-policy/pkg/client/clientset/versioned"
informers "github.com/nirmata/kube-policy/pkg/client/informers/externalversions" informers "github.com/nirmata/kube-policy/pkg/client/informers/externalversions"
policyengine "github.com/nirmata/kube-policy/pkg/policyengine"
policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation"
event "github.com/nirmata/kube-policy/pkg/event" event "github.com/nirmata/kube-policy/pkg/event"
@ -46,11 +45,9 @@ func main() {
eventController := event.NewEventController(kubeclient, policyInformer.Lister(), nil) eventController := event.NewEventController(kubeclient, policyInformer.Lister(), nil)
violationBuilder := policyviolation.NewPolicyViolationBuilder(kubeclient, policyInformer.Lister(), policyClientset, eventController, nil) violationBuilder := policyviolation.NewPolicyViolationBuilder(kubeclient, policyInformer.Lister(), policyClientset, eventController, nil)
policyEngine := policyengine.NewPolicyEngine(kubeclient, nil)
policyController := policycontroller.NewPolicyController(policyClientset, policyController := policycontroller.NewPolicyController(policyClientset,
policyInformer, policyInformer,
policyEngine,
violationBuilder, violationBuilder,
eventController, eventController,
nil, nil,
@ -59,7 +56,6 @@ func main() {
mutationWebhook, err := webhooks.CreateMutationWebhook(clientConfig, mutationWebhook, err := webhooks.CreateMutationWebhook(clientConfig,
kubeclient, kubeclient,
policyInformer.Lister(), policyInformer.Lister(),
violationBuilder,
eventController, eventController,
nil) nil)
if err != nil { if err != nil {

View file

@ -0,0 +1,85 @@
package policyengine
import (
"fmt"
"log"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kube-policy/pkg/policyengine/mutation"
)
// Generate should be called to process generate rules on the resource
func Generate(logger *log.Logger, policy types.Policy, rawResource []byte) ([]GenerateReturnData, error) {
patchingSets := mutation.GetPolicyPatchingSets(policy)
generatedList := []GenerateReturnData{}
for ruleIdx, rule := range policy.Spec.Rules {
err := rule.Validate()
if err != nil {
logger.Printf("Invalid rule detected: #%d in policy %s, err: %v\n", ruleIdx, policy.ObjectMeta.Name, err)
continue
}
if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); !ok {
logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name)
return nil, err
}
resourceKind := mutation.ParseKindFromObject(rawResource)
// configMapGenerator and secretGenerator can be applied only to namespaces
if resourceKind == "Namespace" {
generatedData, err := applyRuleGenerators(rawResource, rule)
if err != nil && patchingSets == mutation.PatchingSetsStopOnError {
return nil, fmt.Errorf("Failed to apply generators from rule #%d: %s", ruleIdx, err)
}
generatedList = append(generatedList, generatedData...)
}
}
return generatedList, nil
}
// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule
func applyRuleGenerators(rawResource []byte, rule types.PolicyRule) ([]GenerateReturnData, error) {
returnData := []GenerateReturnData{}
namespaceName := mutation.ParseNameFromObject(rawResource)
var generator *types.PolicyConfigGenerator
// Apply config map generator rule
generator, err := applyConfigGenerator(rule.ConfigMapGenerator, namespaceName, "ConfigMap")
if err != nil {
return returnData, err
}
returnData = append(returnData, GenerateReturnData{namespaceName, "ConfigMap", *generator})
// Apply secrets generator rule
generator, err = applyConfigGenerator(rule.SecretGenerator, namespaceName, "Secret")
if err != nil {
return returnData, err
}
returnData = append(returnData, GenerateReturnData{namespaceName, "Secret", *generator})
return returnData, nil
}
// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request.
func applyConfigGenerator(generator *types.PolicyConfigGenerator, namespace string, configKind string) (*types.PolicyConfigGenerator, error) {
if generator == nil {
return nil, nil
}
err := generator.Validate()
if err != nil {
return nil, fmt.Errorf("Generator for '%s' is invalid: %s", configKind, err)
}
switch configKind {
case "ConfigMap":
return generator, nil
// err = kubeClient.GenerateConfigMap(*generator, namespace)
case "Secret":
return generator, nil
default:
return nil, fmt.Errorf("Unsupported config Kind '%s'", configKind)
}
}
//GenerateReturnData holds the generator details
type GenerateReturnData struct {
namespace string
configKind string
generator types.PolicyConfigGenerator
}

View file

@ -1,30 +1,31 @@
package policyengine package policyengine
import ( import (
"errors"
"fmt" "fmt"
"log"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kube-policy/pkg/policyengine/mutation" "github.com/nirmata/kube-policy/pkg/policyengine/mutation"
) )
func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error) { // Mutation should be claed to process the mutation rules on the resource
//TODO return []event.Info
func Mutation(logger *log.Logger, policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error) {
patchingSets := mutation.GetPolicyPatchingSets(policy) patchingSets := mutation.GetPolicyPatchingSets(policy)
var policyPatches []mutation.PatchBytes var policyPatches []mutation.PatchBytes
for ruleIdx, rule := range policy.Spec.Rules { for ruleIdx, rule := range policy.Spec.Rules {
err := rule.Validate() err := rule.Validate()
if err != nil { if err != nil {
p.logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err) logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err)
continue continue
} }
if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); !ok { 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) logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name)
return nil, err return nil, err
} }
err = p.applyRuleGenerators(rawResource, rule)
if err != nil && patchingSets == mutation.PatchingSetsStopOnError { if err != nil && patchingSets == mutation.PatchingSetsStopOnError {
return nil, fmt.Errorf("Failed to apply generators from rule #%s: %v", rule.Name, err) return nil, fmt.Errorf("Failed to apply generators from rule #%s: %v", rule.Name, err)
} }
@ -36,10 +37,10 @@ func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte)
if rulePatchesProcessed != nil { if rulePatchesProcessed != nil {
policyPatches = append(policyPatches, rulePatchesProcessed...) policyPatches = append(policyPatches, rulePatchesProcessed...)
p.logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed)) logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed))
// TODO: add PolicyApplied events per rule for policy and resource // TODO: add PolicyApplied events per rule for policy and resource
} else { } else {
p.logger.Printf("Rule %d: no patches prepared", ruleIdx) logger.Printf("Rule %d: no patches prepared", ruleIdx)
} }
} }
@ -50,47 +51,3 @@ func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte)
return policyPatches, nil 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

@ -1,103 +1,17 @@
package policyengine package policyengine
import ( import (
"fmt"
"log"
kubeClient "github.com/nirmata/kube-policy/kubeclient"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
event "github.com/nirmata/kube-policy/pkg/event" "github.com/nirmata/kube-policy/pkg/event"
"github.com/nirmata/kube-policy/pkg/policyengine/mutation" "github.com/nirmata/kube-policy/pkg/policyviolation"
policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation"
) )
type PolicyEngine interface { // As the logic to process the policies in stateless, we do not need to define struct and implement behaviors for it
// ProcessMutation should be called from admission contoller // Instead we expose them as standalone functions passing the logger and the required atrributes
// when there is an creation / update of the resource // The each function returns the changes that need to be applied on the resource
// ProcessMutation(policy types.Policy, rawResource []byte) (patchBytes []byte, events []Events, err error) // the caller is responsible to apply the changes to the resource
ProcessMutation(policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error)
// ProcessValidation should be called from admission contoller //TODO wrap the generate, mutation & validation functions for the existing resources
// when there is an creation / update of the resource func ProcessExisting(policy types.Policy, rawResource []byte) ([]policyviolation.Info, []event.Info, error) {
ProcessValidation(policy types.Policy, rawResource []byte) return nil, nil, nil
// 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) ([]policyviolation.Info, []event.Info, error)
}
type policyEngine struct {
kubeClient *kubeClient.KubeClient
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) ([]policyviolation.Info, []event.Info, error) {
var violations []policyviolation.Info
var events []event.Info
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) (
policyviolation.Info, []event.Info, error) {
var violationInfo policyviolation.Info
var eventInfos []event.Info
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))
violationInfo = policyviolation.NewViolation(policyName, resourceKind, resourceNamespace+"/"+resourceName, rule.Name)
// add a violation to queue
// add an event to policy
//TODO: event msg
eventInfos = append(eventInfos, event.NewEvent("Policy", policyName, event.PolicyViolation, event.FResourcePolcy))
// add an event to resource
eventInfos = append(eventInfos, event.NewEvent(resourceKind, resourceNamespace+"/"+resourceName, event.PolicyViolation, event.FResourcePolcy))
}
return violationInfo, eventInfos, nil
} }

View file

@ -1,5 +1,13 @@
package policyengine package policyengine
import types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" import (
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kube-policy/pkg/event"
"github.com/nirmata/kube-policy/pkg/policyviolation"
)
func (p *policyEngine) ProcessValidation(policy types.Policy, rawResource []byte) {} // Validation should be called to process validation rules on the resource
//TODO: validate should return a bool to specify if the validation was succesful or not
func Validation(policy types.Policy, rawResource []byte) (bool, []policyviolation.Info, []event.Info) {
return true, nil, nil
}

View file

@ -11,7 +11,7 @@ import (
infomertypes "github.com/nirmata/kube-policy/pkg/client/informers/externalversions/policy/v1alpha1" infomertypes "github.com/nirmata/kube-policy/pkg/client/informers/externalversions/policy/v1alpha1"
lister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" lister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1"
event "github.com/nirmata/kube-policy/pkg/event" event "github.com/nirmata/kube-policy/pkg/event"
policyengine "github.com/nirmata/kube-policy/pkg/policyengine"
policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -27,7 +27,6 @@ type PolicyController struct {
policyLister lister.PolicyLister policyLister lister.PolicyLister
policyInterface policyclientset.Interface policyInterface policyclientset.Interface
policySynced cache.InformerSynced policySynced cache.InformerSynced
policyEngine policyengine.PolicyEngine
violationBuilder policyviolation.Generator violationBuilder policyviolation.Generator
eventBuilder event.Generator eventBuilder event.Generator
logger *log.Logger logger *log.Logger
@ -37,7 +36,6 @@ type PolicyController struct {
// NewPolicyController from cmd args // NewPolicyController from cmd args
func NewPolicyController(policyInterface policyclientset.Interface, func NewPolicyController(policyInterface policyclientset.Interface,
policyInformer infomertypes.PolicyInformer, policyInformer infomertypes.PolicyInformer,
policyEngine policyengine.PolicyEngine,
violationBuilder policyviolation.Generator, violationBuilder policyviolation.Generator,
eventController event.Generator, eventController event.Generator,
logger *log.Logger, logger *log.Logger,
@ -48,7 +46,6 @@ func NewPolicyController(policyInterface policyclientset.Interface,
policyLister: policyInformer.Lister(), policyLister: policyInformer.Lister(),
policyInterface: policyInterface, policyInterface: policyInterface,
policySynced: policyInformer.Informer().HasSynced, policySynced: policyInformer.Informer().HasSynced,
policyEngine: policyEngine,
violationBuilder: violationBuilder, violationBuilder: violationBuilder,
eventBuilder: eventController, eventBuilder: eventController,
logger: logger, logger: logger,

View file

@ -5,9 +5,10 @@ import (
"fmt" "fmt"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
event "github.com/nirmata/kube-policy/pkg/event" "github.com/nirmata/kube-policy/pkg/event"
"github.com/nirmata/kube-policy/pkg/policyengine"
"github.com/nirmata/kube-policy/pkg/policyengine/mutation" "github.com/nirmata/kube-policy/pkg/policyengine/mutation"
policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" "github.com/nirmata/kube-policy/pkg/policyviolation"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@ -57,7 +58,7 @@ func (pc *PolicyController) processPolicy(policy types.Policy) (
continue continue
} }
violation, eventInfos, err := pc.policyEngine.ProcessExisting(policy, rawResource) violation, eventInfos, err := policyengine.ProcessExisting(policy, rawResource)
if err != nil { if err != nil {
pc.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err) pc.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err)
continue continue

View file

@ -11,9 +11,8 @@ import (
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
policylister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" policylister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1"
event "github.com/nirmata/kube-policy/pkg/event" event "github.com/nirmata/kube-policy/pkg/event"
policyengine "github.com/nirmata/kube-policy/pkg/policyengine" "github.com/nirmata/kube-policy/pkg/policyengine"
mutation "github.com/nirmata/kube-policy/pkg/policyengine/mutation" mutation "github.com/nirmata/kube-policy/pkg/policyengine/mutation"
policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation"
v1beta1 "k8s.io/api/admission/v1beta1" v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -25,13 +24,11 @@ import (
// MutationWebhook is a data type that represents // MutationWebhook is a data type that represents
// business logic for resource mutation // business logic for resource mutation
type MutationWebhook struct { type MutationWebhook struct {
kubeclient *kubeclient.KubeClient kubeclient *kubeclient.KubeClient
policyEngine policyengine.PolicyEngine policyLister policylister.PolicyLister
policyLister policylister.PolicyLister registration *MutationWebhookRegistration
registration *MutationWebhookRegistration eventBuilder event.Generator
violationBuilder policyviolation.Generator logger *log.Logger
eventBuilder event.Generator
logger *log.Logger
} }
// Registers mutation webhook in cluster and creates object for this webhook // Registers mutation webhook in cluster and creates object for this webhook
@ -39,7 +36,6 @@ func CreateMutationWebhook(
clientConfig *rest.Config, clientConfig *rest.Config,
kubeclient *kubeclient.KubeClient, kubeclient *kubeclient.KubeClient,
policyLister policylister.PolicyLister, policyLister policylister.PolicyLister,
violationBuilder policyviolation.Generator,
eventController event.Generator, eventController event.Generator,
logger *log.Logger) (*MutationWebhook, error) { logger *log.Logger) (*MutationWebhook, error) {
if clientConfig == nil || kubeclient == nil { if clientConfig == nil || kubeclient == nil {
@ -59,16 +55,13 @@ func CreateMutationWebhook(
if logger == nil { if logger == nil {
logger = log.New(os.Stdout, "Mutation WebHook: ", log.LstdFlags|log.Lshortfile) logger = log.New(os.Stdout, "Mutation WebHook: ", log.LstdFlags|log.Lshortfile)
} }
policyengine := policyengine.NewPolicyEngine(kubeclient, logger)
return &MutationWebhook{ return &MutationWebhook{
kubeclient: kubeclient, kubeclient: kubeclient,
policyEngine: policyengine, policyLister: policyLister,
policyLister: policyLister, registration: registration,
registration: registration, eventBuilder: eventController,
violationBuilder: violationBuilder, logger: logger,
eventBuilder: eventController,
logger: logger,
}, nil }, nil
} }
@ -141,7 +134,7 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad
// May return nil patches if it is not necessary to create patches for requested object. // 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. // 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) { func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]mutation.PatchBytes, error) {
return mw.policyEngine.ProcessMutation(policy, request.Object.Raw) return policyengine.Mutation(mw.logger, policy, request.Object.Raw)
} }
// kind is the type of object being manipulated, e.g. request.Kind.kind // kind is the type of object being manipulated, e.g. request.Kind.kind