diff --git a/api/v1/groupversion_info.go b/Nimbus/api/v1/groupversion_info.go similarity index 100% rename from api/v1/groupversion_info.go rename to Nimbus/api/v1/groupversion_info.go diff --git a/api/v1/zz_generated.deepcopy.go b/Nimbus/api/v1/zz_generated.deepcopy.go similarity index 99% rename from api/v1/zz_generated.deepcopy.go rename to Nimbus/api/v1/zz_generated.deepcopy.go index 875d649d..d00d6c4a 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/Nimbus/api/v1/zz_generated.deepcopy.go @@ -11,6 +11,8 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) + + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Intent) DeepCopyInto(out *Intent) { *out = *in diff --git a/main.go b/Nimbus/cmd/main.go similarity index 65% rename from main.go rename to Nimbus/cmd/main.go index 1097c3ce..a6082dca 100644 --- a/main.go +++ b/Nimbus/cmd/main.go @@ -4,11 +4,15 @@ package main import ( + "context" "flag" "os" + "os/signal" + "syscall" + "time" - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. + // Importing all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can utilize them. _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/apimachinery/pkg/runtime" @@ -19,11 +23,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - intentv1 "github.com/5GSEC/nimbus/api/v1" - "github.com/5GSEC/nimbus/controllers" - general "github.com/5GSEC/nimbus/controllers/general" - policy "github.com/5GSEC/nimbus/controllers/policy" + // Importing custom API types and controllers + intentv1 "github.com/5GSEC/nimbus/Nimbus/api/v1" + "github.com/5GSEC/nimbus/Nimbus/controllers" + cleanup "github.com/5GSEC/nimbus/Nimbus/controllers/cleanup" + general "github.com/5GSEC/nimbus/Nimbus/controllers/general" + policy "github.com/5GSEC/nimbus/Nimbus/controllers/policy" + // Importing third-party Kubernetes resource types ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" kubearmorhostpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorHostPolicy/api/security.kubearmor.com/v1" kubearmorpolicyv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorPolicy/api/security.kubearmor.com/v1" @@ -39,9 +46,7 @@ var ( func init() { // In init, various Kubernetes and custom resources are added to the scheme. utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(intentv1.AddToScheme(scheme)) - utilruntime.Must(kubearmorpolicyv1.AddToScheme(scheme)) utilruntime.Must(kubearmorhostpolicyv1.AddToScheme(scheme)) utilruntime.Must(ciliumv2.AddToScheme(scheme)) @@ -49,7 +54,7 @@ func init() { } func main() { - // Flags for the command line parameters like metrics address, leader election, etc. + // Flags for command line parameters such as metrics address, leader election, etc. var metricsAddr string var enableLeaderElection bool var probeAddr string @@ -88,14 +93,14 @@ func main() { // LeaderElectionReleaseOnCancel: true, }) if err != nil { - setupLog.Error(err, "unable to start manager") + setupLog.Error(err, "Unable to start manager") os.Exit(1) } // Setting up the GeneralController and PolicyController. generalController, err := general.NewGeneralController(mgr.GetClient()) if err != nil { - setupLog.Error(err, "unable to create GeneralController") + setupLog.Error(err, "Unable to create GeneralController") os.Exit(1) } @@ -106,27 +111,61 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), GeneralController: generalController, - PolicyController: policyController, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "SecurityIntent") + setupLog.Error(err, "Unable to create controller", "controller", "SecurityIntent") os.Exit(1) } + if err = (&controllers.SecurityIntentBindingReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + GeneralController: generalController, + PolicyController: policyController, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "Unable to create controller", "controller", "SecurityIntentBinding") + os.Exit(1) + } //+kubebuilder:scaffold:builder + // Setting up health checks for the manager. if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") + setupLog.Error(err, "Unable to set up health check") os.Exit(1) } if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") + setupLog.Error(err, "Unable to set up ready check") os.Exit(1) } - // Starting the manager. - setupLog.Info("starting manager") + // Creating channels for handling termination signals. + sigs := make(chan os.Signal, 1) + cleanupDone := make(chan bool) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + // Separate goroutine to wait for termination signal. + go func() { + <-sigs // Waiting for termination signal + setupLog.Info("Received termination signal, performing cleanup...") + + // Calling the Cleanup function + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + err := cleanup.Cleanup(ctx, mgr.GetClient(), setupLog) + + if err != nil { + setupLog.Error(err, "Cleanup failed") + } + + cleanupDone <- true // Signaling cleanup completion + }() + + // Starting the manager + setupLog.Info("Starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") + setupLog.Error(err, "Problem running manager") os.Exit(1) } + + <-cleanupDone // Waiting for cleanup completion + setupLog.Info("Cleanup completed") } diff --git a/Nimbus/config/crd/bases/intent.security.nimbus.com_securityintents.yaml b/Nimbus/config/crd/bases/intent.security.nimbus.com_securityintents.yaml new file mode 100644 index 00000000..0eb4c348 --- /dev/null +++ b/Nimbus/config/crd/bases/intent.security.nimbus.com_securityintents.yaml @@ -0,0 +1,196 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: securityintents.intent.security.nimbus.com +spec: + group: intent.security.nimbus.com + names: + kind: SecurityIntent + listKind: SecurityIntentList + plural: securityintents + singular: securityintent + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: SecurityIntent is the Schema for the securityintents API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SecurityIntentSpec defines the desired state of SecurityIntent + properties: + intent: + description: Intent defines the security policy details + properties: + action: + type: string + mode: + type: string + resource: + items: + description: Resource defines the resources that the security + policy applies to + properties: + attrs: + items: + type: string + type: array + key: + type: string + val: + items: + type: string + type: array + valcel: + type: string + type: object + type: array + type: + type: string + required: + - action + - mode + - resource + - type + type: object + selector: + description: Selector defines the selection criteria for resources + properties: + cel: + items: + type: string + type: array + match: + description: Match defines the resource filters to be used + properties: + all: + items: + description: ResourceFilter is used for filtering resources, + subjects, roles, and cluster roles + properties: + resources: + description: Resources defines the properties for selecting + Kubernetes resources + properties: + kinds: + items: + type: string + type: array + matchLabels: + additionalProperties: + type: string + type: object + names: + items: + type: string + type: array + namespaces: + items: + type: string + type: array + operations: + items: + type: string + type: array + required: + - kinds + type: object + roles: + items: + type: string + type: array + subjects: + items: + description: Subject defines the subject for filtering + properties: + kind: + type: string + name: + type: string + required: + - kind + type: object + type: array + type: object + type: array + any: + items: + description: ResourceFilter is used for filtering resources, + subjects, roles, and cluster roles + properties: + resources: + description: Resources defines the properties for selecting + Kubernetes resources + properties: + kinds: + items: + type: string + type: array + matchLabels: + additionalProperties: + type: string + type: object + names: + items: + type: string + type: array + namespaces: + items: + type: string + type: array + operations: + items: + type: string + type: array + required: + - kinds + type: object + roles: + items: + type: string + type: array + subjects: + items: + description: Subject defines the subject for filtering + properties: + kind: + type: string + name: + type: string + required: + - kind + type: object + type: array + type: object + type: array + type: object + required: + - cel + type: object + required: + - intent + - selector + type: object + status: + description: SecurityIntentStatus defines the observed state of SecurityIntent + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/Nimbus/config/crd/kustomization.yaml similarity index 100% rename from config/crd/kustomization.yaml rename to Nimbus/config/crd/kustomization.yaml diff --git a/config/crd/kustomizeconfig.yaml b/Nimbus/config/crd/kustomizeconfig.yaml similarity index 100% rename from config/crd/kustomizeconfig.yaml rename to Nimbus/config/crd/kustomizeconfig.yaml diff --git a/config/default/kustomization.yaml b/Nimbus/config/default/kustomization.yaml similarity index 100% rename from config/default/kustomization.yaml rename to Nimbus/config/default/kustomization.yaml diff --git a/config/default/manager_auth_proxy_patch.yaml b/Nimbus/config/default/manager_auth_proxy_patch.yaml similarity index 100% rename from config/default/manager_auth_proxy_patch.yaml rename to Nimbus/config/default/manager_auth_proxy_patch.yaml diff --git a/config/default/manager_config_patch.yaml b/Nimbus/config/default/manager_config_patch.yaml similarity index 100% rename from config/default/manager_config_patch.yaml rename to Nimbus/config/default/manager_config_patch.yaml diff --git a/config/manager/kustomization.yaml b/Nimbus/config/manager/kustomization.yaml similarity index 100% rename from config/manager/kustomization.yaml rename to Nimbus/config/manager/kustomization.yaml diff --git a/config/manager/manager.yaml b/Nimbus/config/manager/manager.yaml similarity index 100% rename from config/manager/manager.yaml rename to Nimbus/config/manager/manager.yaml diff --git a/config/prometheus/kustomization.yaml b/Nimbus/config/prometheus/kustomization.yaml similarity index 100% rename from config/prometheus/kustomization.yaml rename to Nimbus/config/prometheus/kustomization.yaml diff --git a/config/prometheus/monitor.yaml b/Nimbus/config/prometheus/monitor.yaml similarity index 100% rename from config/prometheus/monitor.yaml rename to Nimbus/config/prometheus/monitor.yaml diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/Nimbus/config/rbac/auth_proxy_client_clusterrole.yaml similarity index 100% rename from config/rbac/auth_proxy_client_clusterrole.yaml rename to Nimbus/config/rbac/auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/auth_proxy_role.yaml b/Nimbus/config/rbac/auth_proxy_role.yaml similarity index 100% rename from config/rbac/auth_proxy_role.yaml rename to Nimbus/config/rbac/auth_proxy_role.yaml diff --git a/config/rbac/auth_proxy_role_binding.yaml b/Nimbus/config/rbac/auth_proxy_role_binding.yaml similarity index 100% rename from config/rbac/auth_proxy_role_binding.yaml rename to Nimbus/config/rbac/auth_proxy_role_binding.yaml diff --git a/config/rbac/auth_proxy_service.yaml b/Nimbus/config/rbac/auth_proxy_service.yaml similarity index 100% rename from config/rbac/auth_proxy_service.yaml rename to Nimbus/config/rbac/auth_proxy_service.yaml diff --git a/config/rbac/kustomization.yaml b/Nimbus/config/rbac/kustomization.yaml similarity index 100% rename from config/rbac/kustomization.yaml rename to Nimbus/config/rbac/kustomization.yaml diff --git a/config/rbac/leader_election_role.yaml b/Nimbus/config/rbac/leader_election_role.yaml similarity index 100% rename from config/rbac/leader_election_role.yaml rename to Nimbus/config/rbac/leader_election_role.yaml diff --git a/config/rbac/leader_election_role_binding.yaml b/Nimbus/config/rbac/leader_election_role_binding.yaml similarity index 100% rename from config/rbac/leader_election_role_binding.yaml rename to Nimbus/config/rbac/leader_election_role_binding.yaml diff --git a/config/rbac/role.yaml b/Nimbus/config/rbac/role.yaml similarity index 100% rename from config/rbac/role.yaml rename to Nimbus/config/rbac/role.yaml diff --git a/config/rbac/role_binding.yaml b/Nimbus/config/rbac/role_binding.yaml similarity index 100% rename from config/rbac/role_binding.yaml rename to Nimbus/config/rbac/role_binding.yaml diff --git a/config/rbac/securityintent_editor_role.yaml b/Nimbus/config/rbac/securityintent_editor_role.yaml similarity index 100% rename from config/rbac/securityintent_editor_role.yaml rename to Nimbus/config/rbac/securityintent_editor_role.yaml diff --git a/config/rbac/securityintent_viewer_role.yaml b/Nimbus/config/rbac/securityintent_viewer_role.yaml similarity index 100% rename from config/rbac/securityintent_viewer_role.yaml rename to Nimbus/config/rbac/securityintent_viewer_role.yaml diff --git a/config/rbac/service_account.yaml b/Nimbus/config/rbac/service_account.yaml similarity index 100% rename from config/rbac/service_account.yaml rename to Nimbus/config/rbac/service_account.yaml diff --git a/config/samples/intent_v1_securityintent.yaml b/Nimbus/config/samples/intent_v1_securityintent.yaml similarity index 100% rename from config/samples/intent_v1_securityintent.yaml rename to Nimbus/config/samples/intent_v1_securityintent.yaml diff --git a/config/samples/kustomization.yaml b/Nimbus/config/samples/kustomization.yaml similarity index 100% rename from config/samples/kustomization.yaml rename to Nimbus/config/samples/kustomization.yaml diff --git a/hack/boilerplate.go.txt b/Nimbus/hack/boilerplate.go.txt similarity index 100% rename from hack/boilerplate.go.txt rename to Nimbus/hack/boilerplate.go.txt diff --git a/api/v1/securityintent_types.go b/api/v1/securityintent_types.go deleted file mode 100644 index 7938246f..00000000 --- a/api/v1/securityintent_types.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -package v1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// SecurityIntentSpec defines the desired state of SecurityIntent -type SecurityIntentSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - - Selector Selector `json:"selector"` // Define criteria for selecting resources - Intent Intent `json:"intent"` // Define the details of the security policy. -} - -// Selector defines the selection criteria for resources -type Selector struct { - Match Match `json:"match,omitempty"` // Define the resource filter to be used - CEL []string `json:"cel"` // Define filter conditions as CEL expressions -} - -// Match defines the resource filters to be used -type Match struct { - Any []ResourceFilter `json:"any,omitempty"` // Apply when one or more conditions match - All []ResourceFilter `json:"all,omitempty"` //Apply when all conditions must match -} - -// ResourceFilter is used for filtering resources, subjects, roles, and cluster roles -type ResourceFilter struct { - Resources Resources `json:"resources,omitempty"` // Define properties to select k8s resources - Subjects []Subject `json:"subjects,omitempty"` // Define the subjects to filter - Roles []string `json:"roles,omitempty"` // Define the roles to filter. -} - -// Resources defines the properties for selecting Kubernetes resources -type Resources struct { - Names []string `json:"names,omitempty"` // Define the resource name - Namespaces []string `json:"namespaces,omitempty"` // Define the namespaces to which the resource belongs - Kinds []string `json:"kinds"` // Define resource kinds - Operations []string `json:"operations,omitempty"` // Define operations for the resource - - MatchLabels map[string]string `json:"matchLabels,omitempty"` // Define labels to apply to the resource -} - -// Subject defines the subject for filtering -type Subject struct { - Kind string `json:"kind"` // Define the kind of policy - Name string `json:"name,omitempty"` // Define the name of the policy -} - -// Intent defines the security policy details -type Intent struct { - Action string `json:"action"` // Define the action of the policy - Mode string `json:"mode"` // Defines the mode of the policy - Type string `json:"type"` // Defines the type of the policy - Resource []Resource `json:"resource"` // Define the resource to which the security policy applies -} - -// Resource defines the resources that the security policy applies to -type Resource struct { - Key string `json:"key,omitempty"` // Define a resource key - Val []string `json:"val,omitempty"` // Define a resource value list - Valcel string `json:"valcel,omitempty"` // Define a CEL expression - Attrs []string `json:"attrs,omitempty"` // Define additional attributes -} - -// SecurityIntentStatus defines the observed state of SecurityIntent -type SecurityIntentStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - // This field can be updated to reflect the actual status of the application of the security intents -} - -// SecurityIntent is the Schema for the securityintents API -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SecurityIntent is the Schema for the securityintents API -type SecurityIntent struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec SecurityIntentSpec `json:"spec,omitempty"` - Status SecurityIntentStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// SecurityIntentList contains a list of SecurityIntent -type SecurityIntentList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []SecurityIntent `json:"items"` -} - -func init() { - SchemeBuilder.Register(&SecurityIntent{}, &SecurityIntentList{}) -} diff --git a/controllers/general/general_controller.go b/controllers/general/general_controller.go deleted file mode 100644 index a188c2d4..00000000 --- a/controllers/general/general_controller.go +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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 deleted file mode 100644 index ab6a207b..00000000 --- a/controllers/general/watch_intent.go +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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 deleted file mode 100644 index 897bab7a..00000000 --- a/controllers/policy/network_policy.go +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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. - -// 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 deleted file mode 100644 index 36642fb6..00000000 --- a/controllers/policy/policy_controller.go +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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 deleted file mode 100644 index 11ac44f0..00000000 --- a/controllers/policy/system_policy.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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 deleted file mode 100644 index c6008ee1..00000000 --- a/controllers/securityintent_controller.go +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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/controllers/suite_test.go b/controllers/suite_test.go deleted file mode 100644 index 846d176e..00000000 --- a/controllers/suite_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -package controllers - -import ( - "fmt" - "path/filepath" - "runtime" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - intentv1 "github.com/5GSEC/nimbus/api/v1" - //+kubebuilder:scaffold:imports -) - -// 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 // 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) // Register a Ginkgo fail handler. - - RunSpecs(t, "Controller Suite") // Run the Ginkgo specs for the 'Controller Suite'. -} - -var _ = BeforeSuite(func() { - // 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, - - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", - fmt.Sprintf("1.28.3-%s-%s", runtime.GOOS, runtime.GOARCH)), - } - - var err error - 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()) // Assert that adding the scheme does not produce an error. - - // 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()) // 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() // 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 deleted file mode 100644 index 34f73d4c..00000000 --- a/controllers/utils/utils_policy.go +++ /dev/null @@ -1,618 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Authors of Nimbus - -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/test-yaml/env/busybox-pod.yaml b/test/env/busybox-pod.yaml similarity index 100% rename from test-yaml/env/busybox-pod.yaml rename to test/env/busybox-pod.yaml diff --git a/test-yaml/env/multiubuntu.yaml b/test/env/multiubuntu.yaml similarity index 100% rename from test-yaml/env/multiubuntu.yaml rename to test/env/multiubuntu.yaml diff --git a/test-yaml/env/redis-pod.yaml b/test/env/redis-pod.yaml similarity index 100% rename from test-yaml/env/redis-pod.yaml rename to test/env/redis-pod.yaml diff --git a/test-yaml/intents/network/intent-net-icmp-audit.yaml b/test/v1/intents/network/intent-net-icmp-audit.yaml similarity index 100% rename from test-yaml/intents/network/intent-net-icmp-audit.yaml rename to test/v1/intents/network/intent-net-icmp-audit.yaml diff --git a/test-yaml/intents/network/intent-network-sample.yaml b/test/v1/intents/network/intent-network-sample.yaml similarity index 100% rename from test-yaml/intents/network/intent-network-sample.yaml rename to test/v1/intents/network/intent-network-sample.yaml diff --git a/test-yaml/intents/network/intent-redis.yaml b/test/v1/intents/network/intent-redis.yaml similarity index 100% rename from test-yaml/intents/network/intent-redis.yaml rename to test/v1/intents/network/intent-redis.yaml diff --git a/test-yaml/intents/network/intent-risky-network-access.yaml b/test/v1/intents/network/intent-risky-network-access.yaml similarity index 100% rename from test-yaml/intents/network/intent-risky-network-access.yaml rename to test/v1/intents/network/intent-risky-network-access.yaml diff --git a/test-yaml/intents/system/intent-accessd-shadow-file.yaml b/test/v1/intents/system/intent-accessd-shadow-file.yaml similarity index 100% rename from test-yaml/intents/system/intent-accessd-shadow-file.yaml rename to test/v1/intents/system/intent-accessd-shadow-file.yaml diff --git a/test-yaml/intents/system/intent-allow-access-to-credentials-dir.yaml b/test/v1/intents/system/intent-allow-access-to-credentials-dir.yaml similarity index 100% rename from test-yaml/intents/system/intent-allow-access-to-credentials-dir.yaml rename to test/v1/intents/system/intent-allow-access-to-credentials-dir.yaml diff --git a/test-yaml/intents/system/intent-bug-block.yaml b/test/v1/intents/system/intent-bug-block.yaml similarity index 100% rename from test-yaml/intents/system/intent-bug-block.yaml rename to test/v1/intents/system/intent-bug-block.yaml diff --git a/test-yaml/intents/system/intent-cap-net-raw-block.yaml b/test/v1/intents/system/intent-cap-net-raw-block.yaml similarity index 100% rename from test-yaml/intents/system/intent-cap-net-raw-block.yaml rename to test/v1/intents/system/intent-cap-net-raw-block.yaml diff --git a/test-yaml/intents/system/intent-do-not-allow-priv-escalation.yaml b/test/v1/intents/system/intent-do-not-allow-priv-escalation.yaml similarity index 100% rename from test-yaml/intents/system/intent-do-not-allow-priv-escalation.yaml rename to test/v1/intents/system/intent-do-not-allow-priv-escalation.yaml diff --git a/test-yaml/intents/system/intent-path-block.yaml b/test/v1/intents/system/intent-path-block.yaml similarity index 100% rename from test-yaml/intents/system/intent-path-block.yaml rename to test/v1/intents/system/intent-path-block.yaml diff --git a/test-yaml/intents/system/intent-restrict-write-access-to-sys-folders.yaml b/test/v1/intents/system/intent-restrict-write-access-to-sys-folders.yaml similarity index 100% rename from test-yaml/intents/system/intent-restrict-write-access-to-sys-folders.yaml rename to test/v1/intents/system/intent-restrict-write-access-to-sys-folders.yaml diff --git a/test-yaml/intents/template-intent.yaml b/test/v1/intents/template-intent.yaml similarity index 100% rename from test-yaml/intents/template-intent.yaml rename to test/v1/intents/template-intent.yaml diff --git a/test-yaml/policy/cnp/policy-redis.yaml b/test/v1/policy/cnp/policy-redis.yaml similarity index 100% rename from test-yaml/policy/cnp/policy-redis.yaml rename to test/v1/policy/cnp/policy-redis.yaml diff --git a/test-yaml/policy/cnp/policy-risky.yaml b/test/v1/policy/cnp/policy-risky.yaml similarity index 100% rename from test-yaml/policy/cnp/policy-risky.yaml rename to test/v1/policy/cnp/policy-risky.yaml diff --git a/test-yaml/policy/hsp/policy-accessed-shadow-file.yaml b/test/v1/policy/hsp/policy-accessed-shadow-file.yaml similarity index 100% rename from test-yaml/policy/hsp/policy-accessed-shadow-file.yaml rename to test/v1/policy/hsp/policy-accessed-shadow-file.yaml diff --git a/test-yaml/policy/hsp/policy-bug-block.yaml b/test/v1/policy/hsp/policy-bug-block.yaml similarity index 100% rename from test-yaml/policy/hsp/policy-bug-block.yaml rename to test/v1/policy/hsp/policy-bug-block.yaml diff --git a/test-yaml/policy/ksp/policy-audit-all-unlink.yaml b/test/v1/policy/ksp/policy-audit-all-unlink.yaml similarity index 100% rename from test-yaml/policy/ksp/policy-audit-all-unlink.yaml rename to test/v1/policy/ksp/policy-audit-all-unlink.yaml diff --git a/test-yaml/policy/ksp/policy-cap-net-raw-block.yaml b/test/v1/policy/ksp/policy-cap-net-raw-block.yaml similarity index 100% rename from test-yaml/policy/ksp/policy-cap-net-raw-block.yaml rename to test/v1/policy/ksp/policy-cap-net-raw-block.yaml diff --git a/test-yaml/policy/ksp/policy-file-dir-allow-from-source-path.yaml b/test/v1/policy/ksp/policy-file-dir-allow-from-source-path.yaml similarity index 100% rename from test-yaml/policy/ksp/policy-file-dir-allow-from-source-path.yaml rename to test/v1/policy/ksp/policy-file-dir-allow-from-source-path.yaml diff --git a/test-yaml/policy/ksp/policy-net-icmp-audit.yaml b/test/v1/policy/ksp/policy-net-icmp-audit.yaml similarity index 100% rename from test-yaml/policy/ksp/policy-net-icmp-audit.yaml rename to test/v1/policy/ksp/policy-net-icmp-audit.yaml diff --git a/test-yaml/policy/ksp/policy-path-block.yaml b/test/v1/policy/ksp/policy-path-block.yaml similarity index 100% rename from test-yaml/policy/ksp/policy-path-block.yaml rename to test/v1/policy/ksp/policy-path-block.yaml diff --git a/test-yaml/policy/ksp/policy-proc-dir-block.yaml b/test/v1/policy/ksp/policy-proc-dir-block.yaml similarity index 100% rename from test-yaml/policy/ksp/policy-proc-dir-block.yaml rename to test/v1/policy/ksp/policy-proc-dir-block.yaml