diff --git a/Dockerfile b/Dockerfile
index c389c098..11b9c4be 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,16 +12,16 @@ COPY go.sum go.sum
RUN go mod download
# Copy the go source
-COPY cmd/main.go cmd/main.go
+COPY main.go main.go
COPY api/ api/
-COPY internal/controller/ internal/controller/
+COPY controller/ controller/
# Build
# the GOARCH has not a default value to allow the binary be built according to the host where the command
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
-RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
diff --git a/controllers/general/general_controller.go b/controllers/general/general_controller.go
new file mode 100644
index 00000000..a0c54586
--- /dev/null
+++ b/controllers/general/general_controller.go
@@ -0,0 +1,60 @@
+package general
+
+import (
+ "context"
+ "fmt"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ intentv1 "github.com/5GSEC/nimbus/api/v1"
+)
+
+// GeneralController is a struct that holds a Kubernetes client and a WatcherIntent.
+type GeneralController struct {
+ Client client.Client // Client is used to interact with the Kubernetes API.
+ WatcherIntent *WatcherIntent // WatcherIntent is a custom struct to manage specific operations.
+}
+
+// NewGeneralController creates a new instance of GeneralController.
+func NewGeneralController(client client.Client) (*GeneralController, error) {
+ if client == nil {
+ // If the client is not provided, return an error.
+ return nil, fmt.Errorf("GeneralController: Client is nil")
+ }
+
+ // Create a new WatcherIntent.
+ watcherIntent, err := NewWatcherIntent(client)
+ if err != nil {
+ // If there is an error in creating WatcherIntent, return an error.
+ return nil, fmt.Errorf("GeneralController: Error creating WatcherIntent: %v", err)
+ }
+
+ // Return a new GeneralController instance with initialized fields.
+ return &GeneralController{
+ Client: client,
+ WatcherIntent: watcherIntent,
+ }, nil
+}
+
+// Reconcile is the method that will be called when there is an update to the resources being watched.
+func (gc *GeneralController) Reconcile(ctx context.Context, req ctrl.Request) (*intentv1.SecurityIntent, error) {
+ if gc == nil {
+ // If the GeneralController instance is nil, return an error.
+ return nil, fmt.Errorf("GeneralController is nil")
+ }
+ if gc.WatcherIntent == nil {
+ // If the WatcherIntent is not set, return an error.
+ return nil, fmt.Errorf("WatcherIntent is nil")
+ }
+
+ // Call the Reconcile method of WatcherIntent to handle the specific logic.
+ intent, err := gc.WatcherIntent.Reconcile(ctx, req)
+ if err != nil {
+ // If there is an error in reconciliation, return the error.
+ return nil, fmt.Errorf("Error in WatcherIntent.Reconcile: %v", err)
+ }
+
+ // Return the intent and nil as error if reconciliation is successful.
+ return intent, nil
+}
diff --git a/controllers/general/watch_intent.go b/controllers/general/watch_intent.go
new file mode 100644
index 00000000..7ffd7e36
--- /dev/null
+++ b/controllers/general/watch_intent.go
@@ -0,0 +1,64 @@
+package general
+
+import (
+ "context"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/types"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+
+ intentv1 "github.com/5GSEC/nimbus/api/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+)
+
+// WatcherIntent is a struct that holds a Kubernetes client.
+type WatcherIntent struct {
+ Client client.Client // Client to interact with Kubernetes resources.
+}
+
+// NewWatcherIntent creates a new instance of WatcherIntent.
+func NewWatcherIntent(client client.Client) (*WatcherIntent, error) {
+ if client == nil {
+ // Return an error if the client is not provided.
+ return nil, fmt.Errorf("WatcherIntent: Client is nil")
+ }
+
+ // Return a new WatcherIntent instance with the provided client.
+ return &WatcherIntent{
+ Client: client,
+ }, nil
+}
+
+// Reconcile is the method that handles the reconciliation of the Kubernetes resources.
+func (wi *WatcherIntent) Reconcile(ctx context.Context, req ctrl.Request) (*intentv1.SecurityIntent, error) {
+ log := log.FromContext(ctx) // Get the logger from the context.
+
+ // Check if WatcherIntent or its client is not initialized.
+ if wi == nil || wi.Client == nil {
+ fmt.Println("WatcherIntent is nil or Client is nil in Reconcile")
+ return nil, fmt.Errorf("WatcherIntent or Client is not initialized")
+ }
+
+ intent := &intentv1.SecurityIntent{} // Create an instance of SecurityIntent.
+ // Attempt to get the SecurityIntent resource from Kubernetes.
+ err := wi.Client.Get(ctx, types.NamespacedName{
+ Name: req.Name,
+ Namespace: req.Namespace,
+ }, intent)
+
+ if err != nil {
+ // Handle the case where the SecurityIntent resource is not found.
+ if errors.IsNotFound(err) {
+ log.Info("SecurityIntent resource not found. Ignoring since object must be deleted")
+ return nil, nil
+ }
+ // Log and return an error if there is a problem getting the SecurityIntent.
+ log.Error(err, "Failed to get SecurityIntent")
+ return nil, err
+ }
+
+ // Return the SecurityIntent instance if found successfully.
+ return intent, nil
+}
diff --git a/controllers/policy/network_policy.go b/controllers/policy/network_policy.go
new file mode 100644
index 00000000..159ac3c6
--- /dev/null
+++ b/controllers/policy/network_policy.go
@@ -0,0 +1,115 @@
+package policy
+
+import (
+ "context"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+
+ intentv1 "github.com/5GSEC/nimbus/api/v1"
+ ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
+ kubearmorpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1"
+
+ utils "github.com/5GSEC/nimbus/controllers/utils"
+)
+
+// NetworkPolicyController struct to handle network policies.
+type NetworkPolicyController struct {
+ Client client.Client // Client to interact with Kubernetes API.
+ Scheme *runtime.Scheme // Scheme defines the runtime scheme of the Kubernetes objects.
+}
+
+// NewNetworkPolicyController creates a new instance of NetworkPolicyController.
+func NewNetworkPolicyController(client client.Client, scheme *runtime.Scheme) *NetworkPolicyController {
+ return &NetworkPolicyController{
+ Client: client,
+ Scheme: scheme,
+ }
+}
+
+// HandlePolicy processes the network policies defined in the SecurityIntent resource.
+func (npc *NetworkPolicyController) HandlePolicy(ctx context.Context, intent *intentv1.SecurityIntent) error {
+ log := log.FromContext(ctx) // Logger with context.
+
+ // Build and apply/update Cilium Network Policy based on SecurityIntent.
+ ciliumPolicy := utils.BuildCiliumNetworkPolicySpec(ctx, intent).(*ciliumv2.CiliumNetworkPolicy)
+ err := utils.ApplyOrUpdatePolicy(ctx, npc.Client, ciliumPolicy, ciliumPolicy.Name)
+ if err != nil {
+ log.Error(err, "Failed to apply Cilium Network Policy", "Name", ciliumPolicy.Name)
+ return err
+ }
+
+ // If SecurityIntent contains protocol resources, build and apply/update KubeArmor Network Policy.
+ if containsProtocolResource(intent) {
+ armorNetPolicy := utils.BuildKubeArmorPolicySpec(ctx, intent, utils.GetPolicyType(utils.IsHostPolicy(intent))).(*kubearmorpolicyv1.KubeArmorPolicy)
+ err = utils.ApplyOrUpdatePolicy(ctx, npc.Client, armorNetPolicy, armorNetPolicy.Name)
+ if err != nil {
+ log.Error(err, "Failed to apply KubeArmor Network Policy", "Name", armorNetPolicy.Name)
+ return err
+ }
+ }
+
+ log.Info("Applied Network Policy", "PolicyName", intent.Name)
+ return nil
+}
+
+// DeletePolicy removes the network policy associated with the SecurityIntent resource.
+func (npc *NetworkPolicyController) DeletePolicy(ctx context.Context, intent *intentv1.SecurityIntent) error {
+ log := log.FromContext(ctx)
+ var err error
+
+ // Delete KubeArmor or Cilium Network Policy based on the contents of SecurityIntent.
+
+ if containsProtocolResource(intent) {
+ err = deleteNetworkPolicy(ctx, npc.Client, "KubeArmorPolicy", intent.Name, intent.Namespace)
+ if err != nil {
+ log.Error(err, "Failed to delete KubeArmor Network Policy", "Name", intent.Name)
+ return err
+ }
+ } else {
+ // Delete Cilium Network Policy by default
+ err = deleteNetworkPolicy(ctx, npc.Client, "CiliumNetworkPolicy", intent.Name, intent.Namespace)
+ if err != nil {
+ log.Error(err, "Failed to delete Cilium Network Policy", "Name", intent.Name)
+ return err
+ }
+ }
+
+ log.Info("Deleted Network Policy", "PolicyName", intent.Name)
+ return nil
+}
+
+// Additional helper functions for policy creation and deletion.
+func createCiliumNetworkPolicy(ctx context.Context, intent *intentv1.SecurityIntent) *ciliumv2.CiliumNetworkPolicy {
+ return utils.BuildCiliumNetworkPolicySpec(ctx, intent).(*ciliumv2.CiliumNetworkPolicy)
+}
+
+func createKubeArmorNetworkPolicy(ctx context.Context, intent *intentv1.SecurityIntent) *kubearmorpolicyv1.KubeArmorPolicy {
+ return utils.BuildKubeArmorPolicySpec(ctx, intent, "policy").(*kubearmorpolicyv1.KubeArmorPolicy)
+}
+
+func applyCiliumNetworkPolicy(ctx context.Context, c client.Client, policy *ciliumv2.CiliumNetworkPolicy) {
+ utils.ApplyOrUpdatePolicy(ctx, c, policy, policy.Name)
+}
+
+func applyKubeArmorNetworkPolicy(ctx context.Context, c client.Client, policy *kubearmorpolicyv1.KubeArmorPolicy) {
+ utils.ApplyOrUpdatePolicy(ctx, c, policy, policy.Name)
+}
+
+// containsProtocolResource checks for the presence of protocol resources in SecurityIntent.
+func containsProtocolResource(intent *intentv1.SecurityIntent) bool {
+ // Iterates through the intent resources to find if 'protocols' key is present.
+ for _, resource := range intent.Spec.Intent.Resource {
+ if resource.Key == "protocols" {
+ return true
+ }
+ }
+ return false
+}
+
+// deleteNetworkPolicy helps in deleting a specified network policy.
+func deleteNetworkPolicy(ctx context.Context, c client.Client, policyType, name, namespace string) error {
+ // Utilizes utility function to delete the specified network policy.
+ return utils.DeletePolicy(ctx, c, policyType, name, namespace)
+}
diff --git a/controllers/policy/policy_controller.go b/controllers/policy/policy_controller.go
new file mode 100644
index 00000000..f0ef1e81
--- /dev/null
+++ b/controllers/policy/policy_controller.go
@@ -0,0 +1,90 @@
+package policy
+
+import (
+ "context"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+
+ intentv1 "github.com/5GSEC/nimbus/api/v1"
+ utils "github.com/5GSEC/nimbus/controllers/utils"
+)
+
+// Constant for the finalizer name used in the SecurityIntent resource.
+const securityIntentFinalizer = "finalizer.securityintent.intent.security.nimbus.com"
+
+// PolicyController struct handles different types of policies.
+type PolicyController struct {
+ Client client.Client // Client for interacting with Kubernetes API.
+ Scheme *runtime.Scheme // Scheme defines the runtime scheme of the Kubernetes objects.
+ SystemPolicyController *SystemPolicyController // Controller for handling system policies.
+ NetworkPolicyController *NetworkPolicyController // Controller for handling network policies.
+}
+
+// NewPolicyController creates a new instance of PolicyController.
+func NewPolicyController(client client.Client, scheme *runtime.Scheme) *PolicyController {
+ if client == nil || scheme == nil {
+ // Print an error and return nil if the client or scheme is not provided.
+ fmt.Println("PolicyController: Client or Scheme is nil")
+ return nil
+ }
+
+ // Initialize and return a new PolicyController with system and network policy controllers.
+ return &PolicyController{
+ Client: client,
+ Scheme: scheme,
+ SystemPolicyController: NewSystemPolicyController(client, scheme),
+ NetworkPolicyController: NewNetworkPolicyController(client, scheme),
+ }
+}
+
+// Reconcile handles the reconciliation logic for the SecurityIntent resource.
+func (pc *PolicyController) Reconcile(ctx context.Context, intent *intentv1.SecurityIntent) error {
+ log := log.FromContext(ctx) // Logger with context.
+ log.Info("Processing policy", "Name", intent.Name, "Type", intent.Spec.Intent.Type)
+
+ var err error
+
+ // Switch-case to handle different types of policies based on the intent type.
+ switch intent.Spec.Intent.Type {
+ case "system":
+ log.Info("Handling system policy")
+ err = pc.SystemPolicyController.HandlePolicy(ctx, intent)
+ case "network":
+ log.Info("Handling network policy")
+ err = pc.NetworkPolicyController.HandlePolicy(ctx, intent)
+ default:
+ log.Info("Unknown policy type", "Type", intent.Spec.Intent.Type)
+ }
+
+ // Handling finalizer logic for clean up during delete operations.
+ if intent.ObjectMeta.DeletionTimestamp.IsZero() {
+ // If the resource is not being deleted, add the finalizer if it's not present.
+ if !utils.ContainsString(intent.ObjectMeta.Finalizers, securityIntentFinalizer) {
+ intent.ObjectMeta.Finalizers = append(intent.ObjectMeta.Finalizers, securityIntentFinalizer)
+ err = pc.Client.Update(ctx, intent)
+ }
+ } else {
+ // If the resource is being deleted, process deletion based on policy type and remove finalizer.
+ if utils.ContainsString(intent.ObjectMeta.Finalizers, securityIntentFinalizer) {
+ switch intent.Spec.Intent.Type {
+ case "system":
+ err = pc.SystemPolicyController.DeletePolicy(ctx, intent)
+ case "network":
+ err = pc.NetworkPolicyController.DeletePolicy(ctx, intent)
+ default:
+ err = fmt.Errorf("unknown policy type: %s", intent.Spec.Intent.Type)
+ }
+
+ // Removing the finalizer after handling deletion.
+ intent.ObjectMeta.Finalizers = utils.RemoveString(intent.ObjectMeta.Finalizers, securityIntentFinalizer)
+ if updateErr := pc.Client.Update(ctx, intent); updateErr != nil {
+ return updateErr
+ }
+ }
+ }
+
+ return err
+}
diff --git a/controllers/policy/system_policy.go b/controllers/policy/system_policy.go
new file mode 100644
index 00000000..8bcf92ab
--- /dev/null
+++ b/controllers/policy/system_policy.go
@@ -0,0 +1,99 @@
+package policy
+
+import (
+ "context"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+
+ utils "github.com/5GSEC/nimbus/controllers/utils"
+
+ intentv1 "github.com/5GSEC/nimbus/api/v1"
+ kubearmorhostpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy/api/security.kubearmor.com/v1"
+ kubearmorpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1"
+)
+
+// SystemPolicyController is a struct to handle system policies.
+type SystemPolicyController struct {
+ Client client.Client // Client for interacting with Kubernetes API.
+ Scheme *runtime.Scheme // Scheme defines the runtime scheme of the Kubernetes objects.
+}
+
+// NewSystemPolicyController creates a new instance of SystemPolicyController.
+func NewSystemPolicyController(client client.Client, scheme *runtime.Scheme) *SystemPolicyController {
+ return &SystemPolicyController{
+ Client: client,
+ Scheme: scheme,
+ }
+}
+
+// HandlePolicy processes the system policy as defined in SecurityIntent.
+func (spc *SystemPolicyController) HandlePolicy(ctx context.Context, intent *intentv1.SecurityIntent) error {
+ log := log.FromContext(ctx) // Logger with context.
+
+ // Determine if the policy is a HostPolicy.
+ isHost := utils.IsHostPolicy(intent)
+
+ var err error
+ if isHost {
+ // Create and apply a KubeArmorHostPolicy if it's a host policy.
+ hostPolicy := createKubeArmorHostPolicy(ctx, intent)
+ err = applyKubeArmorHostPolicy(ctx, spc.Client, hostPolicy)
+ } else {
+ // Create and apply a KubeArmorPolicy otherwise.
+ armorPolicy := createKubeArmorPolicy(ctx, intent)
+ err = applyKubeArmorPolicy(ctx, spc.Client, armorPolicy)
+ }
+
+ if err != nil {
+ log.Error(err, "Failed to apply policy")
+ return err
+ }
+
+ log.Info("Applied System Policy", "PolicyType", utils.GetPolicyType(isHost), "PolicyName", intent.Name)
+ return nil
+}
+
+// DeletePolicy removes the system policy associated with the SecurityIntent resource.
+func (spc *SystemPolicyController) DeletePolicy(ctx context.Context, intent *intentv1.SecurityIntent) error {
+ log := log.FromContext(ctx)
+
+ isHost := utils.IsHostPolicy(intent)
+ policyType := utils.GetPolicyType(isHost)
+
+ // Delete the system policy.
+ err := deleteSystemPolicy(ctx, spc.Client, policyType, intent.Name, intent.Namespace)
+ if err != nil {
+ log.Error(err, "Failed to delete policy")
+ return err
+ }
+
+ log.Info("Deleted System Policy", "PolicyType", policyType, "PolicyName", intent.Name)
+ return nil
+}
+
+// createKubeArmorHostPolicy(): Creates a KubeArmorHostPolicy object based on the given SecurityIntent
+func createKubeArmorHostPolicy(ctx context.Context, intent *intentv1.SecurityIntent) *kubearmorhostpolicyv1.KubeArmorHostPolicy {
+ return utils.BuildKubeArmorPolicySpec(ctx, intent, "host").(*kubearmorhostpolicyv1.KubeArmorHostPolicy)
+}
+
+// createKubeArmorPolicy creates a KubeArmorPolicy object based on the given SecurityIntent
+func createKubeArmorPolicy(ctx context.Context, intent *intentv1.SecurityIntent) *kubearmorpolicyv1.KubeArmorPolicy {
+ return utils.BuildKubeArmorPolicySpec(ctx, intent, "policy").(*kubearmorpolicyv1.KubeArmorPolicy)
+}
+
+// applyKubeArmorPolicy applies a KubeArmorPolicy to the Kubernetes cluster
+func applyKubeArmorPolicy(ctx context.Context, c client.Client, policy *kubearmorpolicyv1.KubeArmorPolicy) error {
+ return utils.ApplyOrUpdatePolicy(ctx, c, policy, policy.Name)
+}
+
+// applyKubeArmorHostPolicy applies a KubeArmorHostPolicy to the Kubernetes cluster
+func applyKubeArmorHostPolicy(ctx context.Context, c client.Client, policy *kubearmorhostpolicyv1.KubeArmorHostPolicy) error {
+ return utils.ApplyOrUpdatePolicy(ctx, c, policy, policy.Name)
+}
+
+func deleteSystemPolicy(ctx context.Context, c client.Client, policyType, name, namespace string) error {
+ // Utilizes utility function to delete the specified system policy.
+ return utils.DeletePolicy(ctx, c, policyType, name, namespace)
+}
diff --git a/controllers/securityintent_controller.go b/controllers/securityintent_controller.go
new file mode 100644
index 00000000..bfbdfbd3
--- /dev/null
+++ b/controllers/securityintent_controller.go
@@ -0,0 +1,122 @@
+/*
+Copyright 2023.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controllers
+
+import (
+ "context"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "github.com/5GSEC/nimbus/api/v1"
+ general "github.com/5GSEC/nimbus/controllers/general"
+ policy "github.com/5GSEC/nimbus/controllers/policy"
+)
+
+// SecurityIntentReconciler reconciles a SecurityIntent object.
+type SecurityIntentReconciler struct {
+ client.Client
+ Scheme *runtime.Scheme // Scheme defines the runtime scheme of the Kubernetes objects.
+ GeneralController *general.GeneralController // GeneralController is a custom controller for general operations.
+ PolicyController *policy.PolicyController // PolicyController is a custom controller for policy operations.
+}
+
+// NewSecurityIntentReconciler creates a new SecurityIntentReconciler.
+func NewSecurityIntentReconciler(client client.Client, scheme *runtime.Scheme) *SecurityIntentReconciler {
+ // Check if the client is nil.
+ if client == nil {
+ fmt.Println("SecurityIntentReconciler: Client is nil")
+ return nil
+ }
+
+ // Initialize GeneralController; if failed, return nil.
+ generalController, err := general.NewGeneralController(client)
+ if err != nil || generalController == nil { // Check if generalController is nil.
+ fmt.Println("SecurityIntentReconciler: Failed to initialize GeneralController:", err)
+ return nil
+ }
+
+ // Initialize PolicyController; if failed, return nil.
+ policyController := policy.NewPolicyController(client, scheme)
+ if policyController == nil {
+ fmt.Println("SecurityIntentReconciler: Failed to initialize PolicyController")
+ return nil
+ }
+
+ // Return a new instance of SecurityIntentReconciler.
+ return &SecurityIntentReconciler{
+ Client: client,
+ Scheme: scheme,
+ GeneralController: generalController,
+ PolicyController: policyController,
+ }
+}
+
+//+kubebuilder:rbac:groups=intent.security.nimbus.com,resources=securityintents,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=intent.security.nimbus.com,resources=securityintents/status,verbs=get;update;patch
+//+kubebuilder:rbac:groups=intent.security.nimbus.com,resources=securityintents/finalizers,verbs=update
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the SecurityIntent object against the actual cluster state, and then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcil
+
+// Reconcile handles the reconciliation of the SecurityIntent resources.
+func (r *SecurityIntentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ // Check if GeneralController or its components are properly initialized.
+ if r.GeneralController == nil {
+ fmt.Println("SecurityIntentReconciler: GeneralController is nil")
+ return ctrl.Result{}, fmt.Errorf("GeneralController is not properly initialized")
+ }
+ if r.GeneralController.WatcherIntent == nil {
+ fmt.Println("SecurityIntentReconciler: WatcherIntent is nil")
+ return ctrl.Result{}, fmt.Errorf("WatcherIntent is not properly initialized")
+ }
+
+ // Perform Reconcile logic regardless of the state of GeneralController and WatcherIntent.
+ intent, err := r.GeneralController.Reconcile(ctx, req)
+ if err != nil {
+ return ctrl.Result{}, err
+ }
+
+ if intent == nil {
+ return ctrl.Result{}, nil
+ }
+
+ // Invoke the PolicyController's Reconcile method with the intent.
+ err = r.PolicyController.Reconcile(ctx, intent)
+ if err != nil {
+ return ctrl.Result{}, err
+ }
+
+ return ctrl.Result{}, nil
+}
+
+// SetupWithManager sets up the reconciler with the provided manager.
+func (r *SecurityIntentReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ // Set up the controller to manage SecurityIntent resources.
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&v1.SecurityIntent{}).
+ Complete(r)
+}
diff --git a/internal/controller/suite_test.go b/controllers/suite_test.go
similarity index 58%
rename from internal/controller/suite_test.go
rename to controllers/suite_test.go
index 680373d4..59066b3c 100644
--- a/internal/controller/suite_test.go
+++ b/controllers/suite_test.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package controller
+package controllers
import (
"fmt"
@@ -39,21 +39,24 @@ import (
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
-var cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
+var cfg *rest.Config // cfg will hold the Kubernetes rest configuration.
+var k8sClient client.Client // k8sClient is the Kubernetes client for the test environment.
+var testEnv *envtest.Environment // testEnv is the environment for running the tests.
+// TestControllers is the entry point for testing the controllers package.
func TestControllers(t *testing.T) {
- RegisterFailHandler(Fail)
+ RegisterFailHandler(Fail) // Register a Ginkgo fail handler.
- RunSpecs(t, "Controller Suite")
+ RunSpecs(t, "Controller Suite") // Run the Ginkgo specs for the 'Controller Suite'.
}
var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+ // Setup for the test suite, executed before any specs are run.
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) // Set up the logger.
By("bootstrapping test environment")
testEnv = &envtest.Environment{
+ // CRDDirectoryPaths specifies the paths to the CRD manifests.
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
@@ -67,24 +70,26 @@ var _ = BeforeSuite(func() {
}
var err error
- // cfg is defined in this file globally.
- cfg, err = testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
+ cfg, err = testEnv.Start() // Start the test environment.
+ Expect(err).NotTo(HaveOccurred()) // Assert that starting the environment does not produce an error.
+ Expect(cfg).NotTo(BeNil()) // Assert that the config is not nil.
+ // Add the intentv1 API scheme to the runtime scheme.
err = intentv1.AddToScheme(scheme.Scheme)
- Expect(err).NotTo(HaveOccurred())
+ Expect(err).NotTo(HaveOccurred()) // Assert that adding the scheme does not produce an error.
- //+kubebuilder:scaffold:scheme
+ // Scaffold additional schemes here if needed.
+ // Initialize the Kubernetes client for the test environment.
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
- Expect(err).NotTo(HaveOccurred())
- Expect(k8sClient).NotTo(BeNil())
+ Expect(err).NotTo(HaveOccurred()) // Assert that creating the client does not produce an error.
+ Expect(k8sClient).NotTo(BeNil()) // Assert that the client is not nil.
})
var _ = AfterSuite(func() {
+ // Teardown for the test suite, executed after all specs have run.
By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
+ err := testEnv.Stop() // Stop the test environment.
+ Expect(err).NotTo(HaveOccurred()) // Assert that stopping the environment does not produce an error.
})
diff --git a/controllers/utils/utils_policy.go b/controllers/utils/utils_policy.go
new file mode 100644
index 00000000..327795f2
--- /dev/null
+++ b/controllers/utils/utils_policy.go
@@ -0,0 +1,615 @@
+package utils
+
+import (
+ "context"
+ "fmt"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/types"
+ "reflect"
+ "strings"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ client "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+
+ intentv1 "github.com/5GSEC/nimbus/api/v1"
+ ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
+ "github.com/cilium/cilium/pkg/policy/api"
+ kubearmorhostpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy/api/security.kubearmor.com/v1"
+ kubearmorpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1"
+)
+
+// ---------------------------------------------------
+// -------- Creation of Policy Specifications --------
+// ---------------------------------------------------
+
+// BuildKubeArmorPolicySpec creates a policy specification (either KubeArmorPolicy or KubeArmorHostPolicy)
+// based on the provided SecurityIntent and the type of policy.
+func BuildKubeArmorPolicySpec(ctx context.Context, intent *intentv1.SecurityIntent, policyType string) interface{} {
+ log := log.FromContext(ctx)
+ // Logging the creation of a KubeArmor policy.
+ log.Info("Creating KubeArmorPolicy", "Name", intent.Name)
+
+ matchLabels := convertToMapString(extractLabels(intent))
+
+ // Convert extracted information into specific KubeArmor policy types.
+ if policyType == "host" {
+ return &kubearmorhostpolicyv1.KubeArmorHostPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: intent.Name,
+ Namespace: intent.Namespace,
+ },
+ Spec: kubearmorhostpolicyv1.KubeArmorHostPolicySpec{
+ NodeSelector: kubearmorhostpolicyv1.NodeSelectorType{
+ MatchLabels: matchLabels,
+ },
+ Process: convertToKubeArmorHostPolicyProcessType(extractProcessPolicy(intent)),
+ File: convertToKubeArmorHostPolicyFileType(extractFilePolicy(intent)),
+ Capabilities: convertToKubeArmorHostPolicyCapabilitiesType(extractCapabilitiesPolicy(intent)),
+ Network: convertToKubeArmorHostPolicyNetworkType(extractNetworkPolicy(intent)),
+ Action: kubearmorhostpolicyv1.ActionType(formatAction(intent.Spec.Intent.Action)),
+ },
+ }
+ } else {
+ return &kubearmorpolicyv1.KubeArmorPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: intent.Name,
+ Namespace: intent.Namespace,
+ },
+ Spec: kubearmorpolicyv1.KubeArmorPolicySpec{
+ Selector: kubearmorpolicyv1.SelectorType{
+ MatchLabels: matchLabels,
+ },
+ Process: convertToKubeArmorPolicyProcessType(extractProcessPolicy(intent)),
+ File: convertToKubeArmorPolicyFileType(extractFilePolicy(intent)),
+ Capabilities: convertToKubeArmorPolicyCapabilitiesType(extractCapabilitiesPolicy(intent)),
+ Network: convertToKubeArmorPolicyNetworkType(extractNetworkPolicy(intent)),
+ Action: kubearmorpolicyv1.ActionType(formatAction(intent.Spec.Intent.Action)),
+ },
+ }
+ }
+}
+
+// BuildCiliumNetworkPolicySpec creates a Cilium network policy specification based on the provided SecurityIntent.
+func BuildCiliumNetworkPolicySpec(ctx context.Context, intent *intentv1.SecurityIntent) interface{} {
+ // Logging the creation of a Cilium Network Policy.
+ log := log.FromContext(ctx)
+ log.Info("Creating CiliumNetworkPolicy", "Name", intent.Name)
+
+ // Utilize utility functions to construct a Cilium network policy from the intent.
+ endpointSelector := getEndpointSelector(intent)
+ ingressDenyRules := getIngressDenyRules(intent)
+
+ // Build and return a Cilium Network Policy based on the intent.
+ return &ciliumv2.CiliumNetworkPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: intent.Name,
+ Namespace: intent.Namespace,
+ },
+ Spec: &api.Rule{
+ EndpointSelector: endpointSelector,
+ IngressDeny: ingressDenyRules,
+ },
+ }
+}
+
+// ----------------------------------------
+// -------- Conversion Functions ----------
+// ----------------------------------------
+
+// Various conversion functions to transform data from the SecurityIntent into specific policy types.
+// These functions handle different aspects like processing types, file types, network types, etc.,
+// and convert them into the format required by KubeArmor and Cilium policies.
+
+// convertToMapString converts a slice of interfaces to a map of string key-value pairs.
+func convertToMapString(slice []interface{}) map[string]string {
+ // Iterate through the slice, converting each item into a map and merging them into a single map.
+ result := make(map[string]string)
+ for _, item := range slice {
+ for key, value := range item.(map[string]string) {
+ result[key] = value
+ }
+ }
+ return result
+}
+
+func convertToKubeArmorHostPolicyProcessType(slice []interface{}) kubearmorhostpolicyv1.ProcessType {
+ var result kubearmorhostpolicyv1.ProcessType
+ for _, item := range slice {
+ str, ok := item.(string)
+ if !ok {
+ continue // or appropriate error handling
+ }
+ // The 'Pattern' field is of type string, so it can be assigned directly
+ result.MatchPatterns = append(result.MatchPatterns, kubearmorhostpolicyv1.ProcessPatternType{
+ Pattern: str,
+ })
+ }
+ return result
+}
+
+func convertToKubeArmorHostPolicyFileType(slice []interface{}) kubearmorhostpolicyv1.FileType {
+ var result kubearmorhostpolicyv1.FileType
+ for _, item := range slice {
+ result.MatchPaths = append(result.MatchPaths, kubearmorhostpolicyv1.FilePathType{
+ Path: kubearmorhostpolicyv1.MatchPathType(item.(string)),
+ })
+ }
+ return result
+}
+
+func convertToKubeArmorHostPolicyNetworkType(slice []interface{}) kubearmorhostpolicyv1.NetworkType {
+ var result kubearmorhostpolicyv1.NetworkType
+ for _, item := range slice {
+ str, ok := item.(string)
+ if !ok {
+ continue // or appropriate error handling
+ }
+ // Requires explicit type conversion to MatchNetworkProtocolStringType
+ protocol := kubearmorhostpolicyv1.MatchNetworkProtocolStringType(str)
+ result.MatchProtocols = append(result.MatchProtocols, kubearmorhostpolicyv1.MatchNetworkProtocolType{
+ Protocol: protocol,
+ })
+ }
+ return result
+}
+
+func convertToKubeArmorHostPolicyCapabilitiesType(slice []interface{}) kubearmorhostpolicyv1.CapabilitiesType {
+ var result kubearmorhostpolicyv1.CapabilitiesType
+ for _, item := range slice {
+ str, ok := item.(string)
+ if !ok {
+ continue // or appropriate error handling
+ }
+ // Convert to MatchCapabilitiesStringType
+ capability := kubearmorhostpolicyv1.MatchCapabilitiesStringType(str)
+ result.MatchCapabilities = append(result.MatchCapabilities, kubearmorhostpolicyv1.MatchCapabilitiesType{
+ Capability: capability,
+ })
+ }
+ return result
+}
+
+func convertToKubeArmorPolicyProcessType(slice []interface{}) kubearmorpolicyv1.ProcessType {
+ var result kubearmorpolicyv1.ProcessType
+ for _, item := range slice {
+ if str, ok := item.(string); ok {
+ result.MatchPatterns = append(result.MatchPatterns, kubearmorpolicyv1.ProcessPatternType{
+ Pattern: str,
+ })
+ }
+ }
+ return result
+}
+
+func convertToKubeArmorPolicyFileType(slice []interface{}) kubearmorpolicyv1.FileType {
+ var result kubearmorpolicyv1.FileType
+ for _, item := range slice {
+ str, ok := item.(string)
+ if !ok {
+ continue // or appropriate error handling
+ }
+ result.MatchPaths = append(result.MatchPaths, kubearmorpolicyv1.FilePathType{
+ Path: kubearmorpolicyv1.MatchPathType(str),
+ })
+ }
+ return result
+}
+
+func convertToKubeArmorPolicyCapabilitiesType(slice []interface{}) kubearmorpolicyv1.CapabilitiesType {
+ var result kubearmorpolicyv1.CapabilitiesType
+ for _, item := range slice {
+ str, ok := item.(string)
+ if !ok {
+ continue // or appropriate error handling
+ }
+ result.MatchCapabilities = append(result.MatchCapabilities, kubearmorpolicyv1.MatchCapabilitiesType{
+ Capability: kubearmorpolicyv1.MatchCapabilitiesStringType(str),
+ })
+ }
+ return result
+}
+
+func convertToKubeArmorPolicyNetworkType(slice []interface{}) kubearmorpolicyv1.NetworkType {
+ var result kubearmorpolicyv1.NetworkType
+ for _, item := range slice {
+ str, ok := item.(string)
+ if !ok {
+ continue // or appropriate error handling
+ }
+ result.MatchProtocols = append(result.MatchProtocols, kubearmorpolicyv1.MatchNetworkProtocolType{
+ Protocol: kubearmorpolicyv1.MatchNetworkProtocolStringType(str),
+ })
+ }
+ return result
+}
+
+// --------------------------------------
+// -------- Utility Functions ----------
+// --------------------------------------
+
+// GetPolicyType returns the type of policy as a string based on whether it's a host policy.
+func GetPolicyType(isHost bool) string {
+ if isHost {
+ return "KubeArmorHostPolicy"
+ }
+ return "KubeArmorPolicy"
+}
+
+// IsHostPolicy determines if the given SecurityIntent is a Host Policy.
+func IsHostPolicy(intent *intentv1.SecurityIntent) bool {
+ // Check for specific labels in the CEL field of the intent to determine if it's a host policy.
+ for _, cel := range intent.Spec.Selector.CEL {
+ if strings.Contains(cel, "kubernetes.io") {
+ return true
+ }
+ }
+ return false
+}
+
+// cleanLabelKey cleans up the label key to remove unnecessary prefixes.
+func cleanLabelKey(key string) string {
+ // Remove specific prefixes from the label key, if present.
+ if strings.HasPrefix(key, "object.metadata.labels.") {
+ return strings.TrimPrefix(key, "object.metadata.labels.")
+ }
+ return key
+}
+
+// parseCELExpression parses a CEL expression and extracts labels as key-value pairs.
+func parseCELExpression(expression string) map[string]string {
+ // Process the CEL expression and convert it into a map of labels.
+ parsedLabels := make(map[string]string)
+
+ expression = strings.TrimSpace(expression)
+ expressions := strings.Split(expression, " && ")
+
+ for _, expr := range expressions {
+ parts := strings.Split(expr, " == ")
+ if len(parts) == 2 {
+ key := strings.TrimSpace(parts[0])
+ value := strings.TrimSpace(parts[1])
+
+ key = strings.TrimPrefix(key, "object.metadata.labels['")
+ key = strings.TrimSuffix(key, "']")
+ value = strings.Trim(value, "'")
+
+ parsedLabels[key] = value
+ }
+ }
+
+ return parsedLabels
+}
+
+// extractLabels(): Extracts and returns labels from a CEL expression and Match fields
+func extractLabels(intent *intentv1.SecurityIntent) []interface{} {
+ labels := make([]interface{}, 0)
+
+ isHost := IsHostPolicy(intent)
+
+ // Extract labels from Match.Any
+ for _, filter := range intent.Spec.Selector.Match.Any {
+ for key, val := range filter.Resources.MatchLabels {
+ if strings.HasPrefix(key, "object.metadata.labels.") {
+ cleanKey := cleanLabelKey(key)
+ labels = append(labels, map[string]string{cleanKey: val})
+ }
+ }
+ }
+
+ // Extract labels from Match.All
+ for _, filter := range intent.Spec.Selector.Match.All {
+ for key, val := range filter.Resources.MatchLabels {
+ if strings.HasPrefix(key, "object.metadata.labels.") {
+ cleanKey := cleanLabelKey(key)
+ labels = append(labels, map[string]string{cleanKey: val})
+ }
+ }
+ }
+
+ // Extract labels from CEL expressions
+ if isHost {
+ // Special handling for KubeArmorHostPolicy
+ for _, cel := range intent.Spec.Selector.CEL {
+ if strings.Contains(cel, "kubernetes.io/") {
+ parts := strings.Split(cel, " == ")
+ if len(parts) == 2 {
+ key := strings.TrimSpace(parts[0])
+ key = strings.TrimPrefix(key, "object.metadata.labels['")
+ key = strings.TrimSuffix(key, "']")
+ value := strings.Trim(parts[1], "'")
+ cleanKey := cleanLabelKey(key)
+ labels = append(labels, map[string]string{cleanKey: value})
+ }
+ }
+ }
+ } else {
+ // General handling for other policies
+ for _, cel := range intent.Spec.Selector.CEL {
+ if strings.HasPrefix(cel, "object.metadata.labels.") {
+ parsedLabels := parseCELExpression(cel)
+ for key, val := range parsedLabels {
+ cleanKey := cleanLabelKey(key)
+ labels = append(labels, map[string]string{cleanKey: val})
+ }
+ }
+ }
+ }
+
+ return labels
+}
+
+func extractProcessPolicy(intent *intentv1.SecurityIntent) []interface{} {
+ var matchPatterns []interface{}
+ for _, resource := range intent.Spec.Intent.Resource {
+ if resource.Key == "commands" {
+ for _, cmd := range resource.Val {
+ matchPatterns = append(matchPatterns, map[string]string{"Pattern": cmd})
+ }
+ }
+ }
+ return matchPatterns
+}
+
+func extractFilePolicy(intent *intentv1.SecurityIntent) []interface{} {
+ var matchPaths []interface{}
+ for _, resource := range intent.Spec.Intent.Resource {
+ if resource.Key == "paths" {
+ for _, path := range resource.Val {
+ matchPaths = append(matchPaths, map[string]string{"Path": path})
+ }
+ }
+ }
+ return matchPaths
+}
+
+func extractCapabilitiesPolicy(intent *intentv1.SecurityIntent) []interface{} {
+ var matchCapabilities []interface{}
+ for _, resource := range intent.Spec.Intent.Resource {
+ if resource.Key == "capabilities" {
+ for _, capability := range resource.Val {
+ matchCapabilities = append(matchCapabilities, map[string]string{"Capability": capability})
+ }
+ }
+ }
+ return matchCapabilities
+}
+
+// extractNetworkPolicy() - Extracts network policy from SecurityIntent and returns it as a slice of interface{}
+func extractNetworkPolicy(intent *intentv1.SecurityIntent) []interface{} {
+ var matchNetworkProtocols []interface{}
+
+ for _, resource := range intent.Spec.Intent.Resource {
+ if resource.Key == "protocols" {
+ for _, protocol := range resource.Val {
+ protocolMap := map[string]string{"Protocol": protocol}
+ matchNetworkProtocols = append(matchNetworkProtocols, protocolMap)
+ }
+ }
+ }
+
+ return matchNetworkProtocols
+}
+
+// getEndpointSelector creates an endpoint selector from the SecurityIntent.
+func getEndpointSelector(intent *intentv1.SecurityIntent) api.EndpointSelector {
+ // Create an Endpoint Selector based on matched labels extracted from the intent.
+ matchLabels := make(map[string]string)
+
+ // Matching labels to a "Match Any" filter
+ for _, filter := range intent.Spec.Selector.Match.Any {
+ for key, val := range filter.Resources.MatchLabels {
+ matchLabels[key] = val
+ }
+ }
+
+ // Matching labels that fit the "Match All" filter
+ for _, filter := range intent.Spec.Selector.Match.All {
+ for key, val := range filter.Resources.MatchLabels {
+ matchLabels[key] = val
+ }
+ }
+
+ // Create an Endpoint Selector based on matched labels
+ return api.NewESFromMatchRequirements(matchLabels, nil)
+}
+
+// getIngressDenyRules generates ingress deny rules from SecurityIntent.
+func getIngressDenyRules(intent *intentv1.SecurityIntent) []api.IngressDenyRule {
+ // Process the intent to create ingress deny rules.
+ var ingressDenyRules []api.IngressDenyRule
+
+ for _, resource := range intent.Spec.Intent.Resource {
+ if resource.Key == "ingress" {
+ for _, val := range resource.Val {
+ cidr, port, protocol := splitCIDRAndPort(val)
+
+ ingressRule := api.IngressDenyRule{
+ ToPorts: api.PortDenyRules{
+ {
+ Ports: []api.PortProtocol{
+ {
+ Port: port,
+ Protocol: parseProtocol(protocol),
+ },
+ },
+ },
+ },
+ }
+
+ if cidr != "" {
+ ingressRule.FromCIDRSet = []api.CIDRRule{{Cidr: api.CIDR(cidr)}}
+ }
+
+ ingressDenyRules = append(ingressDenyRules, ingressRule)
+ }
+ }
+ }
+
+ return ingressDenyRules
+}
+
+// splitCIDRAndPort separates CIDR, port, and protocol information from a combined string.
+func splitCIDRAndPort(cidrAndPort string) (string, string, string) {
+ // Split the string into CIDR, port, and protocol components, with handling for different formats.
+
+ // Default protocol is TCP
+ defaultProtocol := "TCP"
+
+ // Separate strings based on '-'
+ split := strings.Split(cidrAndPort, "-")
+
+ // If there are three separate elements, return the CIDR, port, and protocol separately
+ if len(split) == 3 {
+ return split[0], split[1], split[2]
+ }
+
+ // If there are two separate elements, return the CIDR, port, and default protocol
+ if len(split) == 2 {
+ return split[0], split[1], defaultProtocol
+ }
+
+ // If there is only one element, return the CIDR, empty port, and default protocol
+ return cidrAndPort, "", defaultProtocol
+}
+
+func parseProtocol(protocol string) api.L4Proto {
+ // Convert protocol string to L4Proto type.
+ switch strings.ToUpper(protocol) {
+ case "TCP":
+ return api.ProtoTCP
+ case "UDP":
+ return api.ProtoUDP
+ case "ICMP":
+ return api.ProtoICMP
+ default:
+ return api.ProtoTCP
+ }
+}
+
+func formatAction(action string) string {
+ // Convert action string to a specific format.
+ switch strings.ToLower(action) {
+ case "block":
+ return "Block"
+ case "audit":
+ return "Audit"
+ case "allow":
+ return "Allow"
+ default:
+ return action
+ }
+}
+
+// ----------------------------------------
+// -------- Apply & Update Policy --------
+// ----------------------------------------
+
+// ApplyOrUpdatePolicy applies or updates the given policy.
+func ApplyOrUpdatePolicy(ctx context.Context, c client.Client, policy client.Object, policyName string) error {
+ // Update the policy if it already exists, otherwise create a new one.
+ log := log.FromContext(ctx)
+
+ var existingPolicy client.Object
+ var policySpec interface{}
+
+ switch p := policy.(type) {
+ case *kubearmorpolicyv1.KubeArmorPolicy:
+ existingPolicy = &kubearmorpolicyv1.KubeArmorPolicy{}
+ policySpec = p.Spec
+ case *kubearmorhostpolicyv1.KubeArmorHostPolicy:
+ existingPolicy = &kubearmorhostpolicyv1.KubeArmorHostPolicy{}
+ policySpec = p.Spec
+ case *ciliumv2.CiliumNetworkPolicy:
+ existingPolicy = &ciliumv2.CiliumNetworkPolicy{}
+ policySpec = p.Spec
+ default:
+ return fmt.Errorf("unsupported policy type")
+ }
+
+ err := c.Get(ctx, types.NamespacedName{Name: policyName, Namespace: policy.GetNamespace()}, existingPolicy)
+ if err != nil && !errors.IsNotFound(err) {
+ // Other error handling
+ log.Error(err, "Failed to get existing policy", "policy", policyName)
+ return err
+ }
+
+ if errors.IsNotFound(err) {
+ // Create a policy if it doesn't exist
+ if err := c.Create(ctx, policy); err != nil {
+ log.Error(err, "Failed to apply policy", "policy", policyName)
+ return err
+ }
+ log.Info("Policy created", "Name", policyName)
+ } else {
+ // Update if policy already exists (compares specs only)
+ existingSpec := reflect.ValueOf(existingPolicy).Elem().FieldByName("Spec").Interface()
+ if !reflect.DeepEqual(policySpec, existingSpec) {
+ reflect.ValueOf(existingPolicy).Elem().FieldByName("Spec").Set(reflect.ValueOf(policySpec))
+ if err := c.Update(ctx, existingPolicy); err != nil {
+ log.Error(err, "Failed to update policy", "policy", policyName)
+ return err
+ }
+ log.Info("Policy updated", "Name", policyName)
+ } else {
+ log.Info("Policy unchanged", "Name", policyName)
+ }
+ }
+ return nil
+}
+
+// ----------------------------------------
+// ----------- Delete Policy -------------
+// ----------------------------------------
+
+// DeletePolicy deletes a policy based on type, name, and namespace.
+func DeletePolicy(ctx context.Context, c client.Client, policyType, name, namespace string) error {
+ // Process the deletion request based on policy type.
+
+ var policy client.Object
+ log := log.FromContext(ctx)
+
+ switch policyType {
+ case "KubeArmorPolicy":
+ policy = &kubearmorpolicyv1.KubeArmorPolicy{}
+ case "KubeArmorHostPolicy":
+ policy = &kubearmorhostpolicyv1.KubeArmorHostPolicy{}
+ case "CiliumNetworkPolicy":
+ policy = &ciliumv2.CiliumNetworkPolicy{}
+ default:
+ return fmt.Errorf("unknown policy type: %s", policyType)
+ }
+
+ policy.SetName(name)
+ policy.SetNamespace(namespace)
+
+ if err := c.Delete(ctx, policy); client.IgnoreNotFound(err) != nil {
+ log.Error(err, "Failed to delete policy", "Type", policyType, "Name", name, "Namespace", namespace)
+ return err
+ }
+ return nil
+}
+
+// containsString checks if a string is included in a slice.
+func ContainsString(slice []string, s string) bool {
+ // Iterate through the slice to check if the string exists.
+
+ for _, item := range slice {
+ if item == s {
+ return true
+ }
+ }
+ return false
+}
+
+// removeString removes a specific string from a slice.
+func RemoveString(slice []string, s string) []string {
+ // Create a new slice excluding the specified string.
+ result := []string{}
+ for _, item := range slice {
+ if item != s {
+ result = append(result, item)
+ }
+ }
+ return result
+}
diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md
index 075e3f10..bd5c3a39 100644
--- a/docs/Getting-Started.md
+++ b/docs/Getting-Started.md
@@ -54,13 +54,14 @@ $ kustomize version
Commands to run Nimbus operators:
-### 1. Generate code
-Generate the necessary code based on the API definition
+### 1. Apply API group resources
+Apply API group resources
```
$ make generate
```
+
### 2. Install CRD
Install Custom Resource Definitions in a Kubernetes Cluster
@@ -68,9 +69,12 @@ Install Custom Resource Definitions in a Kubernetes Cluster
$ make install
```
+📌 Steps 1 and 2 are required if you have a completely clean environment, as they allow the server to find the requested resources.
+
### 3. Run Operators
Run the operator in your local environment
```
+$ make build
$ make run
```
diff --git a/docs/Quick-tutorials.md b/docs/Quick-tutorials.md
index c860797b..f9a2b919 100644
--- a/docs/Quick-tutorials.md
+++ b/docs/Quick-tutorials.md
@@ -5,7 +5,7 @@
busybox-pod
```
-$ kubectl apply -f ./test-yaml/busybox-pod.yaml
+$ kubectl apply -f ./test-yaml/env/busybox-pod.yaml
```
```yaml
@@ -25,7 +25,7 @@ spec:
redis-pod
```
-$ kubectl apply -f ./test-yaml/redis-pod.yaml
+$ kubectl apply -f ./test-yaml/env/redis-pod.yaml
```
```yaml
apiVersion: v1
@@ -65,7 +65,7 @@ $ make run
### Create and apply intent file
```
-$ kubectl apply -f ./test-yaml/intent-redis.yaml
+$ kubectl apply -f ./test-yaml/intents/network/intent-redis.yaml
```
```yaml
apiVersion: intent.security.nimbus.com/v1
@@ -167,5 +167,5 @@ You can see that the policy was applied, so access to port 6379 on the endpoint
```
-$ kubectl delete -f ./test-yaml/intent-redis.yaml
+$ kubectl delete -f ./test-yaml/intents/network/intent-redis.yaml
```
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 919465b1..3692a362 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,9 @@ module github.com/5GSEC/nimbus
go 1.20
require (
+ github.com/cilium/cilium v1.14.3
+ github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy v0.0.0-20230616113436-0f9e047493a0
+ github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy v0.0.0-20230622041458-52e38e236598
github.com/onsi/ginkgo/v2 v2.11.0
github.com/onsi/gomega v1.27.10
k8s.io/apimachinery v0.28.3
@@ -15,7 +18,6 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
- github.com/cilium/cilium v1.14.3 // indirect
github.com/cilium/ebpf v0.10.1-0.20230626090016-654491c8a500 // indirect
github.com/cilium/proxy v0.0.0-20230623092907-8fddead4e52c // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -54,8 +56,6 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
- github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy v0.0.0-20230616113436-0f9e047493a0 // indirect
- github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy v0.0.0-20230622041458-52e38e236598 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
diff --git a/internal/controller/securityintent_controller.go b/internal/controller/securityintent_controller.go
deleted file mode 100644
index 3e118274..00000000
--- a/internal/controller/securityintent_controller.go
+++ /dev/null
@@ -1,978 +0,0 @@
-/*
-Copyright 2023.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package controller
-
-import (
- "context"
- "fmt"
- "k8s.io/apimachinery/pkg/api/errors"
- "reflect"
- "strings"
-
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/types"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/log"
-
- intentv1 "github.com/5GSEC/nimbus/api/v1"
- ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
- "github.com/cilium/cilium/pkg/policy/api"
- kubearmorhostpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy/api/security.kubearmor.com/v1"
- kubearmorpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1"
-)
-
-// SecurityIntentReconciler reconciles a SecurityIntent object
-type SecurityIntentReconciler struct {
- client.Client
- Scheme *runtime.Scheme
-}
-
-//+kubebuilder:rbac:groups=intent.security.nimbus.com,resources=securityintents,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=intent.security.nimbus.com,resources=securityintents/status,verbs=get;update;patch
-//+kubebuilder:rbac:groups=intent.security.nimbus.com,resources=securityintents/finalizers,verbs=update
-
-// Reconcile is part of the main kubernetes reconciliation loop which aims to
-// move the current state of the cluster closer to the desired state.
-// TODO(user): Modify the Reconcile function to compare the state specified by
-// the SecurityIntent object against the actual cluster state, and then
-// perform operations to make the cluster state reflect the state specified by
-// the user.
-//
-// For more details, check Reconcile and its Result here:
-// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcil
-
-const securityIntentFinalizer = "finalizer.securityintent.intent.security.nimbus.com"
-
-// Reconcile attempts to bring the current state of the cluster closer to the desired state.
-func (r *SecurityIntentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- log := log.FromContext(ctx)
-
- // Retrieve the SecurityIntent resource
- intent := &intentv1.SecurityIntent{}
- err := r.Get(ctx, req.NamespacedName, intent)
-
- // Handle cases where the resource is deleted or an error occurred during retrieval
- if err != nil {
- if errors.IsNotFound(err) {
- // Log and exit when the resource is deleted
- log.Info("SecurityIntent resource not found. Ignoring since object must be deleted")
- return ctrl.Result{}, nil
- }
- // Other errors are returned after logging
- log.Error(err, "Failed to get SecurityIntent")
- return ctrl.Result{}, err
- }
-
- // Gets a SecurityIntent object
- intent, err = GeneralController(ctx, r.Client, req.NamespacedName.Name, req.NamespacedName.Namespace)
- if err != nil {
- log.Error(err, "Failed fetching SecurityIntent")
- return ctrl.Result{}, err
- }
-
- // Create and enforce security intent-based policies as actual policies
- if err = PolicyController(ctx, intent, r.Client); err != nil {
- log.Error(err, "Failed applying policy")
- return ctrl.Result{}, err
- }
-
- // Finalizer
- if intent.ObjectMeta.DeletionTimestamp.IsZero() {
- // If the SecurityIntent is not being deleted
- if !containsString(intent.ObjectMeta.Finalizers, securityIntentFinalizer) {
- intent.ObjectMeta.Finalizers = append(intent.ObjectMeta.Finalizers, securityIntentFinalizer)
- if err := r.Update(ctx, intent); err != nil {
- return ctrl.Result{}, err
- }
-
- }
- } else {
- // If the SecurityIntent is being deleted
- if containsString(intent.ObjectMeta.Finalizers, securityIntentFinalizer) {
- // Related policy deletion logic
- if err := deleteRelatedPolicies(ctx, r.Client, intent); err != nil {
- return ctrl.Result{}, err
- }
-
- // Remove Finalizer
- intent.ObjectMeta.Finalizers = removeString(intent.ObjectMeta.Finalizers, securityIntentFinalizer)
- if err := r.Update(ctx, intent); err != nil {
- return ctrl.Result{}, err
- }
-
- }
- return ctrl.Result{}, nil
- }
-
- log.Info("Successfully reconciled SecurityIntent", "intent", intent.Name)
- return ctrl.Result{}, nil
-}
-
-// SetupWithManager sets up the controller with the Manager.
-func (r *SecurityIntentReconciler) SetupWithManager(mgr ctrl.Manager) error {
- return ctrl.NewControllerManagedBy(mgr).
- For(&intentv1.SecurityIntent{}).
- Complete(r)
-}
-
-// ------------------------------
-// ---- General Controller ------
-// ------------------------------
-
-// GeneralController() : Detect and process SecurityIntent objects
-func GeneralController(ctx context.Context, client client.Client, intentName string, namespace string) (*intentv1.SecurityIntent, error) {
- log := log.FromContext(ctx)
- log.Info("Searching for SecurityIntent", "Name", intentName, "Namespace", namespace)
-
- // Search for a SecurityIntent object
- var intent intentv1.SecurityIntent
- if err := client.Get(ctx, types.NamespacedName{Name: intentName, Namespace: namespace}, &intent); err != nil {
- log.Error(err, "Failed searching SecurityIntent")
- return nil, err
- }
-
- log.Info("Found SecurityIntent", "Name", intent.Name, "Namespace", namespace)
- return &intent, nil
-}
-
-// extractLabels(): Extracts and returns labels from a CEL expression and Match fields
-func extractLabels(intent *intentv1.SecurityIntent, isHostPolicy bool) map[string]string {
- labels := make(map[string]string)
-
- // Extract labels from Match.Any
- for _, filter := range intent.Spec.Selector.Match.Any {
- for key, val := range filter.Resources.MatchLabels {
- if strings.HasPrefix(key, "object.metadata.labels.") {
- cleanKey := cleanLabelKey(key)
- labels[cleanKey] = val
- }
- }
- }
-
- // Extract labels from Match.All
- for _, filter := range intent.Spec.Selector.Match.All {
- for key, val := range filter.Resources.MatchLabels {
- if strings.HasPrefix(key, "object.metadata.labels.") {
- cleanKey := cleanLabelKey(key)
- labels[cleanKey] = val
- }
- }
- }
-
- // Extract labels from CEL expressions
- if isHostPolicy {
- // Special handling for KubeArmorHostPolicy
- for _, cel := range intent.Spec.Selector.CEL {
- if strings.Contains(cel, "kubernetes.io/") {
- parts := strings.Split(cel, " == ")
- if len(parts) == 2 {
- key := strings.TrimSpace(parts[0])
- key = strings.TrimPrefix(key, "object.metadata.labels['")
- key = strings.TrimSuffix(key, "']")
- value := strings.Trim(parts[1], "'")
- cleanKey := cleanLabelKey(key)
- labels[cleanKey] = value
- }
- }
- }
- } else {
- // General handling for other policies
- for _, cel := range intent.Spec.Selector.CEL {
- if strings.HasPrefix(cel, "object.metadata.labels.") {
- parsedLabels := parseCELExpression(cel)
- for key, val := range parsedLabels {
- cleanKey := cleanLabelKey(key)
- labels[cleanKey] = val
- }
- }
- }
- }
-
- return labels
-}
-
-// cleanLabelKey(): Cleans up the label key to remove unnecessary prefixes
-func cleanLabelKey(key string) string {
- if strings.HasPrefix(key, "object.metadata.labels.") {
- return strings.TrimPrefix(key, "object.metadata.labels.")
- }
- return key
-}
-
-// parseCELExpression: Parses a CEL expression and extracts labels
-func parseCELExpression(expression string) map[string]string {
- parsedLabels := make(map[string]string)
-
- expression = strings.TrimSpace(expression)
- expressions := strings.Split(expression, " && ")
-
- for _, expr := range expressions {
- parts := strings.Split(expr, " == ")
- if len(parts) == 2 {
- key := strings.TrimSpace(parts[0])
- value := strings.TrimSpace(parts[1])
-
- key = strings.TrimPrefix(key, "object.metadata.labels['")
- key = strings.TrimSuffix(key, "']")
- value = strings.Trim(value, "'")
-
- parsedLabels[key] = value
- }
- }
-
- return parsedLabels
-}
-
-// ------------------------------
-// ---- Policy Controller -------
-// ------------------------------
-
-// PolicyController() : Creates, applies, modifies, and deletes actual policies based on the policies defined in the SecurityIntent.
-func PolicyController(ctx context.Context, intent *intentv1.SecurityIntent, c client.Client) error {
- log := log.FromContext(ctx)
- log.Info("Processing policy", "Name", intent.Name, "Type", intent.Spec.Intent.Type)
-
- switch intent.Spec.Intent.Type {
- case "system":
- if err := handleSystemPolicy(ctx, intent, c); err != nil {
- log.Error(err, "Failed handling system policy")
- return err
- }
- case "network":
- if err := handleNetworkPolicy(ctx, intent, c); err != nil {
- log.Error(err, "Failed handling network policy")
- return err
- }
- if containsProtocolResource(intent) {
- if err := handleKubeArmorNetworkPolicy(ctx, intent, c); err != nil {
- log.Error(err, "Failed handling KubeArmor network policy")
- return err
- }
- }
- default:
- log.Info("Encountered unknown policy type", "type", intent.Spec.Intent.Type)
- }
- log.Info("Policy processing completed", "Name", intent.Name)
- return nil
-}
-
-// ---------------------------------------
-// ------ System Policy --------
-// ---------------------------------------
-
-// handleSystemPolicy(): Handle system policy
-func handleSystemPolicy(ctx context.Context, intent *intentv1.SecurityIntent, c client.Client) error {
- log := log.FromContext(ctx)
-
- // Create and apply the appropriate policy based on whether it's a HostPolicy or not
- if isHostPolicy(intent) {
- hostPolicy := createKubeArmorHostPolicy(ctx, intent, isHostPolicy(intent))
- if err := applyKubeArmorHostPolicy(ctx, c, hostPolicy); err != nil {
- return err
- }
- log.Info("Applied System Policy")
- } else {
- armorPolicy := createKubeArmorPolicy(ctx, intent, isHostPolicy(intent))
- if err := applyKubeArmorPolicy(ctx, c, armorPolicy); err != nil {
- return err
- }
- log.Info("Applied System Policy")
- }
- return nil
-}
-
-// ---------------------------------------
-// ------ Generate System Policy --------
-// ---------------------------------------
-
-// createKubeArmorHostPolicy(): Creates a KubeArmorHostPolicy object based on the given SecurityIntent
-func createKubeArmorHostPolicy(ctx context.Context, intent *intentv1.SecurityIntent, isHost bool) *kubearmorhostpolicyv1.KubeArmorHostPolicy {
- log := log.FromContext(ctx)
- log.Info("Creating KubeArmorHostPolicy", "Name", intent.Name)
-
- process, file, capabilities, network := buildPolicySpec(intent)
- if process == nil {
- process = &kubearmorhostpolicyv1.ProcessType{}
- }
- if file == nil {
- file = &kubearmorhostpolicyv1.FileType{}
- }
- if capabilities == nil {
- capabilities = &kubearmorhostpolicyv1.CapabilitiesType{}
- }
- if network == nil {
- network = &kubearmorhostpolicyv1.NetworkType{}
- }
-
- matchLabels := extractLabels(intent, isHost)
-
- spec := kubearmorhostpolicyv1.KubeArmorHostPolicySpec{
- NodeSelector: kubearmorhostpolicyv1.NodeSelectorType{
- MatchLabels: matchLabels,
- },
- Process: *process,
- File: *file,
- Capabilities: *capabilities,
- Network: *network,
- }
-
- log.Info("KubeArmorHostPolicy created", "Name", intent.Name)
- return &kubearmorhostpolicyv1.KubeArmorHostPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: intent.Name,
- Namespace: intent.Namespace,
- },
- Spec: spec,
- }
-}
-
-// createKubeArmorPolicy() - Creates a KubeArmor policy based on SecurityIntent
-func createKubeArmorPolicy(ctx context.Context, intent *intentv1.SecurityIntent, isHost bool) *kubearmorpolicyv1.KubeArmorPolicy {
- log := log.FromContext(ctx)
- log.Info("Creating KubeArmorPolicy", "Name", intent.Name)
-
- // Extract label match conditions from SecurityIntent
- matchLabels := extractLabels(intent, isHost)
-
- // Constructing KubeArmorPolicySpec based on the intent
- armorPolicySpec := kubearmorpolicyv1.KubeArmorPolicySpec{
- Selector: kubearmorpolicyv1.SelectorType{
- MatchLabels: matchLabels,
- },
- Process: extractProcessPolicy(intent),
- File: extractFilePolicy(intent),
- Network: extractNetworkPolicy(intent),
- Capabilities: extractCapabilitiesPolicy(intent),
- Action: formatActionForArmorPolicy(intent.Spec.Intent.Action),
- }
-
- // Creating the KubeArmorPolicy object
- log.Info("KubeArmorPolicy created", "Name", intent.Name)
- return &kubearmorpolicyv1.KubeArmorPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: intent.Name,
- Namespace: intent.Namespace,
- },
- Spec: armorPolicySpec,
- }
-}
-
-// buildPolicySpec(): Configures the Process, File, Capabilities, Network fields in the given SecurityIntent
-func buildPolicySpec(intent *intentv1.SecurityIntent) (*kubearmorhostpolicyv1.ProcessType, *kubearmorhostpolicyv1.FileType, *kubearmorhostpolicyv1.CapabilitiesType, *kubearmorhostpolicyv1.NetworkType) {
- var process *kubearmorhostpolicyv1.ProcessType
- var file *kubearmorhostpolicyv1.FileType
- var capabilities *kubearmorhostpolicyv1.CapabilitiesType
- var network *kubearmorhostpolicyv1.NetworkType
-
- // Configure the Process and File fields
- if len(intent.Spec.Intent.Resource) > 0 {
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "paths" {
- for _, val := range resource.Val {
- if isExecutablePath(val) {
- // Executable path, add to process match patterns
- if process == nil {
- process = &kubearmorhostpolicyv1.ProcessType{
- MatchPatterns: []kubearmorhostpolicyv1.ProcessPatternType{},
- Action: kubearmorhostpolicyv1.ActionType(intent.Spec.Intent.Action),
- }
- }
- process.MatchPatterns = append(process.MatchPatterns, kubearmorhostpolicyv1.ProcessPatternType{
- Pattern: val,
- })
- } else {
- // Non-executable path, add to file match paths
- if file == nil {
- file = &kubearmorhostpolicyv1.FileType{
- MatchPaths: []kubearmorhostpolicyv1.FilePathType{},
- Action: kubearmorhostpolicyv1.ActionType(intent.Spec.Intent.Action),
- }
- }
- file.MatchPaths = append(file.MatchPaths, kubearmorhostpolicyv1.FilePathType{
- Path: kubearmorhostpolicyv1.MatchPathType(val),
- })
- }
- }
- }
- }
- }
-
- // Set default values for capabilities and network if they are nil
- if capabilities == nil {
- capabilities = &kubearmorhostpolicyv1.CapabilitiesType{
- MatchCapabilities: []kubearmorhostpolicyv1.MatchCapabilitiesType{},
- }
- }
- if network == nil {
- network = &kubearmorhostpolicyv1.NetworkType{
- MatchProtocols: []kubearmorhostpolicyv1.MatchNetworkProtocolType{},
- }
- }
-
- return process, file, capabilities, network
-}
-
-// isHostPolicy(): Determines if the given SecurityIntent is a Host Policy
-func isHostPolicy(intent *intentv1.SecurityIntent) bool {
- // Check for the kubernetes.io label in the CEL field
- for _, cel := range intent.Spec.Selector.CEL {
- if strings.Contains(cel, "kubernetes.io") {
- return true
- }
- }
- return false
-}
-
-// isExecutablePath: Determines if a given path is likely an executable
-func isExecutablePath(path string) bool {
- // Define common executable paths
- executablePaths := []string{"/bin", "/usr/bin", "/sbin", "/usr/sbin", "/usr/local/bin"}
-
- // Check if the path starts with any of the common executable paths
- for _, prefix := range executablePaths {
- if strings.HasPrefix(path, prefix) {
- return true
- }
- }
- return false
-}
-
-// formatPatternWithValcel() : Form the entire pattern based on the 'valcel' pattern
-func formatPatternWithValcel(valcel, command string) string {
- if strings.HasPrefix(valcel, "pattern: ") {
- pattern := strings.TrimPrefix(valcel, "pattern: ")
- // e.g. 'pattern: /**/{command}'
- return strings.Replace(pattern, "{command}", command, -1)
- }
- return command
-}
-
-// extractMatchPaths(): Extract file paths from the resource
-func extractMatchPaths(resources []intentv1.Resource) []kubearmorhostpolicyv1.FilePathType {
- var matchPaths []kubearmorhostpolicyv1.FilePathType
- for _, resource := range resources {
- if resource.Key == "paths" {
- for _, path := range resource.Val {
- matchPaths = append(matchPaths, kubearmorhostpolicyv1.FilePathType{
- Path: kubearmorhostpolicyv1.MatchPathType(path),
- })
- }
- }
- }
- return matchPaths
-}
-
-// extractProcessPolicy() - Extracts process policy from SecurityIntent
-func extractProcessPolicy(intent *intentv1.SecurityIntent) kubearmorpolicyv1.ProcessType {
- var processPolicy kubearmorpolicyv1.ProcessType
-
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "commands" {
- for _, cmd := range resource.Val {
- processPolicy.MatchPatterns = append(processPolicy.MatchPatterns, kubearmorpolicyv1.ProcessPatternType{
- Pattern: cmd,
- })
- }
- }
- }
-
- return processPolicy
-}
-
-// extractFilePolicy() - Extracts file policy from SecurityIntent
-func extractFilePolicy(intent *intentv1.SecurityIntent) kubearmorpolicyv1.FileType {
- var filePolicy kubearmorpolicyv1.FileType
-
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "paths" {
- for _, path := range resource.Val {
- filePolicy.MatchPaths = append(filePolicy.MatchPaths, kubearmorpolicyv1.FilePathType{
- Path: kubearmorpolicyv1.MatchPathType(path), // 올바른 타입으로 수정
- })
- }
- }
- }
-
- return filePolicy
-}
-
-// extractCapabilitiesPolicy() - Extracts capabilities policy from SecurityIntent
-func extractCapabilitiesPolicy(intent *intentv1.SecurityIntent) kubearmorpolicyv1.CapabilitiesType {
- var capabilitiesPolicy kubearmorpolicyv1.CapabilitiesType
-
- // Check for capabilities in the SecurityIntent's resources
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "capabilities" {
- for _, capability := range resource.Val {
- // Add each capability to the MatchCapabilities slice
- capabilitiesPolicy.MatchCapabilities = append(capabilitiesPolicy.MatchCapabilities, kubearmorpolicyv1.MatchCapabilitiesType{
- Capability: kubearmorpolicyv1.MatchCapabilitiesStringType(capability),
- })
- }
- }
- }
-
- // Ensure that MatchCapabilities is always initialized, even if it's empty
- if len(capabilitiesPolicy.MatchCapabilities) == 0 {
- capabilitiesPolicy.MatchCapabilities = []kubearmorpolicyv1.MatchCapabilitiesType{}
- }
-
- return capabilitiesPolicy
-}
-
-// ---------------------------------------
-// ------- Apply System Policy ----------
-// ---------------------------------------
-
-func applyKubeArmorPolicy(ctx context.Context, c client.Client, policy *kubearmorpolicyv1.KubeArmorPolicy) error {
- return applyOrUpdatePolicy(ctx, c, policy, policy.Name)
-}
-
-func applyKubeArmorHostPolicy(ctx context.Context, c client.Client, policy *kubearmorhostpolicyv1.KubeArmorHostPolicy) error {
- return applyOrUpdatePolicy(ctx, c, policy, policy.Name)
-}
-
-// -------------------------------
-// ------ Network Policy --------
-// -------------------------------
-
-// handleNetworkPolicy(): Handle Cilium network policy
-func handleNetworkPolicy(ctx context.Context, intent *intentv1.SecurityIntent, c client.Client) error {
- log := log.FromContext(ctx)
-
- ciliumPolicy := createCiliumNetworkPolicy(ctx, intent)
- if err := applyCiliumNetworkPolicy(ctx, c, ciliumPolicy); err != nil {
- log.Error(err, "Failed applying CiliumNetworkPolicy", "policy", ciliumPolicy)
- return err
- }
- log.Info("Applied CiliumNetworkPolicy", "policy", ciliumPolicy)
-
- return nil // Returns nil on success
-}
-
-// handleKubeArmorNetworkPolicy(): Handle KubeArmor network policy
-func handleKubeArmorNetworkPolicy(ctx context.Context, intent *intentv1.SecurityIntent, c client.Client) error {
- log := log.FromContext(ctx)
-
- // Only create KubeArmor Network Policy if intent type is 'network'
- if intent.Spec.Intent.Type == "network" {
- armorPolicy := createKubeArmorNetworkPolicy(ctx, intent, isHostPolicy(intent))
- if err := applyKubeArmorNetworkPolicy(ctx, c, armorPolicy); err != nil {
- log.Error(err, "Failed applying KubeArmorPolicy", "policy", armorPolicy.Name)
- return err
- }
- log.Info("Applied KubeArmorPolicy", "policy", armorPolicy)
- }
-
- return nil
-}
-
-// ----------------------------------------
-// ------ Generate Network Policy --------
-// ----------------------------------------
-
-// createKubeArmorNetworkPolicy(): Creates a KubeArmor policy for network-related intents
-func createKubeArmorNetworkPolicy(ctx context.Context, intent *intentv1.SecurityIntent, isHost bool) *kubearmorpolicyv1.KubeArmorPolicy {
- log := log.FromContext(ctx)
- log.Info("Creating KubearmorPolicy", "Name", intent.Name)
-
- matchLabels := extractLabels(intent, isHost)
-
- armorPolicySpec := kubearmorpolicyv1.KubeArmorPolicySpec{
- Selector: kubearmorpolicyv1.SelectorType{
- MatchLabels: matchLabels,
- },
- Network: extractNetworkPolicy(intent),
- Action: formatActionForArmorPolicy(intent.Spec.Intent.Action),
- Capabilities: kubearmorpolicyv1.CapabilitiesType{ // 추가된 부분
- MatchCapabilities: []kubearmorpolicyv1.MatchCapabilitiesType{},
- },
- }
-
- return &kubearmorpolicyv1.KubeArmorPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: intent.Name,
- Namespace: intent.Namespace,
- },
- Spec: armorPolicySpec,
- }
-}
-
-// createCiliumNetworkPolicy() : Create a CiliumNetworkPolicy
-func createCiliumNetworkPolicy(ctx context.Context, intent *intentv1.SecurityIntent) *ciliumv2.CiliumNetworkPolicy {
- log := log.FromContext(ctx)
- log.Info("Creating CiliumNetworkPolicy", "Name", intent.Name)
-
- // Create a Endpoint Selector field
- endpointSelector := getEndpointSelector(intent)
- // Create a Ingress Deny Rule
- ingressDenyRules := getIngressDenyRules(intent)
-
- //egressRules := getEgressRules(intent)
-
- // Initializing a CiliumNetworkPolicy Object
- log.Info("CiliumNetworkPolicy created", "Name", intent.Name)
- return &ciliumv2.CiliumNetworkPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: intent.Name, // Policy name
- Namespace: intent.Namespace, // Policy namespace
- },
- Spec: &api.Rule{
- EndpointSelector: endpointSelector, // endpoint selector
- IngressDeny: ingressDenyRules, // Ingress Deny Rule
- //Egress: egressRules,
- },
- }
-}
-
-// extractNetworkPolicy() - Extracts network policy from SecurityIntent
-func extractNetworkPolicy(intent *intentv1.SecurityIntent) kubearmorpolicyv1.NetworkType {
- var networkPolicy kubearmorpolicyv1.NetworkType
-
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "protocols" {
- for _, protocol := range resource.Val {
- networkPolicy.MatchProtocols = append(networkPolicy.MatchProtocols, kubearmorpolicyv1.MatchNetworkProtocolType{
- Protocol: kubearmorpolicyv1.MatchNetworkProtocolStringType(protocol),
- })
- }
- }
- }
-
- // Ensure that MatchProtocols is always initialized, even if it's empty
- if networkPolicy.MatchProtocols == nil {
- networkPolicy.MatchProtocols = []kubearmorpolicyv1.MatchNetworkProtocolType{}
- }
-
- return networkPolicy
-}
-
-// getEndpointSelector(): Creates an endpoint selector from the SecurityIntent
-func getEndpointSelector(intent *intentv1.SecurityIntent) api.EndpointSelector {
- matchLabels := make(map[string]string)
-
- // Matching labels to a "Match Any" filter
- for _, filter := range intent.Spec.Selector.Match.Any {
- for key, val := range filter.Resources.MatchLabels {
- matchLabels[key] = val
- }
- }
-
- // Matching labels that fit the "Match All" filter
- for _, filter := range intent.Spec.Selector.Match.All {
- for key, val := range filter.Resources.MatchLabels {
- matchLabels[key] = val
- }
- }
-
- // Create an Endpoint Selector based on matched labels
- return api.NewESFromMatchRequirements(matchLabels, nil)
-}
-
-// getIngressDenyRules(): Generate ingress deny rules from SecurityIntent
-func getIngressDenyRules(intent *intentv1.SecurityIntent) []api.IngressDenyRule {
- var ingressDenyRules []api.IngressDenyRule
-
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "ingress" {
- for _, val := range resource.Val {
- cidr, port, protocol := splitCIDRAndPort(val)
-
- ingressRule := api.IngressDenyRule{
- ToPorts: api.PortDenyRules{
- {
- Ports: []api.PortProtocol{
- {
- Port: port,
- Protocol: parseProtocol(protocol),
- },
- },
- },
- },
- }
-
- if cidr != "" {
- ingressRule.FromCIDRSet = []api.CIDRRule{{Cidr: api.CIDR(cidr)}}
- }
-
- ingressDenyRules = append(ingressDenyRules, ingressRule)
- }
- }
- }
-
- return ingressDenyRules
-}
-
-// splitCIDRAndPort(): Separates CIDR, port, and protocol information
-func splitCIDRAndPort(cidrAndPort string) (string, string, string) {
- // Default protocol is TCP
- defaultProtocol := "TCP"
-
- // Separate strings based on '-'
- split := strings.Split(cidrAndPort, "-")
-
- // If there are three separate elements, return the CIDR, port, and protocol separately
- if len(split) == 3 {
- return split[0], split[1], split[2]
- }
-
- // If there are two separate elements, return the CIDR, port, and default protocol
- if len(split) == 2 {
- return split[0], split[1], defaultProtocol
- }
-
- // If there is only one element, return the CIDR, empty port, and default protocol
- return cidrAndPort, "", defaultProtocol
-}
-
-// ---------------------------------------
-// ------- Apply Network Policy ---------
-// ---------------------------------------
-
-func applyCiliumNetworkPolicy(ctx context.Context, c client.Client, policy *ciliumv2.CiliumNetworkPolicy) error {
- return applyOrUpdatePolicy(ctx, c, policy, policy.Name)
-}
-
-func applyKubeArmorNetworkPolicy(ctx context.Context, c client.Client, policy *kubearmorpolicyv1.KubeArmorPolicy) error {
- return applyOrUpdatePolicy(ctx, c, policy, policy.Name)
-}
-
-// ------------------------------
-// ------ Apply Policy --------
-// ------------------------------
-
-// applyOrUpdatePolicy: Applies or updates the given policy
-// Update the policy if it already exists, otherwise create a new one.
-func applyOrUpdatePolicy(ctx context.Context, c client.Client, policy client.Object, policyName string) error {
- log := log.FromContext(ctx)
-
- var existingPolicy client.Object
- var policySpec interface{}
-
- switch p := policy.(type) {
- case *kubearmorpolicyv1.KubeArmorPolicy:
- existingPolicy = &kubearmorpolicyv1.KubeArmorPolicy{}
- policySpec = p.Spec
- case *kubearmorhostpolicyv1.KubeArmorHostPolicy:
- existingPolicy = &kubearmorhostpolicyv1.KubeArmorHostPolicy{}
- policySpec = p.Spec
- case *ciliumv2.CiliumNetworkPolicy:
- existingPolicy = &ciliumv2.CiliumNetworkPolicy{}
- policySpec = p.Spec
- default:
- return fmt.Errorf("unsupported policy type")
- }
-
- err := c.Get(ctx, types.NamespacedName{Name: policyName, Namespace: policy.GetNamespace()}, existingPolicy)
- if err != nil && !errors.IsNotFound(err) {
- // Other error handling
- log.Error(err, "Failed to get existing policy", "policy", policyName)
- return err
- }
-
- if errors.IsNotFound(err) {
- // Create a policy if it doesn't exist
- if err := c.Create(ctx, policy); err != nil {
- log.Error(err, "Failed to apply policy", "policy", policyName)
- return err
- }
- log.Info("Policy created", "Name", policyName)
- } else {
- // Update if policy already exists (compares specs only)
- existingSpec := reflect.ValueOf(existingPolicy).Elem().FieldByName("Spec").Interface()
- if !reflect.DeepEqual(policySpec, existingSpec) {
- reflect.ValueOf(existingPolicy).Elem().FieldByName("Spec").Set(reflect.ValueOf(policySpec))
- if err := c.Update(ctx, existingPolicy); err != nil {
- log.Error(err, "Failed to update policy", "policy", policyName)
- return err
- }
- log.Info("Policy updated", "Name", policyName)
- } else {
- log.Info("Policy unchanged", "Name", policyName)
- }
- }
- return nil
-}
-
-// ------------------------------
-// ------ Delete Policy --------
-// ------------------------------
-
-// deleteRelatedPolicies: Delete the associated policies based on the SecurityIntent
-func deleteRelatedPolicies(ctx context.Context, c client.Client, intent *intentv1.SecurityIntent) error {
- log := log.FromContext(ctx)
- log.Info("Deleting related policies", "Name", intent.Name)
-
- switch intent.Spec.Intent.Type {
- case "system":
- if isHostPolicy(intent) {
- // Delete KubeArmorHostPolicy
- if err := deleteKubeArmorHostPolicy(ctx, c, intent.Name, intent.Namespace); err != nil {
- return err
- }
- } else {
- // Delete KubeArmorPolicy
- if err := deleteKubeArmorPolicy(ctx, c, intent.Name, intent.Namespace); err != nil {
- return err
- }
- }
-
- case "network":
- // Delete CiliumNetworkPolicy
- if err := deleteCiliumNetworkPolicy(ctx, c, intent.Name, intent.Namespace); err != nil {
- return err
- }
- // Delete the protocol-specific KubeArmorPolicy if it exists
- if containsProtocolResource(intent) {
- if err := deleteKubeArmorPolicy(ctx, c, intent.Name, intent.Namespace); err != nil {
- return err
- }
- }
- }
-
- log.Info("Related policies deleted successfully", "Name", intent.Name)
- return nil
-}
-
-// deleteKubeArmorPolicy: Delete KubeArmorPolicy
-func deleteKubeArmorPolicy(ctx context.Context, c client.Client, name, namespace string) error {
- log := log.FromContext(ctx)
-
- armorPolicy := &kubearmorpolicyv1.KubeArmorPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- }
- if err := c.Delete(ctx, armorPolicy); client.IgnoreNotFound(err) != nil {
- log.Error(err, "Failed to delete KubeArmorPolicy", "Name", name, "Namespace", namespace)
- return err
- }
- return nil
-}
-
-// deleteKubeArmorHostPolicy: Delete KubeArmorHostPolicy
-func deleteKubeArmorHostPolicy(ctx context.Context, c client.Client, name, namespace string) error {
- log := log.FromContext(ctx)
-
- hostPolicy := &kubearmorhostpolicyv1.KubeArmorHostPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- }
- if err := c.Delete(ctx, hostPolicy); client.IgnoreNotFound(err) != nil {
- log.Error(err, "Failed to delete KubeArmorHostPolicy", "Name", name, "Namespace", namespace)
- return err
- }
- return nil
-}
-
-// deleteCiliumNetworkPolicy: Delete CiliumNetworkPolicy
-func deleteCiliumNetworkPolicy(ctx context.Context, c client.Client, name, namespace string) error {
- log := log.FromContext(ctx)
-
- ciliumPolicy := &ciliumv2.CiliumNetworkPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- }
- if err := c.Delete(ctx, ciliumPolicy); client.IgnoreNotFound(err) != nil {
- log.Error(err, "Failed to delete CiliumNetworkPolicy", "Name", name, "Namespace", namespace)
- return err
- }
- return nil
-}
-
-// -----------------------
-// ------- etc ----------
-// -----------------------
-
-// formatAction(): Convert action value to a valid format
-func formatActionForHostPolicy(action string) kubearmorhostpolicyv1.ActionType {
- switch strings.ToLower(action) {
- case "block":
- return kubearmorhostpolicyv1.ActionType("Block")
- case "audit":
- return kubearmorhostpolicyv1.ActionType("Audit")
- case "allow":
- return kubearmorhostpolicyv1.ActionType("Allow")
- default:
- return kubearmorhostpolicyv1.ActionType(action)
- }
-}
-
-// formatActionForArmorPolicy(): Convert action value to kubearmorpolicyv1.ActionType
-func formatActionForArmorPolicy(action string) kubearmorpolicyv1.ActionType {
- switch strings.ToLower(action) {
- case "block":
- return kubearmorpolicyv1.ActionType("Block")
- case "audit":
- return kubearmorpolicyv1.ActionType("Audit")
- case "allow":
- return kubearmorpolicyv1.ActionType("Allow")
- default:
- return kubearmorpolicyv1.ActionType(action)
- }
-}
-
-func parseProtocol(protocol string) api.L4Proto {
- switch strings.ToUpper(protocol) {
- case "TCP":
- return api.ProtoTCP
- case "UDP":
- return api.ProtoUDP
- case "ICMP":
- return api.ProtoICMP
- default:
- return api.ProtoTCP
- }
-}
-
-// containsProtocolResource: Checking for the presence of protocol resources in SecurityIntent
-func containsProtocolResource(intent *intentv1.SecurityIntent) bool {
- for _, resource := range intent.Spec.Intent.Resource {
- if resource.Key == "protocols" {
- return true
- }
- }
- return false
-}
-
-// containsString: Checking if a string is included in a slice
-func containsString(slice []string, s string) bool {
- for _, item := range slice {
- if item == s {
- return true
- }
- }
- return false
-}
-
-// removeString: Remove a specific string from a slice
-func removeString(slice []string, s string) []string {
- result := []string{}
- for _, item := range slice {
- if item != s {
- result = append(result, item)
- }
- }
- return result
-}
diff --git a/main.go b/main.go
index 9592e580..08f3e2a4 100644
--- a/main.go
+++ b/main.go
@@ -33,7 +33,9 @@ import (
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
intentv1 "github.com/5GSEC/nimbus/api/v1"
- "github.com/5GSEC/nimbus/internal/controller"
+ "github.com/5GSEC/nimbus/controllers"
+ general "github.com/5GSEC/nimbus/controllers/general"
+ policy "github.com/5GSEC/nimbus/controllers/policy"
ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
kubearmorhostpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy/api/security.kubearmor.com/v1"
@@ -41,12 +43,14 @@ import (
//+kubebuilder:scaffold:imports
)
+// Global variable for registering schemes.
var (
- scheme = runtime.NewScheme()
- setupLog = ctrl.Log.WithName("setup")
+ scheme = runtime.NewScheme() // Scheme registers the API types that the client and server should know.
+ setupLog = ctrl.Log.WithName("setup") // Logger specifically for setup.
)
func init() {
+ // In init, various Kubernetes and custom resources are added to the scheme.
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(intentv1.AddToScheme(scheme))
@@ -58,9 +62,11 @@ func init() {
}
func main() {
+ // Flags for the command line parameters like metrics address, leader election, etc.
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
+
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
@@ -72,8 +78,10 @@ func main() {
opts.BindFlags(flag.CommandLine)
flag.Parse()
+ // Setting the logger with the provided options.
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+ // Creating a new manager which will manage all the controllers.
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: metricsserver.Options{BindAddress: metricsAddr},
@@ -97,13 +105,26 @@ func main() {
os.Exit(1)
}
- if err = (&controller.SecurityIntentReconciler{
- Client: mgr.GetClient(),
- Scheme: mgr.GetScheme(),
+ // Setting up the GeneralController and PolicyController.
+ generalController, err := general.NewGeneralController(mgr.GetClient())
+ if err != nil {
+ setupLog.Error(err, "unable to create GeneralController")
+ os.Exit(1)
+ }
+
+ policyController := policy.NewPolicyController(mgr.GetClient(), mgr.GetScheme())
+
+ // Setting up the SecurityIntentReconciler controller with the manager.
+ if err = (&controllers.SecurityIntentReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ GeneralController: generalController,
+ PolicyController: policyController,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "SecurityIntent")
os.Exit(1)
}
+
//+kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
@@ -115,6 +136,7 @@ func main() {
os.Exit(1)
}
+ // Starting the manager.
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
diff --git a/test-yaml/busybox-pod.yaml b/test-yaml/env/busybox-pod.yaml
similarity index 100%
rename from test-yaml/busybox-pod.yaml
rename to test-yaml/env/busybox-pod.yaml
diff --git a/test-yaml/multiubuntu.yaml b/test-yaml/env/multiubuntu.yaml
similarity index 100%
rename from test-yaml/multiubuntu.yaml
rename to test-yaml/env/multiubuntu.yaml
diff --git a/test-yaml/redis-pod.yaml b/test-yaml/env/redis-pod.yaml
similarity index 100%
rename from test-yaml/redis-pod.yaml
rename to test-yaml/env/redis-pod.yaml
diff --git a/test-yaml/intent-net-icmp-audit.yaml b/test-yaml/intents/network/intent-net-icmp-audit.yaml
similarity index 100%
rename from test-yaml/intent-net-icmp-audit.yaml
rename to test-yaml/intents/network/intent-net-icmp-audit.yaml
diff --git a/test-yaml/intent-network-sample.yaml b/test-yaml/intents/network/intent-network-sample.yaml
similarity index 100%
rename from test-yaml/intent-network-sample.yaml
rename to test-yaml/intents/network/intent-network-sample.yaml
diff --git a/test-yaml/intent-redis.yaml b/test-yaml/intents/network/intent-redis.yaml
similarity index 100%
rename from test-yaml/intent-redis.yaml
rename to test-yaml/intents/network/intent-redis.yaml
diff --git a/test-yaml/intent-risky-network-access.yaml b/test-yaml/intents/network/intent-risky-network-access.yaml
similarity index 100%
rename from test-yaml/intent-risky-network-access.yaml
rename to test-yaml/intents/network/intent-risky-network-access.yaml
diff --git a/test-yaml/intent-accessd-shadow-file.yaml b/test-yaml/intents/system/intent-accessd-shadow-file.yaml
similarity index 100%
rename from test-yaml/intent-accessd-shadow-file.yaml
rename to test-yaml/intents/system/intent-accessd-shadow-file.yaml
diff --git a/test-yaml/intent-allow-access-to-credentials-dir.yaml b/test-yaml/intents/system/intent-allow-access-to-credentials-dir.yaml
similarity index 100%
rename from test-yaml/intent-allow-access-to-credentials-dir.yaml
rename to test-yaml/intents/system/intent-allow-access-to-credentials-dir.yaml
diff --git a/test-yaml/intent-bug-block.yaml b/test-yaml/intents/system/intent-bug-block.yaml
similarity index 100%
rename from test-yaml/intent-bug-block.yaml
rename to test-yaml/intents/system/intent-bug-block.yaml
diff --git a/test-yaml/intent-cap-net-raw-block.yaml b/test-yaml/intents/system/intent-cap-net-raw-block.yaml
similarity index 100%
rename from test-yaml/intent-cap-net-raw-block.yaml
rename to test-yaml/intents/system/intent-cap-net-raw-block.yaml
diff --git a/test-yaml/intent-do-not-allow-priv-escalation.yaml b/test-yaml/intents/system/intent-do-not-allow-priv-escalation.yaml
similarity index 100%
rename from test-yaml/intent-do-not-allow-priv-escalation.yaml
rename to test-yaml/intents/system/intent-do-not-allow-priv-escalation.yaml
diff --git a/test-yaml/intent-path-block.yaml b/test-yaml/intents/system/intent-path-block.yaml
similarity index 100%
rename from test-yaml/intent-path-block.yaml
rename to test-yaml/intents/system/intent-path-block.yaml
diff --git a/test-yaml/intent-restrict-write-access-to-sys-folders.yaml b/test-yaml/intents/system/intent-restrict-write-access-to-sys-folders.yaml
similarity index 100%
rename from test-yaml/intent-restrict-write-access-to-sys-folders.yaml
rename to test-yaml/intents/system/intent-restrict-write-access-to-sys-folders.yaml
diff --git a/test-yaml/template-intent.yaml b/test-yaml/intents/template-intent.yaml
similarity index 100%
rename from test-yaml/template-intent.yaml
rename to test-yaml/intents/template-intent.yaml
diff --git a/test-yaml/policy-redis.yaml b/test-yaml/policy/cnp/policy-redis.yaml
similarity index 100%
rename from test-yaml/policy-redis.yaml
rename to test-yaml/policy/cnp/policy-redis.yaml
diff --git a/test-yaml/policy-risky.yaml b/test-yaml/policy/cnp/policy-risky.yaml
similarity index 100%
rename from test-yaml/policy-risky.yaml
rename to test-yaml/policy/cnp/policy-risky.yaml
diff --git a/test-yaml/policy-accessed-shadow-file.yaml b/test-yaml/policy/hsp/policy-accessed-shadow-file.yaml
similarity index 100%
rename from test-yaml/policy-accessed-shadow-file.yaml
rename to test-yaml/policy/hsp/policy-accessed-shadow-file.yaml
diff --git a/test-yaml/policy-bug-block.yaml b/test-yaml/policy/hsp/policy-bug-block.yaml
similarity index 100%
rename from test-yaml/policy-bug-block.yaml
rename to test-yaml/policy/hsp/policy-bug-block.yaml
diff --git a/test-yaml/policy-audit-all-unlink.yaml b/test-yaml/policy/ksp/policy-audit-all-unlink.yaml
similarity index 100%
rename from test-yaml/policy-audit-all-unlink.yaml
rename to test-yaml/policy/ksp/policy-audit-all-unlink.yaml
diff --git a/test-yaml/policy-cap-net-raw-block.yaml b/test-yaml/policy/ksp/policy-cap-net-raw-block.yaml
similarity index 100%
rename from test-yaml/policy-cap-net-raw-block.yaml
rename to test-yaml/policy/ksp/policy-cap-net-raw-block.yaml
diff --git a/test-yaml/policy-file-dir-allow-from-source-path.yaml b/test-yaml/policy/ksp/policy-file-dir-allow-from-source-path.yaml
similarity index 100%
rename from test-yaml/policy-file-dir-allow-from-source-path.yaml
rename to test-yaml/policy/ksp/policy-file-dir-allow-from-source-path.yaml
diff --git a/test-yaml/policy-net-icmp-audit.yaml b/test-yaml/policy/ksp/policy-net-icmp-audit.yaml
similarity index 100%
rename from test-yaml/policy-net-icmp-audit.yaml
rename to test-yaml/policy/ksp/policy-net-icmp-audit.yaml
diff --git a/test-yaml/policy-path-block.yaml b/test-yaml/policy/ksp/policy-path-block.yaml
similarity index 100%
rename from test-yaml/policy-path-block.yaml
rename to test-yaml/policy/ksp/policy-path-block.yaml
diff --git a/test-yaml/policy-proc-dir-block.yaml b/test-yaml/policy/ksp/policy-proc-dir-block.yaml
similarity index 100%
rename from test-yaml/policy-proc-dir-block.yaml
rename to test-yaml/policy/ksp/policy-proc-dir-block.yaml