From 4a700b4b0069279f516eed9f0508dcc626297591 Mon Sep 17 00:00:00 2001 From: Ved Ratan <82467006+VedRatan@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:24:46 +0530 Subject: [PATCH] (chore): Mutate logic update (#198) * updated mutate logic for namespaced policy --------- Signed-off-by: Ved Ratan --- .../clusterscoped/coco-workload-si-sib.yaml | 2 +- .../clusterscoped/csib-3-exclude-names.yaml | 2 + examples/namespaced/coco-workload-si-sib.yaml | 22 ++ pkg/adapter/nimbus-kyverno/manager/manager.go | 63 ++++- .../nimbus-kyverno/processor/kcpbuilder.go | 141 +++++++---- .../nimbus-kyverno/processor/kpbuilder.go | 235 ++++++++++++++---- .../nimbus-kyverno/watcher/kpwatcher.go | 17 +- .../cluster-kyverno-policy.yaml | 2 - 8 files changed, 383 insertions(+), 101 deletions(-) create mode 100644 examples/namespaced/coco-workload-si-sib.yaml diff --git a/examples/clusterscoped/coco-workload-si-sib.yaml b/examples/clusterscoped/coco-workload-si-sib.yaml index 6a74395e..36736ea1 100644 --- a/examples/clusterscoped/coco-workload-si-sib.yaml +++ b/examples/clusterscoped/coco-workload-si-sib.yaml @@ -21,4 +21,4 @@ spec: - "*" workloadSelector: matchLabels: - app: nginx \ No newline at end of file + app: nginx diff --git a/examples/clusterscoped/csib-3-exclude-names.yaml b/examples/clusterscoped/csib-3-exclude-names.yaml index 1a39f991..f8a8c421 100644 --- a/examples/clusterscoped/csib-3-exclude-names.yaml +++ b/examples/clusterscoped/csib-3-exclude-names.yaml @@ -20,6 +20,8 @@ spec: - name: escape-to-host selector: nsSelector: + matchNames: + - "*" excludeNames: - ns-2 - ns-3 diff --git a/examples/namespaced/coco-workload-si-sib.yaml b/examples/namespaced/coco-workload-si-sib.yaml new file mode 100644 index 00000000..748cec6e --- /dev/null +++ b/examples/namespaced/coco-workload-si-sib.yaml @@ -0,0 +1,22 @@ +apiVersion: intent.security.nimbus.com/v1alpha1 +kind: SecurityIntent +metadata: + name: coco-workload +spec: + intent: + id: cocoWorkload + description: "Ensure workload is encryted by running the specified workload in a Confidential VM" + action: Block +--- +apiVersion: intent.security.nimbus.com/v1alpha1 +kind: SecurityIntentBinding +metadata: + name: coco-workload-binding +spec: + intents: + - name: coco-workload + selector: + workloadSelector: + matchLabels: + app: nginx + app1: test \ No newline at end of file diff --git a/pkg/adapter/nimbus-kyverno/manager/manager.go b/pkg/adapter/nimbus-kyverno/manager/manager.go index 1897c422..e29c5c8f 100644 --- a/pkg/adapter/nimbus-kyverno/manager/manager.go +++ b/pkg/adapter/nimbus-kyverno/manager/manager.go @@ -5,11 +5,14 @@ package manager import ( "context" + "fmt" "strings" "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -35,6 +38,7 @@ var ( func init() { utilruntime.Must(v1alpha1.AddToScheme(scheme)) utilruntime.Must(kyvernov1.AddToScheme(scheme)) + utilruntime.Must(corev1.AddToScheme(scheme)) k8sClient = k8s.NewOrDie(scheme) } @@ -51,9 +55,10 @@ func Run(ctx context.Context) { deletedKcpCh := make(chan string) go watcher.WatchKcps(ctx, updatedKcpCh, deletedKcpCh) + addKpCh := make(chan common.Request) updatedKpCh := make(chan common.Request) deletedKpCh := make(chan common.Request) - go watcher.WatchKps(ctx, updatedKpCh, deletedKpCh) + go watcher.WatchKps(ctx, addKpCh, updatedKpCh, deletedKpCh) for { select { @@ -67,6 +72,7 @@ func Run(ctx context.Context) { close(updatedKcpCh) close(deletedKcpCh) + close(addKpCh) close(updatedKpCh) close(deletedKpCh) return @@ -78,6 +84,8 @@ func Run(ctx context.Context) { logKpToDelete(ctx, deletedNp) case deletedCnp := <-deletedClusterNpChan: logKcpToDelete(ctx, deletedCnp) + case addedKp := <-addKpCh: + createTriggerForKp(ctx, addedKp) case updatedKp := <-updatedKpCh: reconcileKp(ctx, updatedKp.Name, updatedKp.Namespace, true) case updatedKcp := <-updatedKcpCh: @@ -259,6 +267,23 @@ func logKpToDelete(ctx context.Context, deletedNp *unstructured.Unstructured) { ) } } + if strings.Contains(deletedNp.GetName(), "coco-workload") { + var cms corev1.ConfigMapList + if err := k8sClient.List(ctx, &cms, &client.ListOptions{Namespace: deletedNp.GetNamespace()}); err != nil { + logger.Error(err, "failed to list ConfigMaps") + return + } + for _, cm := range cms.Items { + for _, cmOwnerRef := range cm.OwnerReferences { + if cmOwnerRef.Name == kp.GetName() && cmOwnerRef.UID == kp.GetUID() { + logger.Info("ConfigMap deleted due to KyvernoPolicy deletion", + "ConfigMap.Name", cm.GetName(), "ConfigMap.Namespace", cm.GetNamespace(), + "KyvernoPolicy.Name", kp.Name, "KyvernoPolicy.Namespace", kp.Namespace, + ) + } + } + } + } } } @@ -382,3 +407,39 @@ func deleteDanglingkcps(ctx context.Context, cnp v1alpha1.ClusterNimbusPolicy, l } } + +func createTriggerForKp(ctx context.Context, nameNamespace common.Request) { + logger := log.FromContext(ctx) + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: nameNamespace.Name + "-trigger-configmap", + Namespace: nameNamespace.Namespace, + }, + Data: map[string]string{ + "data": "dummy", + }, + } + + var existingKp kyvernov1.Policy + err := k8sClient.Get(ctx, types.NamespacedName{Name: nameNamespace.Name, Namespace: nameNamespace.Namespace}, &existingKp) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "failed to get existing KyvernoPolicy", "KyvernoPolicy.Name", existingKp.Name, "KyvernoPolicy.Namespace", nameNamespace.Namespace) + return + } + + // Set MutateExistingKyvernoPolicy as the owner of the zConfigMap + if err := ctrl.SetControllerReference(&existingKp, &configMap.ObjectMeta, scheme); err != nil { + logger.Error(err, "failed to set OwnerReference on ConfigMap", "Name", configMap.Name) + return + } + + // Create the ConfigMap + err = k8sClient.Create(context.TODO(), configMap) + + if err != nil { + logger.Error(err, "failed to create trigger ConfigMap in namespace", nameNamespace.Namespace) + } else { + fmt.Println(nameNamespace) + logger.Info("Created trigger ConfigMap in namespace ", nameNamespace.Namespace) + } +} diff --git a/pkg/adapter/nimbus-kyverno/processor/kcpbuilder.go b/pkg/adapter/nimbus-kyverno/processor/kcpbuilder.go index 509b98a2..b13f8c37 100644 --- a/pkg/adapter/nimbus-kyverno/processor/kcpbuilder.go +++ b/pkg/adapter/nimbus-kyverno/processor/kcpbuilder.go @@ -57,44 +57,79 @@ var nsBlackList = []string{"kube-system"} func clusterCocoRuntimeAddition(cnp *v1alpha1.ClusterNimbusPolicy, rule v1alpha1.Rule) kyvernov1.ClusterPolicy { var matchFilters, excludeFilters []kyvernov1.ResourceFilter - var resourceFilter kyvernov1.ResourceFilter - + labels := cnp.Spec.WorkloadSelector.MatchLabels + excludeNamespaces := cnp.Spec.NsSelector.ExcludeNames + namespaces := cnp.Spec.NsSelector.MatchNames // exclude kube-system - resourceFilter = kyvernov1.ResourceFilter{ + resourceFilter := kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Namespaces: nsBlackList, }, } excludeFilters = append(excludeFilters, resourceFilter) - if len(cnp.Spec.NsSelector.MatchNames) > 0 { - if len(cnp.Spec.WorkloadSelector.MatchLabels) > 0 { + if namespaces[0] != "*" && len(labels) > 0 { + for key, value := range labels { resourceFilter = kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ "apps/v1/Deployment", }, - Namespaces: cnp.Spec.NsSelector.MatchNames, + Namespaces: namespaces, Selector: &metav1.LabelSelector{ - MatchLabels: cnp.Spec.WorkloadSelector.MatchLabels, + MatchLabels: map[string]string{ + key: value, + }, }, }, } + matchFilters = append(matchFilters, resourceFilter) + } + } else if namespaces[0] != "*" && len(labels) == 0 { + resourceFilter = kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{ + "apps/v1/Deployment", + }, + Namespaces: cnp.Spec.NsSelector.MatchNames, + }, + } + matchFilters = append(matchFilters, resourceFilter) + } else if namespaces[0] == "*" && len(labels) > 0 { + + if len(excludeNamespaces) > 0 { + resourceFilter = kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Namespaces: excludeNamespaces, + }, + } + excludeFilters = append(excludeFilters, resourceFilter) + } - } else { + for key, value := range labels { resourceFilter = kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ "apps/v1/Deployment", }, - Namespaces: cnp.Spec.NsSelector.MatchNames, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + key: value, + }, + }, }, } + matchFilters = append(matchFilters, resourceFilter) + } + } else if namespaces[0] == "*" && len(labels) == 0 { + if len(excludeNamespaces) > 0 { + resourceFilter = kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Namespaces: excludeNamespaces, + }, + } + excludeFilters = append(excludeFilters, resourceFilter) } - matchFilters = append(matchFilters, resourceFilter) - - } else if len(cnp.Spec.NsSelector.ExcludeNames) > 0 { - resourceFilter = kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ @@ -103,14 +138,8 @@ func clusterCocoRuntimeAddition(cnp *v1alpha1.ClusterNimbusPolicy, rule v1alpha1 }, } matchFilters = append(matchFilters, resourceFilter) - - resourceFilter = kyvernov1.ResourceFilter{ - ResourceDescription: kyvernov1.ResourceDescription{ - Namespaces: cnp.Spec.NsSelector.ExcludeNames, - }, - } - excludeFilters = append(excludeFilters, resourceFilter) } + patchStrategicMerge := map[string]interface{}{ "spec": map[string]interface{}{ "template": map[string]interface{}{ @@ -141,13 +170,13 @@ func clusterCocoRuntimeAddition(cnp *v1alpha1.ClusterNimbusPolicy, rule v1alpha1 kyvernov1.TargetResourceSpec{ ResourceSpec: kyvernov1.ResourceSpec{ APIVersion: "apps/v1", - Kind: "Deployment", + Kind: "Deployment", }, }, }, RawPatchStrategicMerge: &v1.JSON{ Raw: patchBytes, - } , + }, }, }, }, @@ -171,44 +200,78 @@ func clusterEscapeToHost(cnp *v1alpha1.ClusterNimbusPolicy, rule v1alpha1.Rule) } var matchFilters, excludeFilters []kyvernov1.ResourceFilter - var resourceFilter kyvernov1.ResourceFilter - + labels := cnp.Spec.WorkloadSelector.MatchLabels + excludeNamespaces := cnp.Spec.NsSelector.ExcludeNames + namespaces := cnp.Spec.NsSelector.MatchNames // exclude kube-system - resourceFilter = kyvernov1.ResourceFilter{ + resourceFilter := kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Namespaces: nsBlackList, }, } excludeFilters = append(excludeFilters, resourceFilter) - if len(cnp.Spec.NsSelector.MatchNames) > 0 { - if len(cnp.Spec.WorkloadSelector.MatchLabels) > 0 { + if namespaces[0] != "*" && len(labels) > 0 { + for key, value := range labels { resourceFilter = kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ "v1/Pod", }, - Namespaces: cnp.Spec.NsSelector.MatchNames, + Namespaces: namespaces, Selector: &metav1.LabelSelector{ - MatchLabels: cnp.Spec.WorkloadSelector.MatchLabels, + MatchLabels: map[string]string{ + key: value, + }, }, }, } - - } else { + matchFilters = append(matchFilters, resourceFilter) + } + } else if namespaces[0] != "*" && len(labels) == 0 { + resourceFilter = kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{ + "v1/Pod", + }, + Namespaces: cnp.Spec.NsSelector.MatchNames, + }, + } + matchFilters = append(matchFilters, resourceFilter) + } else if namespaces[0] == "*" && len(labels) > 0 { + if len(excludeNamespaces) > 0 { + resourceFilter = kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription { + Namespaces: excludeNamespaces, + }, + } + excludeFilters = append(excludeFilters, resourceFilter) + } + for key, value := range labels { resourceFilter = kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ "v1/Pod", }, - Namespaces: cnp.Spec.NsSelector.MatchNames, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + key: value, + }, + }, }, } + matchFilters = append(matchFilters, resourceFilter) } - matchFilters = append(matchFilters, resourceFilter) - - } else if len(cnp.Spec.NsSelector.ExcludeNames) > 0 { + } else if namespaces[0] == "*" && len(labels) == 0 { + if len(excludeNamespaces) > 0 { + resourceFilter = kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Namespaces: excludeNamespaces, + }, + } + excludeFilters = append(excludeFilters, resourceFilter) + } resourceFilter = kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ @@ -217,15 +280,7 @@ func clusterEscapeToHost(cnp *v1alpha1.ClusterNimbusPolicy, rule v1alpha1.Rule) }, } matchFilters = append(matchFilters, resourceFilter) - - resourceFilter = kyvernov1.ResourceFilter{ - ResourceDescription: kyvernov1.ResourceDescription{ - Namespaces: cnp.Spec.NsSelector.ExcludeNames, - }, - } - excludeFilters = append(excludeFilters, resourceFilter) } - background := true return kyvernov1.ClusterPolicy{ Spec: kyvernov1.Spec{ diff --git a/pkg/adapter/nimbus-kyverno/processor/kpbuilder.go b/pkg/adapter/nimbus-kyverno/processor/kpbuilder.go index db380e84..ebc11090 100644 --- a/pkg/adapter/nimbus-kyverno/processor/kpbuilder.go +++ b/pkg/adapter/nimbus-kyverno/processor/kpbuilder.go @@ -4,57 +4,86 @@ package processor import ( + "context" "encoding/json" "strings" v1alpha1 "github.com/5GSEC/nimbus/api/v1alpha1" "github.com/5GSEC/nimbus/pkg/adapter/idpool" + "github.com/5GSEC/nimbus/pkg/adapter/k8s" "github.com/go-logr/logr" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "go.uber.org/multierr" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" "k8s.io/pod-security-admission/api" ) +var ( + client dynamic.Interface +) + +func init() { + client = k8s.NewDynamicClient() +} + func BuildKpsFrom(logger logr.Logger, np *v1alpha1.NimbusPolicy) []kyvernov1.Policy { // Build KPs based on given IDs - var kps []kyvernov1.Policy + var allkps []kyvernov1.Policy for _, nimbusRule := range np.Spec.NimbusRules { id := nimbusRule.ID if idpool.IsIdSupportedBy(id, "kyverno") { - kp := buildKpFor(id, np) - kp.Name = np.Name + "-" + strings.ToLower(id) - kp.Namespace = np.Namespace - kp.Annotations = make(map[string]string) - kp.Annotations["policies.kyverno.io/description"] = nimbusRule.Description - if nimbusRule.Rule.RuleAction == "Block" { - kp.Spec.ValidationFailureAction = kyvernov1.ValidationFailureAction("Enforce") - } else { - kp.Spec.ValidationFailureAction = kyvernov1.ValidationFailureAction("Audit") + kps, err := buildKpFor(id, np) + if err != nil { + logger.Error(err, "error while building kyverno policies") + } + for _, kp := range kps { + if id != "cocoWorkload" { + kp.Name = np.Name + "-" + strings.ToLower(id) + } + kp.Namespace = np.Namespace + kp.Annotations = make(map[string]string) + kp.Annotations["policies.kyverno.io/description"] = nimbusRule.Description + if nimbusRule.Rule.RuleAction == "Block" { + kp.Spec.ValidationFailureAction = kyvernov1.ValidationFailureAction("Enforce") + } else { + kp.Spec.ValidationFailureAction = kyvernov1.ValidationFailureAction("Audit") + } + addManagedByAnnotation(&kp) + allkps = append(allkps, kp) } - addManagedByAnnotation(&kp) - kps = append(kps, kp) } else { logger.Info("Kyverno does not support this ID", "ID", id, "NimbusPolicy", np.Name, "NimbusPolicy.Namespace", np.Namespace) } } - return kps + return allkps } // buildKpFor builds a KyvernoPolicy based on intent ID supported by Kyverno Policy Engine. -func buildKpFor(id string, np *v1alpha1.NimbusPolicy) kyvernov1.Policy { +func buildKpFor(id string, np *v1alpha1.NimbusPolicy) ([]kyvernov1.Policy, error) { + var kps []kyvernov1.Policy switch id { case idpool.EscapeToHost: - return escapeToHost(np, np.Spec.NimbusRules[0].Rule) + kps = append(kps, escapeToHost(np, np.Spec.NimbusRules[0].Rule)) case idpool.CocoWorkload: - return cocoRuntimeAddition(np, np.Spec.NimbusRules[0].Rule) - default: - return kyvernov1.Policy{} + kpols, err := cocoRuntimeAddition(np) + if err != nil { + return kps, err + } + kps = append(kps, kpols...) } + return kps, nil } -func cocoRuntimeAddition(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov1.Policy { +func cocoRuntimeAddition(np *v1alpha1.NimbusPolicy) ([]kyvernov1.Policy, error) { + var kps []kyvernov1.Policy + var errs []error + var deployNames []string + var mutateTargetResourceSpecs []kyvernov1.TargetResourceSpec + var matchResourceFilters []kyvernov1.ResourceFilter labels := np.Spec.Selector.MatchLabels patchStrategicMerge := map[string]interface{}{ "spec": map[string]interface{}{ @@ -67,12 +96,82 @@ func cocoRuntimeAddition(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov } patchBytes, err := json.Marshal(patchStrategicMerge) if err != nil { - panic(err) + errs = append(errs, err) } if err != nil { - panic(err) + errs = append(errs, err) } - kp := kyvernov1.Policy{ + + deploymentsGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + // labelSelector := metav1.LabelSelector{MatchLabels: labels} + // listOptions := metav1.ListOptions{ + // LabelSelector: apiLabels.Set(labelSelector.MatchLabels).String(), + // } + deployments, err := client.Resource(deploymentsGVR).Namespace(np.Namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + errs = append(errs, err) + } + var markLabels = make(map[string]string) + for _, d := range deployments.Items { + for k, v := range d.GetLabels() { + key := k + ":" + v + markLabels[key] = d.GetName() + } + } + for k, v := range labels { + key := k + ":" + v + if markLabels[key] != "" { + deployNames = append(deployNames, markLabels[key]) + } + } + + for _, deployName := range deployNames { + mutateResourceSpec := kyvernov1.TargetResourceSpec{ + ResourceSpec: kyvernov1.ResourceSpec{ + APIVersion: "apps/v1", + Kind: "Deployment", + Name: deployName, + }, + } + mutateTargetResourceSpecs = append(mutateTargetResourceSpecs, mutateResourceSpec) + } + if len(labels) > 0 { + for key, value := range labels { + resourceFilter := kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{ + "apps/v1/Deployment", + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + key: value, + }, + }, + }, + } + matchResourceFilters = append(matchResourceFilters, resourceFilter) + } + } else if len(labels) == 0 { + mutateResourceSpec := kyvernov1.TargetResourceSpec{ + ResourceSpec: kyvernov1.ResourceSpec{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + } + mutateTargetResourceSpecs = append(mutateTargetResourceSpecs, mutateResourceSpec) + + resourceFilter := kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{ + "apps/v1/Deployment", + }, + }, + } + + matchResourceFilters = append(matchResourceFilters, resourceFilter) + } + + mutateExistingKp := kyvernov1.Policy{ Spec: kyvernov1.Spec{ MutateExistingOnPolicyUpdate: true, Rules: []kyvernov1.Rule{ @@ -83,25 +182,35 @@ func cocoRuntimeAddition(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov kyvernov1.ResourceFilter{ ResourceDescription: kyvernov1.ResourceDescription{ Kinds: []string{ - "apps/v1/Deployment", - }, - Selector: &metav1.LabelSelector{ - MatchLabels: np.Spec.Selector.MatchLabels, + "v1/ConfigMap", }, + Name: np.Name + "-mutateexisting-trigger-configmap", }, }, }, }, Mutation: kyvernov1.Mutation{ - Targets: []kyvernov1.TargetResourceSpec{ - kyvernov1.TargetResourceSpec{ - ResourceSpec: kyvernov1.ResourceSpec{ - APIVersion: "apps/v1", - Kind: "Deployment", - Namespace: np.Namespace, - }, - }, + Targets: mutateTargetResourceSpecs, + RawPatchStrategicMerge: &v1.JSON{ + Raw: patchBytes, }, + }, + }, + }, + }, + } + mutateExistingKp.Name = np.Name + "-mutateexisting" + + mutateNewKp := kyvernov1.Policy{ + Spec: kyvernov1.Spec{ + + Rules: []kyvernov1.Rule{ + { + Name: "add runtime", + MatchResources: kyvernov1.MatchResources{ + Any: matchResourceFilters, + }, + Mutation: kyvernov1.Mutation{ RawPatchStrategicMerge: &v1.JSON{ Raw: patchBytes, }, @@ -111,15 +220,23 @@ func cocoRuntimeAddition(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov }, } - if len(labels) > 0 { - kp.Spec.Rules[0].MatchResources.Any[0].ResourceDescription.Selector.MatchLabels = labels + mutateNewKp.Name = np.Name + "-mutateoncreate" + + if (len(deployNames) > 0) || (len(labels) == 0 && len(deployments.Items) > 0) { // if labels are present but no deploy exists with matching label or labels are not present but deployments exists + kps = append(kps, mutateExistingKp) } - return kp + kps = append(kps, mutateNewKp) + + if len(errs) != 0 { + return kps, nil + } + return kps, multierr.Combine(errs...) } func escapeToHost(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov1.Policy { var psa_level api.Level = api.LevelBaseline + var matchResourceFilters []kyvernov1.ResourceFilter if rule.Params["psa_level"] != nil { @@ -134,6 +251,33 @@ func escapeToHost(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov1.Polic labels := np.Spec.Selector.MatchLabels + if len(labels) > 0 { + for key, value := range labels { + resourceFilter := kyvernov1.ResourceFilter { + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{ + "v1/Pod", + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + key: value, + }, + }, + }, + } + matchResourceFilters = append(matchResourceFilters, resourceFilter) + } + } else { + resourceFilter := kyvernov1.ResourceFilter{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{ + "v1/Pod", + }, + }, + } + matchResourceFilters = append(matchResourceFilters, resourceFilter) + } + background := true kp := kyvernov1.Policy{ Spec: kyvernov1.Spec{ @@ -142,18 +286,7 @@ func escapeToHost(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov1.Polic { Name: "pod-security-standard", MatchResources: kyvernov1.MatchResources{ - Any: kyvernov1.ResourceFilters{ - kyvernov1.ResourceFilter{ - ResourceDescription: kyvernov1.ResourceDescription{ - Kinds: []string{ - "v1/Pod", - }, - Selector: &metav1.LabelSelector{ - MatchLabels: np.Spec.Selector.MatchLabels, - }, - }, - }, - }, + Any: matchResourceFilters, }, Validation: kyvernov1.Validation{ PodSecurity: &kyvernov1.PodSecurity{ @@ -166,10 +299,6 @@ func escapeToHost(np *v1alpha1.NimbusPolicy, rule v1alpha1.Rule) kyvernov1.Polic }, } - if len(labels) > 0 { - kp.Spec.Rules[0].MatchResources.Any[0].ResourceDescription.Selector.MatchLabels = labels - } - return kp } diff --git a/pkg/adapter/nimbus-kyverno/watcher/kpwatcher.go b/pkg/adapter/nimbus-kyverno/watcher/kpwatcher.go index 119ad763..f11c2f91 100644 --- a/pkg/adapter/nimbus-kyverno/watcher/kpwatcher.go +++ b/pkg/adapter/nimbus-kyverno/watcher/kpwatcher.go @@ -5,6 +5,7 @@ package watcher import ( "context" + "strings" "time" "github.com/5GSEC/nimbus/pkg/adapter/common" @@ -38,10 +39,24 @@ func kpInformer() cache.SharedIndexInformer { // WatchKps watches update and delete event for KyvernoPolicies owned by // NimbusPolicy or ClusterNimbusPolicy and put their info on respective channels. -func WatchKps(ctx context.Context, updatedKpCh, deletedKpCh chan common.Request) { +func WatchKps(ctx context.Context, addKpch, updatedKpCh, deletedKpCh chan common.Request) { logger := log.FromContext(ctx) informer := kpInformer() handlers := cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + u := obj.(*unstructured.Unstructured) + if adapterutil.IsOrphan(u.GetOwnerReferences(), "NimbusPolicy") { + logger.V(4).Info("Ignoring orphan KyvernoPolicy", "KyvernoPolicy.Name", u.GetName(), "KyvernoPolicy.Namespace", u.GetNamespace(), "Operation", "Delete") + return + } + kpNamespacedName := common.Request{ + Name: u.GetName(), + Namespace: u.GetNamespace(), + } + if strings.Contains(kpNamespacedName.Name, "mutateexisting") { + addKpch <- kpNamespacedName + } + }, UpdateFunc: func(oldObj, newObj interface{}) { oldU := oldObj.(*unstructured.Unstructured) newU := newObj.(*unstructured.Unstructured) diff --git a/tests/e2e/escape-to-host-clusterscoped-matchall/cluster-kyverno-policy.yaml b/tests/e2e/escape-to-host-clusterscoped-matchall/cluster-kyverno-policy.yaml index df14f42d..a0d6faf2 100644 --- a/tests/e2e/escape-to-host-clusterscoped-matchall/cluster-kyverno-policy.yaml +++ b/tests/e2e/escape-to-host-clusterscoped-matchall/cluster-kyverno-policy.yaml @@ -33,8 +33,6 @@ spec: - resources: kinds: - v1/Pod - namespaces: - - '*' selector: matchLabels: app: nginx