1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-04-15 16:56:56 +00:00

Merge pull request #880 from realshuting/869_auto_gen_annotation

fix policy violation updated without owner
This commit is contained in:
Jim Bugwadia 2020-05-27 06:44:46 -07:00 committed by GitHub
commit 68c431e8f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 35 deletions

View file

@ -32,7 +32,7 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
}()
var engineResponses []response.EngineResponse
var engineResponse response.EngineResponse
var engineResponseMutation, engineResponseValidation response.EngineResponse
var err error
// build context
ctx := context.NewContext()
@ -41,15 +41,14 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
logger.Error(err, "enable to add transform resource to ctx")
}
//MUTATION
engineResponse, err = mutation(policy, resource, ctx, logger)
engineResponses = append(engineResponses, engineResponse)
engineResponseMutation, err = mutation(policy, resource, ctx, logger)
if err != nil {
logger.Error(err, "failed to process mutation rule")
}
//VALIDATION
engineResponse = engine.Validate(engine.PolicyContext{Policy: policy, Context: ctx, NewResource: resource})
engineResponses = append(engineResponses, engineResponse)
engineResponseValidation = engine.Validate(engine.PolicyContext{Policy: policy, Context: ctx, NewResource: resource})
engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation))
//TODO: GENERATION
return engineResponses
@ -80,28 +79,32 @@ func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse
// resource does not match so there was a mutation rule violated
for index, rule := range engineResponse.PolicyResponse.Rules {
log.V(4).Info("veriying if policy rule was applied before", "rule", rule.Name)
if len(rule.Patches) == 0 {
log.V(4).Info("verifying if policy rule was applied before", "rule", rule.Name)
patches := dropKyvernoAnnotation(rule.Patches, log)
if len(patches) == 0 {
continue
}
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(rule.Patches))
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(patches))
if err != nil {
log.Error(err, "failed to decode JSON patch", "patches", rule.Patches)
log.Error(err, "failed to decode JSON patch", "patches", patches)
return response.EngineResponse{}, err
}
// apply the patches returned by mutate to the original resource
patchedResource, err := patch.Apply(rawResource)
if err != nil {
log.Error(err, "failed to apply JSON patch", "patches", rule.Patches)
log.Error(err, "failed to apply JSON patch", "patches", patches)
return response.EngineResponse{}, err
}
if !jsonpatch.Equal(patchedResource, rawResource) {
log.V(4).Info("policy rule conditions not satisfied by resource", "rule", rule.Name)
engineResponse.PolicyResponse.Rules[index].Success = false
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches, log))
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(patches, log))
}
}
return engineResponse, nil
}
@ -125,3 +128,26 @@ func extractPatchPath(patches [][]byte, log logr.Logger) string {
}
return strings.Join(resultPath, ";")
}
func dropKyvernoAnnotation(patches [][]byte, log logr.Logger) (resultPathes [][]byte) {
for _, patch := range patches {
var data jsonPatch
if err := json.Unmarshal(patch, &data); err != nil {
log.Error(err, "failed to decode the generate patch", "patch", string(patch))
continue
}
value := fmt.Sprintf("%v", data.Value)
if strings.Contains(value, engine.PodTemplateAnnotation) {
continue
}
resultPathes = append(resultPathes, patch)
}
return
}
func mergeRuleRespose(mutation, validation response.EngineResponse) response.EngineResponse {
mutation.PolicyResponse.Rules = append(mutation.PolicyResponse.Rules, validation.PolicyResponse.Rules...)
return mutation
}

View file

@ -1,9 +1,10 @@
package policy
import (
informers "k8s.io/client-go/informers/core/v1"
"time"
informers "k8s.io/client-go/informers/core/v1"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
@ -22,6 +23,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
listerv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
@ -61,6 +63,9 @@ type PolicyController struct {
// nspvLister can list/get namespaced policy violation from the shared informer's store
nspvLister kyvernolister.PolicyViolationLister
// nsLister can list/get namespacecs from the shared informer's store
nsLister listerv1.NamespaceLister
// pListerSynced returns true if the Policy store has been synced at least once
pListerSynced cache.InformerSynced
@ -70,7 +75,8 @@ type PolicyController struct {
// pvListerSynced returns true if the Policy Violation store has been synced at least once
nspvListerSynced cache.InformerSynced
nsInformer informers.NamespaceInformer
// nsListerSynced returns true if the namespace store has been synced at least once
nsListerSynced cache.InformerSynced
// Resource manager, manages the mapping for already processed resource
rm resourceManager
@ -84,7 +90,7 @@ type PolicyController struct {
// resourceWebhookWatcher queues the webhook creation request, creates the webhook
resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister
log logr.Logger
log logr.Logger
}
// NewPolicyController create a new PolicyController
@ -117,7 +123,6 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
configHandler: configHandler,
pvGenerator: pvGenerator,
resourceWebhookWatcher: resourceWebhookWatcher,
nsInformer: namespaces,
log: log,
}
@ -147,10 +152,13 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
pc.pLister = pInformer.Lister()
pc.cpvLister = cpvInformer.Lister()
pc.nspvLister = nspvInformer.Lister()
pc.nsLister = namespaces.Lister()
pc.pListerSynced = pInformer.Informer().HasSynced
pc.cpvListerSynced = cpvInformer.Informer().HasSynced
pc.nspvListerSynced = nspvInformer.Informer().HasSynced
pc.nsListerSynced = namespaces.Informer().HasSynced
// resource manager
// rebuild after 300 seconds/ 5 mins
//TODO: pass the time in seconds instead of converting it internally
@ -159,7 +167,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
return &pc, nil
}
func (pc *PolicyController) canBackgroundProcess(p *kyverno.ClusterPolicy) bool {
func (pc *PolicyController) canBackgroundProcess(p *kyverno.ClusterPolicy) bool {
logger := pc.log.WithValues("policy", p.Name)
if !p.BackgroundProcessingEnabled() {
logger.V(4).Info("background processed is disabled")
@ -174,7 +182,6 @@ func (pc *PolicyController) canBackgroundProcess(p *kyverno.ClusterPolicy) boo
return true
}
func (pc *PolicyController) addPolicy(obj interface{}) {
logger := pc.log
p := obj.(*kyverno.ClusterPolicy)
@ -243,7 +250,7 @@ func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) {
logger.Info("starting")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, pc.pListerSynced, pc.cpvListerSynced, pc.nspvListerSynced) {
if !cache.WaitForCacheSync(stopCh, pc.pListerSynced, pc.cpvListerSynced, pc.nspvListerSynced, pc.nsListerSynced) {
logger.Info("failed to sync informer cache")
return
}

View file

@ -1,12 +1,13 @@
package policy
import (
informers "k8s.io/client-go/informers/core/v1"
"reflect"
"strings"
"sync"
"time"
listerv1 "k8s.io/client-go/listers/core/v1"
"github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
@ -72,7 +73,7 @@ func (pc *PolicyController) listResources(policy kyverno.ClusterPolicy) map[stri
rMap := getResourcesPerNamespace(k, pc.client, "", rule, pc.configHandler, pc.log)
mergeResources(resourceMap, rMap)
} else {
namespaces := getNamespacesForRule(&rule, pc.nsInformer, pc.log)
namespaces := getNamespacesForRule(&rule, pc.nsLister, pc.log)
for _, ns := range namespaces {
rMap := getResourcesPerNamespace(k, pc.client, ns, rule, pc.configHandler, pc.log)
mergeResources(resourceMap, rMap)
@ -84,9 +85,9 @@ func (pc *PolicyController) listResources(policy kyverno.ClusterPolicy) map[stri
return resourceMap
}
func getNamespacesForRule(rule *kyverno.Rule, nsInformer informers.NamespaceInformer, log logr.Logger) []string {
if len(rule.MatchResources.Namespaces) > 0 {
return getAllNamespaces(nsInformer, log)
func getNamespacesForRule(rule *kyverno.Rule, nslister listerv1.NamespaceLister, log logr.Logger) []string {
if len(rule.MatchResources.Namespaces) == 0 {
return getAllNamespaces(nslister, log)
}
var wildcards []string
@ -100,8 +101,8 @@ func getNamespacesForRule(rule *kyverno.Rule, nsInformer informers.NamespaceInfo
}
if len(wildcards) > 0 {
wildcardMatches := getMatchingNamespaces(wildcards, nsInformer, log)
results = append (results, wildcardMatches...)
wildcardMatches := getMatchingNamespaces(wildcards, nslister, log)
results = append(results, wildcardMatches...)
}
return results
@ -115,8 +116,8 @@ func hasWildcard(s string) bool {
return strings.Contains(s, "*") || strings.Contains(s, "?")
}
func getMatchingNamespaces(wildcards []string, nsInformer informers.NamespaceInformer, log logr.Logger) []string {
all := getAllNamespaces(nsInformer, log)
func getMatchingNamespaces(wildcards []string, nslister listerv1.NamespaceLister, log logr.Logger) []string {
all := getAllNamespaces(nslister, log)
if len(all) == 0 {
return all
}
@ -133,9 +134,9 @@ func getMatchingNamespaces(wildcards []string, nsInformer informers.NamespaceInf
return results
}
func getAllNamespaces(nsInformer informers.NamespaceInformer, log logr.Logger) []string {
func getAllNamespaces(nslister listerv1.NamespaceLister, log logr.Logger) []string {
var results []string
namespaces, err := nsInformer.Lister().List(labels.NewSelector())
namespaces, err := nslister.List(labels.NewSelector())
if err != nil {
log.Error(err, "Failed to list namespaces")
}
@ -167,6 +168,10 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri
}
// filter based on name
for _, r := range list.Items {
if r.GetDeletionTimestamp() != nil {
continue
}
// match name
if rule.MatchResources.Name != "" {
if !wildcard.Match(rule.MatchResources.Name, r.GetName()) {

View file

@ -93,8 +93,17 @@ func (cpv *clusterPV) createPV(newPv *kyverno.ClusterPolicyViolation) error {
if err != nil {
return fmt.Errorf("failed to retry getting resource for policy violation %s/%s: %v", newPv.Name, newPv.Spec.Policy, err)
}
if obj.GetDeletionTimestamp() != nil {
return nil
}
// set owner reference to resource
ownerRef := createOwnerReference(obj)
ownerRef, ok := createOwnerReference(obj)
if !ok {
return nil
}
newPv.SetOwnerReferences([]metav1.OwnerReference{ownerRef})
// create resource
@ -123,13 +132,14 @@ func (cpv *clusterPV) updatePV(newPv, oldPv *kyverno.ClusterPolicyViolation) err
// set name
newPv.SetName(oldPv.Name)
newPv.SetResourceVersion(oldPv.ResourceVersion)
newPv.SetOwnerReferences(oldPv.GetOwnerReferences())
// update resource
_, err = cpv.kyvernoInterface.ClusterPolicyViolations().Update(newPv)
if err != nil {
return fmt.Errorf("failed to update cluster policy violation: %v", err)
}
logger.Info("cluster policy violation created")
logger.Info("cluster policy violation updated")
if newPv.Annotations["fromSync"] != "true" {
cpv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules})

View file

@ -14,9 +14,19 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)
func createOwnerReference(resource *unstructured.Unstructured) metav1.OwnerReference {
func createOwnerReference(resource *unstructured.Unstructured) (metav1.OwnerReference, bool) {
controllerFlag := true
blockOwnerDeletionFlag := true
apiversion := resource.GetAPIVersion()
kind := resource.GetKind()
name := resource.GetName()
uid := resource.GetUID()
if apiversion == "" || kind == "" || name == "" || uid == "" {
return metav1.OwnerReference{}, false
}
ownerRef := metav1.OwnerReference{
APIVersion: resource.GetAPIVersion(),
Kind: resource.GetKind(),
@ -25,7 +35,7 @@ func createOwnerReference(resource *unstructured.Unstructured) metav1.OwnerRefer
Controller: &controllerFlag,
BlockOwnerDeletion: &blockOwnerDeletionFlag,
}
return ownerRef
return ownerRef, true
}
func retryGetResource(client *client.Client, rspec kyverno.ResourceSpec) (*unstructured.Unstructured, error) {

View file

@ -11,6 +11,7 @@ import (
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/policystatus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unstructedv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
//NamespacedPV ...
@ -92,8 +93,23 @@ func (nspv *namespacedPV) createPV(newPv *kyverno.PolicyViolation) error {
if err != nil {
return fmt.Errorf("failed to retry getting resource for policy violation %s/%s: %v", newPv.Name, newPv.Spec.Policy, err)
}
if obj.GetDeletionTimestamp() != nil {
return nil
}
if newPv.Spec.ResourceSpec.Kind == "Pod" {
if isEvictedPod(obj.Object) {
return nil
}
}
// set owner reference to resource
ownerRef := createOwnerReference(obj)
ownerRef, ok := createOwnerReference(obj)
if !ok {
return nil
}
newPv.SetOwnerReferences([]metav1.OwnerReference{ownerRef})
// create resource
@ -121,6 +137,7 @@ func (nspv *namespacedPV) updatePV(newPv, oldPv *kyverno.PolicyViolation) error
// set name
newPv.SetName(oldPv.Name)
newPv.SetResourceVersion(oldPv.ResourceVersion)
newPv.SetOwnerReferences(oldPv.GetOwnerReferences())
// update resource
_, err = nspv.kyvernoInterface.PolicyViolations(newPv.GetNamespace()).Update(newPv)
if err != nil {
@ -130,6 +147,15 @@ func (nspv *namespacedPV) updatePV(newPv, oldPv *kyverno.PolicyViolation) error
if newPv.Annotations["fromSync"] != "true" {
nspv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules})
}
logger.Info("namespaced policy violation created")
logger.Info("namespaced policy violation updated")
return nil
}
func isEvictedPod(pod map[string]interface{}) bool {
reason, ok, _ := unstructedv1.NestedString(pod, "status", "reason")
if !ok {
return false
}
return reason == "Evicted"
}