Skip to content

Commit

Permalink
Merge pull request #73 from anurag-rajawat/feat-si-ops
Browse files Browse the repository at this point in the history
fix(core): Handle SecurityIntent update and deletion
  • Loading branch information
seungsoo-lee authored Mar 5, 2024
2 parents fcd5679 + 4e5b0a8 commit 359d519
Show file tree
Hide file tree
Showing 37 changed files with 1,208 additions and 151 deletions.
196 changes: 165 additions & 31 deletions internal/controller/clustersecurityintentbinding_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package controller

import (
"context"
"errors"

"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -15,10 +16,13 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

v1 "github.com/5GSEC/nimbus/api/v1"
processorerrors "github.com/5GSEC/nimbus/pkg/processor/errors"
"github.com/5GSEC/nimbus/pkg/processor/policybuilder"
)

Expand All @@ -43,21 +47,27 @@ func (r *ClusterSecurityIntentBindingReconciler) Reconcile(ctx context.Context,
if err != nil {
if apierrors.IsNotFound(err) {
logger.Info("ClusterSecurityIntentBinding not found. Ignoring since object must be deleted")
logger.Info("ClusterNimbusPolicy deleted due to ClusterSecurityIntentBinding deletion",
"ClusterNimbusPolicy.Name", req.Name, "ClusterSecurityIntentBinding.Name", req.Name,
)
return doNotRequeue()
}
logger.Error(err, "failed to get ClusterSecurityIntentBinding", "ClusterSecurityIntentBinding.Name", csib.Name)
return requeueWithError(err)
}
logger.Info("ClusterSecurityIntentBinding found", "ClusterSecurityIntentBinding.Name", req.Name)

if csib.GetGeneration() == 1 {
logger.Info("ClusterSecurityIntentBinding found", "ClusterSecurityIntentBinding.Name", req.Name)
} else {
logger.Info("ClusterSecurityIntentBinding configured", "ClusterSecurityIntentBinding.Name", req.Name)
}

if err = r.updateCsibStatus(ctx, logger, req); err != nil {
return requeueWithError(err)
}

if err = r.createOrUpdateCwnp(ctx, logger, req); err != nil {
return requeueWithError(err)
}

if err = r.updateStatus(ctx, logger, req); err != nil {
if err = r.updateCSibStatusWithBoundSisAndCwnpInfo(ctx, logger, req); err != nil {
return requeueWithError(err)
}

Expand All @@ -76,6 +86,9 @@ func (r *ClusterSecurityIntentBindingReconciler) SetupWithManager(mgr ctrl.Manag
DeleteFunc: r.deleteFn,
},
).
Watches(&v1.SecurityIntent{},
handler.EnqueueRequestsFromMapFunc(r.findCsibsForSi),
).
Complete(r)
}

Expand All @@ -97,6 +110,9 @@ func (r *ClusterSecurityIntentBindingReconciler) deleteFn(deleteEvent event.Dele
if _, ok := obj.(*v1.ClusterSecurityIntentBinding); ok {
return true
}
if _, ok := obj.(*v1.SecurityIntent); ok {
return true
}
return ownerExists(r.Client, obj)
}

Expand Down Expand Up @@ -124,6 +140,14 @@ func (r *ClusterSecurityIntentBindingReconciler) createOrUpdateCwnp(ctx context.
func (r *ClusterSecurityIntentBindingReconciler) createCwnp(ctx context.Context, logger logr.Logger, csib v1.ClusterSecurityIntentBinding) error {
clusterNp, err := policybuilder.BuildClusterNimbusPolicy(ctx, logger, r.Client, r.Scheme, csib)
if err != nil {
if errors.Is(err, processorerrors.ErrSecurityIntentsNotFound) {
// Since the SecurityIntent(s) referenced in ClusterSecurityIntentBinding spec do not
// exist, so delete ClusterNimbusPolicy if it exists.
if err := r.deleteCwnp(ctx, csib.GetName()); err != nil {
return err
}
return nil
}
logger.Error(err, "failed to build ClusterNimbusPolicy")
return err
}
Expand All @@ -134,7 +158,11 @@ func (r *ClusterSecurityIntentBindingReconciler) createCwnp(ctx context.Context,
}
logger.Info("ClusterNimbusPolicy created", "ClusterNimbusPolicy.Name", clusterNp.Name)

return nil
return r.updateCwnpStatus(ctx, logger, ctrl.Request{
NamespacedName: types.NamespacedName{
Name: csib.Name,
}},
)
}

func (r *ClusterSecurityIntentBindingReconciler) updateCwnp(ctx context.Context, logger logr.Logger, csib v1.ClusterSecurityIntentBinding) error {
Expand All @@ -146,6 +174,14 @@ func (r *ClusterSecurityIntentBindingReconciler) updateCwnp(ctx context.Context,

clusterNp, err := policybuilder.BuildClusterNimbusPolicy(ctx, logger, r.Client, r.Scheme, csib)
if err != nil {
if errors.Is(err, processorerrors.ErrSecurityIntentsNotFound) {
// Since the SecurityIntent(s) referenced in ClusterSecurityIntentBinding spec do not
// exist, so delete ClusterNimbusPolicy if it exists.
if err := r.deleteCwnp(ctx, csib.GetName()); err != nil {
return err
}
return nil
}
logger.Error(err, "failed to build ClusterNimbusPolicy")
return err
}
Expand All @@ -157,18 +193,74 @@ func (r *ClusterSecurityIntentBindingReconciler) updateCwnp(ctx context.Context,
}
logger.Info("ClusterNimbusPolicy configured", "ClusterNimbusPolicy.Name", clusterNp.Name)

return r.updateCwnpStatus(ctx, logger, ctrl.Request{
NamespacedName: types.NamespacedName{
Name: csib.Name,
}},
)
}

func (r *ClusterSecurityIntentBindingReconciler) findCsibsForSi(ctx context.Context, si client.Object) []reconcile.Request {
logger := log.FromContext(ctx)

csibs := &v1.ClusterSecurityIntentBindingList{}
if err := r.List(ctx, csibs); err != nil {
logger.Error(err, "failed to list ClusterSecurityIntentBindings")
return []reconcile.Request{}
}

requests := make([]reconcile.Request, len(csibs.Items))

for idx, csib := range csibs.Items {
for _, intent := range csib.Spec.Intents {
if intent.Name == si.GetName() {
requests[idx] = ctrl.Request{
NamespacedName: types.NamespacedName{
Namespace: csib.GetNamespace(),
Name: csib.GetName(),
},
}
break
}
}
}

return requests
}

func (r *ClusterSecurityIntentBindingReconciler) updateCsibStatus(ctx context.Context, logger logr.Logger, req ctrl.Request) error {
if retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
latestCsib := &v1.ClusterSecurityIntentBinding{}
if err := r.Get(ctx, req.NamespacedName, latestCsib); err != nil && !apierrors.IsNotFound(err) {
logger.Error(err, "failed to fetch ClusterSecurityIntentBinding", "clusterSecurityIntentBindingName", req.Name)
return err
}

latestCsib.Status.Status = StatusCreated
latestCsib.Status.LastUpdated = metav1.Now()
if err := r.Status().Update(ctx, latestCsib); err != nil {
return err
}

return nil
}); retryErr != nil {
logger.Error(retryErr, "failed to update ClusterSecurityIntentBinding status", "ClusterSecurityIntentBinding.Name", req.Name)
return retryErr
}

return nil
}

func (r *ClusterSecurityIntentBindingReconciler) updateStatus(ctx context.Context, logger logr.Logger, req ctrl.Request) error {
// To handle potential latency issues with the Kubernetes API server, we
// implement an exponential backoff strategy when fetching the ClusterNimbusPolicy
// custom resource. This enhances resilience by retrying failed requests with
// increasing intervals, preventing excessive retries in case of persistent 'Not
// Found' errors.
func (r *ClusterSecurityIntentBindingReconciler) updateCwnpStatus(ctx context.Context, logger logr.Logger, req ctrl.Request) error {
cwnp := &v1.ClusterNimbusPolicy{}

// To handle potential latency or outdated cache issues with the Kubernetes API
// server, we implement an exponential backoff strategy when fetching the
// ClusterNimbusPolicy custom resource. This enhances resilience by retrying
// failed requests with increasing intervals, preventing excessive retries in
// case of persistent 'Not Found' errors.
if retryErr := retry.OnError(retry.DefaultRetry, apierrors.IsNotFound, func() error {
np := &v1.ClusterNimbusPolicy{}
if err := r.Get(ctx, req.NamespacedName, np); err != nil {
if err := r.Get(ctx, req.NamespacedName, cwnp); err != nil {
return err
}
return nil
Expand All @@ -184,15 +276,12 @@ func (r *ClusterSecurityIntentBindingReconciler) updateStatus(ctx context.Contex
// potential issues while preventing indefinite retries in case of persistent
// conflicts.
if retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
latestCwnp := &v1.ClusterNimbusPolicy{}
if err := r.Get(ctx, req.NamespacedName, latestCwnp); err != nil {
if err := r.Get(ctx, req.NamespacedName, cwnp); err != nil {
return err
}
latestCwnp.Status = v1.ClusterNimbusPolicyStatus{
Status: StatusCreated,
LastUpdated: metav1.Now(),
}
if err := r.Status().Update(ctx, latestCwnp); err != nil {
cwnp.Status.Status = StatusCreated
cwnp.Status.LastUpdated = metav1.Now()
if err := r.Status().Update(ctx, cwnp); err != nil {
return err
}
return nil
Expand All @@ -201,21 +290,66 @@ func (r *ClusterSecurityIntentBindingReconciler) updateStatus(ctx context.Contex
return retryErr
}

// Fetch the latest SecurityIntentBinding so that we have the latest state
// on the cluster.
return nil
}

func (r *ClusterSecurityIntentBindingReconciler) deleteCwnp(ctx context.Context, name string) error {
logger := log.FromContext(ctx)

var cwnp v1.ClusterNimbusPolicy
err := r.Get(ctx, types.NamespacedName{Name: name}, &cwnp)
if err != nil {
if apierrors.IsNotFound(err) {
return nil
}
return err
}

logger.Info("Deleting ClusterNimbusPolicy since no SecurityIntents found", "clusterNimbusPolicyName", name)
logger.Info("ClusterNimbusPolicy deleted", "clusterNimbusPolicyName", name)
if err = r.Delete(context.Background(), &cwnp); err != nil {
logger.Error(err, "failed to delete ClusterNimbusPolicy", "clusterNimbusPolicyName", name)
return err
}

return nil
}

func (r *ClusterSecurityIntentBindingReconciler) updateCSibStatusWithBoundSisAndCwnpInfo(ctx context.Context, logger logr.Logger, req ctrl.Request) error {
latestCsib := &v1.ClusterSecurityIntentBinding{}
if err := r.Get(ctx, req.NamespacedName, latestCsib); err != nil {
if err := r.Get(ctx, req.NamespacedName, latestCsib); err != nil && !apierrors.IsNotFound(err) {
logger.Error(err, "failed to fetch ClusterSecurityIntentBinding", "ClusterSecurityIntentBinding.Name", req.Name)
return err
}
count, boundIntents := extractBoundIntentsInfo(latestCsib.Spec.Intents)
latestCsib.Status = v1.ClusterSecurityIntentBindingStatus{
Status: StatusCreated,
LastUpdated: metav1.Now(),
NumberOfBoundIntents: count,
BoundIntents: boundIntents,
ClusterNimbusPolicy: req.Name,

latestCwnp := &v1.ClusterNimbusPolicy{}
if retryErr := retry.OnError(retry.DefaultRetry, apierrors.IsNotFound, func() error {
if err := r.Get(ctx, req.NamespacedName, latestCwnp); err != nil {
return err
}
return nil
}); retryErr != nil {
if !apierrors.IsNotFound(retryErr) {
logger.Error(retryErr, "failed to fetch ClusterNimbusPolicy", "ClusterNimbusPolicy.Name", req.Name)
return retryErr
}

// Remove outdated SecurityIntent(s) and ClusterNimbusPolicy info
latestCsib.Status.NumberOfBoundIntents = 0
latestCsib.Status.BoundIntents = nil
latestCsib.Status.ClusterNimbusPolicy = ""
if err := r.Status().Update(ctx, latestCsib); err != nil {
logger.Error(err, "failed to update ClusterSecurityIntentBinding status", "ClusterSecurityIntentBinding.Name", latestCsib.Name)
return err
}
return nil
}

// Update ClusterSecurityIntentBinding status with bound SecurityIntent(s) and NimbusPolicy.
latestCsib.Status.NumberOfBoundIntents = int32(len(latestCwnp.Spec.NimbusRules))
latestCsib.Status.BoundIntents = extractBoundIntentsNameFromCSib(ctx, r.Client, req.Name)
latestCsib.Status.ClusterNimbusPolicy = req.Name

if err := r.Status().Update(ctx, latestCsib); err != nil {
logger.Error(err, "failed to update ClusterSecurityIntentBinding status", "ClusterSecurityIntentBinding.Name", latestCsib.Name)
return err
Expand Down
7 changes: 6 additions & 1 deletion internal/controller/securityintent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ func (r *SecurityIntentReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return requeueWithError(err)
}

if si.GetGeneration() == 1 {
logger.Info("SecurityIntent found", "SecurityIntent.Name", si.Name)
} else {
logger.Info("SecurityIntent configured", "SecurityIntent.Name", si.Name)
}

if err = r.updateStatus(ctx, req.Name); err != nil {
logger.Error(err, "failed to update SecurityIntent status", "SecurityIntent.Name", req.Name)
return requeueWithError(err)
}
logger.Info("SecurityIntent found", "SecurityIntent.Name", si.Name)

return doNotRequeue()
}
Expand Down
Loading

0 comments on commit 359d519

Please sign in to comment.