github.com/cilium/cilium@v1.16.2/pkg/policy/k8s/cilium_network_policy.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package k8s 5 6 import ( 7 "context" 8 9 "github.com/sirupsen/logrus" 10 11 ipcacheTypes "github.com/cilium/cilium/pkg/ipcache/types" 12 "github.com/cilium/cilium/pkg/k8s/resource" 13 "github.com/cilium/cilium/pkg/k8s/types" 14 "github.com/cilium/cilium/pkg/logging/logfields" 15 "github.com/cilium/cilium/pkg/metrics" 16 "github.com/cilium/cilium/pkg/policy" 17 "github.com/cilium/cilium/pkg/source" 18 "github.com/cilium/cilium/pkg/time" 19 ) 20 21 func (p *policyWatcher) onUpsert( 22 cnp *types.SlimCNP, 23 key resource.Key, 24 apiGroup string, 25 resourceID ipcacheTypes.ResourceID, 26 ) error { 27 initialRecvTime := time.Now() 28 29 defer func() { 30 p.k8sResourceSynced.SetEventTimestamp(apiGroup) 31 }() 32 33 oldCNP, ok := p.cnpCache[key] 34 if ok { 35 // no generation change; this was a status update. 36 if oldCNP.Generation == cnp.Generation { 37 return nil 38 } 39 if oldCNP.DeepEqual(cnp) { 40 return nil 41 } 42 43 p.log.WithFields(logrus.Fields{ 44 logfields.K8sAPIVersion: cnp.TypeMeta.APIVersion, 45 logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name, 46 logfields.K8sNamespace: cnp.ObjectMeta.Namespace, 47 "annotations.old": oldCNP.ObjectMeta.Annotations, 48 "annotations": cnp.ObjectMeta.Annotations, 49 }).Debug("Modified CiliumNetworkPolicy") 50 } 51 52 if cnp.RequiresDerivative() { 53 return nil 54 } 55 56 // check if this cnp was referencing or is now referencing at least one non-empty 57 // CiliumCIDRGroup and update the relevant metric accordingly. 58 cidrGroupRefs := getCIDRGroupRefs(cnp) 59 cidrsSets, _ := p.cidrGroupRefsToCIDRsSets(cidrGroupRefs) 60 if len(cidrsSets) > 0 { 61 p.cidrGroupPolicies[key] = struct{}{} 62 } else { 63 delete(p.cidrGroupPolicies, key) 64 } 65 metrics.CIDRGroupsReferenced.Set(float64(len(p.cidrGroupPolicies))) 66 67 // check if this cnp was referencing or is now referencing at least one ToServices rule 68 if hasToServices(cnp) { 69 p.toServicesPolicies[key] = struct{}{} 70 } else { 71 delete(p.toServicesPolicies, key) 72 } 73 74 return p.resolveCiliumNetworkPolicyRefs(cnp, key, initialRecvTime, resourceID) 75 } 76 77 func (p *policyWatcher) onDelete( 78 cnp *types.SlimCNP, 79 key resource.Key, 80 apiGroup string, 81 resourceID ipcacheTypes.ResourceID, 82 ) error { 83 err := p.deleteCiliumNetworkPolicyV2(cnp, resourceID) 84 delete(p.cnpCache, key) 85 86 // Clear CIDRGroupRef index 87 delete(p.cidrGroupPolicies, key) 88 metrics.CIDRGroupsReferenced.Set(float64(len(p.cidrGroupPolicies))) 89 90 // Clear ToServices index 91 for svcID := range p.cnpByServiceID { 92 p.clearCNPForService(key, svcID) 93 } 94 delete(p.toServicesPolicies, key) 95 96 p.k8sResourceSynced.SetEventTimestamp(apiGroup) 97 98 return err 99 } 100 101 // resolveCiliumNetworkPolicyRefs resolves all the references to external resources 102 // (e.g. CiliumCIDRGroups) in a CNP/CCNP, inlines them into a "translated" CNP, 103 // and then adds the translated CNP to the policy repository. 104 // If the CNP was successfully imported, the raw (i.e. untranslated) CNP/CCNP 105 // is also added to p.cnpCache. 106 func (p *policyWatcher) resolveCiliumNetworkPolicyRefs( 107 cnp *types.SlimCNP, 108 key resource.Key, 109 initialRecvTime time.Time, 110 resourceID ipcacheTypes.ResourceID, 111 ) error { 112 // We need to deepcopy this structure because we are writing 113 // fields in cnp.Parse() in upsertCiliumNetworkPolicyV2. 114 // See https://github.com/cilium/cilium/blob/27fee207f5422c95479422162e9ea0d2f2b6c770/pkg/policy/api/ingress.go#L112-L134 115 translatedCNP := cnp.DeepCopy() 116 117 // Resolve CiliumCIDRGroup references 118 translationStart := time.Now() 119 p.resolveCIDRGroupRef(translatedCNP) 120 metrics.CIDRGroupTranslationTimeStats.Observe(time.Since(translationStart).Seconds()) 121 122 // Resolve ToService references 123 p.resolveToServices(key, translatedCNP) 124 125 err := p.upsertCiliumNetworkPolicyV2(translatedCNP, initialRecvTime, resourceID) 126 if err == nil { 127 p.cnpCache[key] = cnp 128 } 129 130 return err 131 } 132 133 func (p *policyWatcher) upsertCiliumNetworkPolicyV2(cnp *types.SlimCNP, initialRecvTime time.Time, resourceID ipcacheTypes.ResourceID) error { 134 scopedLog := p.log.WithFields(logrus.Fields{ 135 logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name, 136 logfields.K8sAPIVersion: cnp.TypeMeta.APIVersion, 137 logfields.K8sNamespace: cnp.ObjectMeta.Namespace, 138 }) 139 140 scopedLog.Debug("Adding CiliumNetworkPolicy") 141 142 rules, policyImportErr := cnp.Parse() 143 if policyImportErr == nil { 144 _, policyImportErr = p.policyManager.PolicyAdd(rules, &policy.AddOptions{ 145 Source: source.CustomResource, 146 ProcessingStartTime: initialRecvTime, 147 Resource: resourceID, 148 ReplaceByResource: true, 149 }) 150 } 151 152 if policyImportErr != nil { 153 scopedLog.WithError(policyImportErr).Warn("Unable to add CiliumNetworkPolicy") 154 } else { 155 scopedLog.Info("Imported CiliumNetworkPolicy") 156 } 157 158 return policyImportErr 159 } 160 161 func (p *policyWatcher) deleteCiliumNetworkPolicyV2(cnp *types.SlimCNP, resourceID ipcacheTypes.ResourceID) error { 162 scopedLog := p.log.WithFields(logrus.Fields{ 163 logfields.CiliumNetworkPolicyName: cnp.ObjectMeta.Name, 164 logfields.K8sAPIVersion: cnp.TypeMeta.APIVersion, 165 logfields.K8sNamespace: cnp.ObjectMeta.Namespace, 166 }) 167 168 scopedLog.Debug("Deleting CiliumNetworkPolicy") 169 170 _, err := p.policyManager.PolicyDelete(nil, &policy.DeleteOptions{ 171 Source: source.CustomResource, 172 Resource: resourceID, 173 DeleteByResource: true, 174 }) 175 if err == nil { 176 scopedLog.Info("Deleted CiliumNetworkPolicy") 177 } else { 178 scopedLog.WithError(err).Warn("Unable to delete CiliumNetworkPolicy") 179 } 180 return err 181 } 182 183 func (p *policyWatcher) registerResourceWithSyncFn(ctx context.Context, resource string, syncFn func() bool) { 184 p.k8sResourceSynced.BlockWaitGroupToSyncResources(ctx.Done(), nil, syncFn, resource) 185 p.k8sAPIGroups.AddAPI(resource) 186 } 187 188 // reportCNPChangeMetrics generates metrics for changes (Add, Update, Delete) to 189 // Cilium Network Policies depending on the operation's success. 190 func reportCNPChangeMetrics(err error) { 191 if err != nil { 192 metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeFail).Inc() 193 } else { 194 metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeSuccess).Inc() 195 } 196 } 197 198 func resourceIDForCiliumNetworkPolicy(key resource.Key, cnp *types.SlimCNP) ipcacheTypes.ResourceID { 199 resourceKind := ipcacheTypes.ResourceKindCNP 200 if len(key.Namespace) == 0 { 201 resourceKind = ipcacheTypes.ResourceKindCCNP 202 } 203 return ipcacheTypes.NewResourceID( 204 resourceKind, 205 cnp.ObjectMeta.Namespace, 206 cnp.ObjectMeta.Name, 207 ) 208 }