github.com/cilium/cilium@v1.16.2/pkg/policy/groups/actions.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package groups
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/sirupsen/logrus"
    10  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  
    12  	"github.com/cilium/cilium/pkg/controller"
    13  	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    14  	"github.com/cilium/cilium/pkg/k8s/client"
    15  	"github.com/cilium/cilium/pkg/logging/logfields"
    16  	"github.com/cilium/cilium/pkg/metrics"
    17  )
    18  
    19  var (
    20  	controllerManager = controller.NewManager()
    21  
    22  	addDerivativeCNPControllerGroup    = controller.NewGroup("add-derivative-cilium-network-policy")
    23  	updateDerivativeCNPControllerGroup = controller.NewGroup("update-derivative-cilium-network-policy")
    24  	deleteDerivativeCNPControllerGroup = controller.NewGroup("delete-derivative-cilium-network-policy")
    25  
    26  	addDerivativeCCNPControllerGroup    = controller.NewGroup("add-derivative-clusterwide-cilium-network-policy")
    27  	updateDerivativeCCNPControllerGroup = controller.NewGroup("update-derivative-clusterwide-cilium-network-policy")
    28  	deleteDerivativeCCNPControllerGroup = controller.NewGroup("delete-derivative-clusterwide-cilium-network-policy")
    29  )
    30  
    31  // AddDerivativeCNPIfNeeded will create a new CNP if the given CNP has any rules
    32  // that need to create a new derivative policy.
    33  // It returns a boolean, true in case that all actions are correct, false if
    34  // something fails.
    35  func AddDerivativeCNPIfNeeded(clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy) bool {
    36  	if !cnp.RequiresDerivative() {
    37  		log.WithFields(logrus.Fields{
    38  			logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name,
    39  			logfields.K8sNamespace:            cnp.ObjectMeta.Namespace,
    40  		}).Debug("CNP does not have derivative policies, skipped")
    41  		return true
    42  	}
    43  	controllerManager.UpdateController(
    44  		"add-derivative-cnp-"+cnp.ObjectMeta.Name,
    45  		controller.ControllerParams{
    46  			Group: addDerivativeCNPControllerGroup,
    47  			DoFunc: func(ctx context.Context) error {
    48  				return addDerivativePolicy(ctx, clientset, cnp, false)
    49  			},
    50  		})
    51  	return true
    52  }
    53  
    54  // AddDerivativeCCNPIfNeeded will create a new CCNP if the given NetworkPolicy has any rules
    55  // that need to create a new derivative policy.
    56  // It returns a boolean, true in case that all actions are correct, false if
    57  // something fails.
    58  func AddDerivativeCCNPIfNeeded(clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy) bool {
    59  	if !cnp.RequiresDerivative() {
    60  		log.WithFields(logrus.Fields{
    61  			logfields.CiliumClusterwideNetworkPolicyName: cnp.ObjectMeta.Name,
    62  		}).Debug("CCNP does not have derivative policies, skipped")
    63  		return true
    64  	}
    65  	controllerManager.UpdateController(
    66  		"add-derivative-ccnp-"+cnp.ObjectMeta.Name,
    67  		controller.ControllerParams{
    68  			Group: addDerivativeCCNPControllerGroup,
    69  			DoFunc: func(ctx context.Context) error {
    70  				return addDerivativePolicy(ctx, clientset, cnp, true)
    71  			},
    72  		})
    73  	return true
    74  }
    75  
    76  // UpdateDerivativeCNPIfNeeded updates or creates a CNP if the given CNP has
    77  // any rule that needs to create a new derivative policy(eg: ToGroups). In case
    78  // that the new CNP does not have any derivative policy and the old one had
    79  // one, it will delete the old policy.
    80  // The function returns true if an update is required for the derivative policy
    81  // and false otherwise.
    82  func UpdateDerivativeCNPIfNeeded(clientset client.Clientset, newCNP *cilium_v2.CiliumNetworkPolicy, oldCNP *cilium_v2.CiliumNetworkPolicy) bool {
    83  	if !newCNP.RequiresDerivative() && oldCNP.RequiresDerivative() {
    84  		log.WithFields(logrus.Fields{
    85  			logfields.CiliumNetworkPolicyName: newCNP.ObjectMeta.Name,
    86  			logfields.K8sNamespace:            newCNP.ObjectMeta.Namespace,
    87  		}).Info("New CNP does not have derivative policy, but old had. Deleting old policies")
    88  
    89  		controllerManager.UpdateController(
    90  			"delete-derivative-cnp-"+oldCNP.ObjectMeta.Name,
    91  			controller.ControllerParams{
    92  				Group: deleteDerivativeCNPControllerGroup,
    93  				DoFunc: func(ctx context.Context) error {
    94  					return DeleteDerivativeCNP(ctx, clientset, oldCNP)
    95  				},
    96  			})
    97  		return false
    98  	}
    99  
   100  	if !newCNP.RequiresDerivative() {
   101  		return false
   102  	}
   103  
   104  	controllerManager.UpdateController(
   105  		"update-derivative-cnp-"+newCNP.ObjectMeta.Name,
   106  		controller.ControllerParams{
   107  			Group: updateDerivativeCNPControllerGroup,
   108  			DoFunc: func(ctx context.Context) error {
   109  				return addDerivativePolicy(ctx, clientset, newCNP, false)
   110  			},
   111  		})
   112  	return true
   113  }
   114  
   115  // UpdateDerivativeCCNPIfNeeded updates or creates a CCNP if the given CCNP has
   116  // any rule that needs to create a new derivative policy(eg: ToGroups). In case
   117  // that the new CCNP does not have any derivative policy and the old one had
   118  // one, it will delete the old policy.
   119  // The function returns true if an update is required for the derivative policy
   120  // and false otherwise.
   121  func UpdateDerivativeCCNPIfNeeded(clientset client.Clientset, newCCNP *cilium_v2.CiliumNetworkPolicy, oldCCNP *cilium_v2.CiliumNetworkPolicy) bool {
   122  	if !newCCNP.RequiresDerivative() && oldCCNP.RequiresDerivative() {
   123  		log.WithFields(logrus.Fields{
   124  			logfields.CiliumClusterwideNetworkPolicyName: newCCNP.ObjectMeta.Name,
   125  		}).Info("New CCNP does not have derivative policy, but old had. Deleting old policies")
   126  
   127  		controllerManager.UpdateController(
   128  			"delete-derivative-ccnp-"+oldCCNP.ObjectMeta.Name,
   129  			controller.ControllerParams{
   130  				Group: deleteDerivativeCCNPControllerGroup,
   131  				DoFunc: func(ctx context.Context) error {
   132  					return DeleteDerivativeCCNP(ctx, clientset, oldCCNP)
   133  				},
   134  			})
   135  		return false
   136  	}
   137  
   138  	if !newCCNP.RequiresDerivative() {
   139  		return false
   140  	}
   141  
   142  	controllerManager.UpdateController(
   143  		"update-derivative-ccnp-"+newCCNP.ObjectMeta.Name,
   144  		controller.ControllerParams{
   145  			Group: updateDerivativeCCNPControllerGroup,
   146  			DoFunc: func(ctx context.Context) error {
   147  				return addDerivativePolicy(ctx, clientset, newCCNP, true)
   148  			},
   149  		})
   150  	return true
   151  }
   152  
   153  // DeleteDerivativeFromCache deletes the given CNP from the groupsCNPCache to
   154  // no continue pooling new data.
   155  func DeleteDerivativeFromCache(cnp *cilium_v2.CiliumNetworkPolicy) {
   156  	groupsCNPCache.DeleteCNP(cnp)
   157  }
   158  
   159  // DeleteDerivativeCNP if the given policy has a derivative constraint,the
   160  // given CNP will be deleted from store and the cache.
   161  func DeleteDerivativeCNP(ctx context.Context, clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy) error {
   162  	scopedLog := log.WithFields(logrus.Fields{
   163  		logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name,
   164  		logfields.K8sNamespace:            cnp.ObjectMeta.Namespace,
   165  	})
   166  
   167  	if !cnp.RequiresDerivative() {
   168  		scopedLog.Debug("CNP does not have derivative policies, skipped")
   169  		return nil
   170  	}
   171  
   172  	err := clientset.CiliumV2().CiliumNetworkPolicies(cnp.ObjectMeta.Namespace).DeleteCollection(
   173  		ctx,
   174  		v1.DeleteOptions{},
   175  		v1.ListOptions{LabelSelector: parentCNP + "=" + string(cnp.ObjectMeta.UID)})
   176  
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	DeleteDerivativeFromCache(cnp)
   182  	return nil
   183  }
   184  
   185  // DeleteDerivativeCCNP if the given policy has a derivative constraint, the
   186  // given CCNP will be deleted from store and the cache.
   187  func DeleteDerivativeCCNP(ctx context.Context, clientset client.Clientset, ccnp *cilium_v2.CiliumNetworkPolicy) error {
   188  	scopedLog := log.WithFields(logrus.Fields{
   189  		logfields.CiliumClusterwideNetworkPolicyName: ccnp.ObjectMeta.Name,
   190  	})
   191  
   192  	if !ccnp.RequiresDerivative() {
   193  		scopedLog.Debug("CCNP does not have derivative policies, skipped")
   194  		return nil
   195  	}
   196  
   197  	err := clientset.CiliumV2().CiliumClusterwideNetworkPolicies().DeleteCollection(
   198  		ctx,
   199  		v1.DeleteOptions{},
   200  		v1.ListOptions{LabelSelector: parentCNP + "=" + string(ccnp.ObjectMeta.UID)})
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	DeleteDerivativeFromCache(ccnp)
   206  	return nil
   207  }
   208  
   209  func addDerivativePolicy(ctx context.Context, clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy, clusterScoped bool) error {
   210  	var (
   211  		scopedLog          *logrus.Entry
   212  		derivativePolicy   v1.Object
   213  		derivativeCNP      *cilium_v2.CiliumNetworkPolicy
   214  		derivativeCCNP     *cilium_v2.CiliumClusterwideNetworkPolicy
   215  		derivativeErr, err error
   216  	)
   217  	if clusterScoped {
   218  		scopedLog = log.WithFields(logrus.Fields{
   219  			logfields.CiliumClusterwideNetworkPolicyName: cnp.ObjectMeta.Name,
   220  		})
   221  	} else {
   222  		scopedLog = log.WithFields(logrus.Fields{
   223  			logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name,
   224  			logfields.K8sNamespace:            cnp.ObjectMeta.Namespace,
   225  		})
   226  	}
   227  
   228  	// If the createDerivativeCNP() fails, a new all block rule will be inserted and
   229  	// the derivative status in the parent policy  will be updated with the
   230  	// error.
   231  	if clusterScoped {
   232  		derivativeCCNP, derivativeErr = createDerivativeCCNP(ctx, cnp)
   233  		derivativePolicy = derivativeCCNP
   234  	} else {
   235  		derivativeCNP, derivativeErr = createDerivativeCNP(ctx, cnp)
   236  		derivativePolicy = derivativeCNP
   237  	}
   238  
   239  	if derivativeErr != nil {
   240  		metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeFail).Inc()
   241  		scopedLog.WithError(derivativeErr).Error("Cannot create derivative rule. Installing deny-all rule.")
   242  		statusErr := updateDerivativeStatus(clientset, cnp, derivativePolicy.GetName(), derivativeErr, clusterScoped)
   243  		if statusErr != nil {
   244  			scopedLog.WithError(statusErr).Error("Cannot update status for derivative policy")
   245  		}
   246  		return derivativeErr
   247  	}
   248  
   249  	groupsCNPCache.UpdateCNP(cnp)
   250  	if clusterScoped {
   251  		_, err = updateOrCreateCCNP(clientset, derivativeCCNP)
   252  	} else {
   253  		_, err = updateOrCreateCNP(clientset, derivativeCNP)
   254  	}
   255  
   256  	if err != nil {
   257  		statusErr := updateDerivativeStatus(clientset, cnp, derivativePolicy.GetName(), err, clusterScoped)
   258  		if statusErr != nil {
   259  			metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeFail).Inc()
   260  			scopedLog.WithError(err).Error("Cannot update status for derivative policy")
   261  		}
   262  		return statusErr
   263  	}
   264  	metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeSuccess).Inc()
   265  
   266  	err = updateDerivativeStatus(clientset, cnp, derivativePolicy.GetName(), nil, clusterScoped)
   267  	if err != nil {
   268  		scopedLog.WithError(err).Error("Cannot update status for derivative policy")
   269  	}
   270  	return err
   271  }