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 }