github.com/cilium/cilium@v1.16.2/pkg/policy/k8s/cilium_cidr_group.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package k8s
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/sirupsen/logrus"
    11  
    12  	cilium_v2_alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    13  	"github.com/cilium/cilium/pkg/k8s/types"
    14  	"github.com/cilium/cilium/pkg/logging/logfields"
    15  	"github.com/cilium/cilium/pkg/policy/api"
    16  	"github.com/cilium/cilium/pkg/time"
    17  )
    18  
    19  func (p *policyWatcher) onUpsertCIDRGroup(
    20  	cidrGroup *cilium_v2_alpha1.CiliumCIDRGroup,
    21  	apiGroup string,
    22  ) error {
    23  
    24  	defer func() {
    25  		p.k8sResourceSynced.SetEventTimestamp(apiGroup)
    26  	}()
    27  
    28  	oldCidrGroup, ok := p.cidrGroupCache[cidrGroup.Name]
    29  	if ok && oldCidrGroup.Spec.DeepEqual(&cidrGroup.Spec) {
    30  		return nil
    31  	}
    32  
    33  	cidrGroupCpy := cidrGroup.DeepCopy()
    34  	p.cidrGroupCache[cidrGroup.Name] = cidrGroupCpy
    35  
    36  	err := p.updateCIDRGroupRefPolicies(cidrGroup.Name)
    37  
    38  	return err
    39  }
    40  
    41  func (p *policyWatcher) onDeleteCIDRGroup(
    42  	cidrGroupName string,
    43  	apiGroup string,
    44  ) error {
    45  	delete(p.cidrGroupCache, cidrGroupName)
    46  
    47  	err := p.updateCIDRGroupRefPolicies(cidrGroupName)
    48  
    49  	p.k8sResourceSynced.SetEventTimestamp(apiGroup)
    50  
    51  	return err
    52  }
    53  
    54  func (p *policyWatcher) updateCIDRGroupRefPolicies(
    55  	cidrGroup string,
    56  ) error {
    57  	var errs []error
    58  	for key, cnp := range p.cnpCache {
    59  		if !hasCIDRGroupRef(cnp, cidrGroup) {
    60  			continue
    61  		}
    62  
    63  		p.log.WithFields(logrus.Fields{
    64  			logfields.CiliumNetworkPolicyName: cnp.Name,
    65  			logfields.K8sAPIVersion:           cnp.APIVersion,
    66  			logfields.K8sNamespace:            cnp.Namespace,
    67  			logfields.CIDRGroupRef:            cidrGroup,
    68  		}).Info("Referenced CiliumCIDRGroup updated or deleted, recalculating CiliumNetworkPolicy rules")
    69  
    70  		initialRecvTime := time.Now()
    71  
    72  		resourceID := resourceIDForCiliumNetworkPolicy(key, cnp)
    73  
    74  		errs = append(errs, p.resolveCiliumNetworkPolicyRefs(cnp, key, initialRecvTime, resourceID))
    75  	}
    76  	return errors.Join(errs...)
    77  }
    78  
    79  func (p *policyWatcher) resolveCIDRGroupRef(cnp *types.SlimCNP) {
    80  	refs := getCIDRGroupRefs(cnp)
    81  	if len(refs) == 0 {
    82  		return
    83  	}
    84  
    85  	cidrsSets, err := p.cidrGroupRefsToCIDRsSets(refs)
    86  	if err != nil {
    87  		p.log.WithFields(logrus.Fields{
    88  			logfields.K8sAPIVersion:           cnp.TypeMeta.APIVersion,
    89  			logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name,
    90  			logfields.K8sNamespace:            cnp.ObjectMeta.Namespace,
    91  			logfields.CIDRGroupRef:            refs,
    92  		}).WithError(err).Warning("Unable to translate all CIDR groups to CIDRs")
    93  	}
    94  
    95  	translateCIDRGroupRefs(cnp, cidrsSets)
    96  }
    97  
    98  func hasCIDRGroupRef(cnp *types.SlimCNP, cidrGroup string) bool {
    99  	if specHasCIDRGroupRef(cnp.Spec, cidrGroup) {
   100  		return true
   101  	}
   102  	for _, spec := range cnp.Specs {
   103  		if specHasCIDRGroupRef(spec, cidrGroup) {
   104  			return true
   105  		}
   106  	}
   107  	return false
   108  }
   109  
   110  func specHasCIDRGroupRef(spec *api.Rule, cidrGroup string) bool {
   111  	if spec == nil {
   112  		return false
   113  	}
   114  	for _, ingress := range spec.Ingress {
   115  		for _, rule := range ingress.FromCIDRSet {
   116  			if string(rule.CIDRGroupRef) == cidrGroup {
   117  				return true
   118  			}
   119  		}
   120  	}
   121  	for _, ingress := range spec.IngressDeny {
   122  		for _, rule := range ingress.FromCIDRSet {
   123  			if string(rule.CIDRGroupRef) == cidrGroup {
   124  				return true
   125  			}
   126  		}
   127  	}
   128  	for _, egress := range spec.Egress {
   129  		for _, rule := range egress.ToCIDRSet {
   130  			if string(rule.CIDRGroupRef) == cidrGroup {
   131  				return true
   132  			}
   133  		}
   134  	}
   135  	for _, egress := range spec.EgressDeny {
   136  		for _, rule := range egress.ToCIDRSet {
   137  			if string(rule.CIDRGroupRef) == cidrGroup {
   138  				return true
   139  			}
   140  		}
   141  	}
   142  	return false
   143  }
   144  
   145  func getCIDRGroupRefs(cnp *types.SlimCNP) []string {
   146  	if cnp.Spec == nil && cnp.Specs == nil {
   147  		return nil
   148  	}
   149  
   150  	specs := cnp.Specs
   151  	if cnp.Spec != nil {
   152  		specs = append(specs, cnp.Spec)
   153  	}
   154  
   155  	var cidrGroupRefs []string
   156  	for _, spec := range specs {
   157  		for _, ingress := range spec.Ingress {
   158  			for _, rule := range ingress.FromCIDRSet {
   159  				// If CIDR is not set, then we assume CIDRGroupRef is set due
   160  				// to OneOf, even if CIDRGroupRef is empty, as that's still a
   161  				// valid reference (although useless from a user perspective).
   162  				if len(rule.Cidr) == 0 {
   163  					cidrGroupRefs = append(cidrGroupRefs, string(rule.CIDRGroupRef))
   164  				}
   165  			}
   166  		}
   167  		for _, ingress := range spec.IngressDeny {
   168  			for _, rule := range ingress.FromCIDRSet {
   169  				if len(rule.Cidr) == 0 {
   170  					cidrGroupRefs = append(cidrGroupRefs, string(rule.CIDRGroupRef))
   171  				}
   172  			}
   173  		}
   174  		for _, egress := range spec.Egress {
   175  			for _, rule := range egress.ToCIDRSet {
   176  				if len(rule.Cidr) == 0 {
   177  					cidrGroupRefs = append(cidrGroupRefs, string(rule.CIDRGroupRef))
   178  				}
   179  			}
   180  		}
   181  		for _, egress := range spec.EgressDeny {
   182  			for _, rule := range egress.ToCIDRSet {
   183  				if len(rule.Cidr) == 0 {
   184  					cidrGroupRefs = append(cidrGroupRefs, string(rule.CIDRGroupRef))
   185  				}
   186  			}
   187  		}
   188  	}
   189  
   190  	return cidrGroupRefs
   191  }
   192  
   193  func (p *policyWatcher) cidrGroupRefsToCIDRsSets(cidrGroupRefs []string) (map[string][]api.CIDR, error) {
   194  	var errs []error
   195  	cidrsSet := make(map[string][]api.CIDR)
   196  	for _, cidrGroupRef := range cidrGroupRefs {
   197  		cidrGroup, found := p.cidrGroupCache[cidrGroupRef]
   198  		if !found {
   199  			errs = append(errs, fmt.Errorf("cidr group %q not found, skipping translation", cidrGroupRef))
   200  			continue
   201  		}
   202  
   203  		cidrs := make([]api.CIDR, 0, len(cidrGroup.Spec.ExternalCIDRs))
   204  		for _, cidr := range cidrGroup.Spec.ExternalCIDRs {
   205  			cidrs = append(cidrs, api.CIDR(cidr))
   206  		}
   207  		cidrsSet[cidrGroupRef] = cidrs
   208  	}
   209  	return cidrsSet, errors.Join(errs...)
   210  }
   211  
   212  func translateCIDRGroupRefs(cnp *types.SlimCNP, cidrsSets map[string][]api.CIDR) {
   213  	if cnp.Spec != nil {
   214  		translateSpec(cnp.Spec, cidrsSets)
   215  	}
   216  	for i := range cnp.Specs {
   217  		if cnp.Specs[i] != nil {
   218  			translateSpec(cnp.Specs[i], cidrsSets)
   219  		}
   220  	}
   221  }
   222  
   223  func translateSpec(spec *api.Rule, cidrsSets map[string][]api.CIDR) {
   224  	for i := range spec.Ingress {
   225  		cidrSet := translateCIDRRuleSlice(spec.Ingress[i].FromCIDRSet, cidrsSets)
   226  		// Careful to distinguish between nil (unset, selecting everything) and
   227  		// empty list (selecting nothing), hence this overly explicit code.
   228  		if cidrSet == nil {
   229  			cidrSet = make([]api.CIDRRule, 0)
   230  		}
   231  		spec.Ingress[i].FromCIDRSet = cidrSet
   232  	}
   233  	for i := range spec.IngressDeny {
   234  		cidrSet := translateCIDRRuleSlice(spec.IngressDeny[i].FromCIDRSet, cidrsSets)
   235  		if cidrSet == nil {
   236  			cidrSet = make([]api.CIDRRule, 0)
   237  		}
   238  		spec.IngressDeny[i].FromCIDRSet = cidrSet
   239  	}
   240  	for i := range spec.Egress {
   241  		cidrSet := translateCIDRRuleSlice(spec.Egress[i].ToCIDRSet, cidrsSets)
   242  		if cidrSet == nil {
   243  			cidrSet = make([]api.CIDRRule, 0)
   244  		}
   245  		spec.Egress[i].ToCIDRSet = cidrSet
   246  	}
   247  	for i := range spec.EgressDeny {
   248  		cidrSet := translateCIDRRuleSlice(spec.EgressDeny[i].ToCIDRSet, cidrsSets)
   249  		if cidrSet == nil {
   250  			cidrSet = make([]api.CIDRRule, 0)
   251  		}
   252  		spec.EgressDeny[i].ToCIDRSet = cidrSet
   253  	}
   254  }
   255  
   256  func translateCIDRRuleSlice(cidrRules api.CIDRRuleSlice, cidrsSets map[string][]api.CIDR) api.CIDRRuleSlice {
   257  	var (
   258  		oldRules api.CIDRRuleSlice
   259  		refRules []api.CIDRRule
   260  	)
   261  
   262  	for _, rule := range cidrRules {
   263  		if rule.CIDRGroupRef == "" {
   264  			// keep rules without a cidr group reference
   265  			oldRules = append(oldRules, rule)
   266  			continue
   267  		}
   268  		// collect all rules with references to a cidr group
   269  		refRules = append(refRules, rule)
   270  	}
   271  
   272  	// add rules for each cidr in the referenced cidr groups
   273  	var newRules api.CIDRRuleSlice
   274  	for _, refRule := range refRules {
   275  		cidrs, found := cidrsSets[string(refRule.CIDRGroupRef)]
   276  		if !found || len(cidrs) == 0 {
   277  			continue
   278  		}
   279  		for _, cidr := range cidrs {
   280  			newRules = append(newRules, api.CIDRRule{Cidr: cidr, ExceptCIDRs: refRule.ExceptCIDRs})
   281  		}
   282  	}
   283  
   284  	return append(oldRules, newRules...)
   285  }