github.com/cilium/cilium@v1.16.2/pkg/policy/groups/helpers.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package groups 5 6 import ( 7 "context" 8 "fmt" 9 10 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 12 cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 13 "github.com/cilium/cilium/pkg/k8s/client" 14 slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 15 "github.com/cilium/cilium/pkg/policy/api" 16 ) 17 18 const ( 19 cnpKindName = "derivative" 20 parentCNP = "io.cilium.network.policy.parent.uuid" 21 cnpKindKey = "io.cilium.network.policy.kind" 22 ) 23 24 func getDerivativeName(obj v1.Object) string { 25 return obj.GetName() + "-groups-" + string(obj.GetUID()) 26 } 27 28 // createDerivativeCNP will return a new CNP based on the given rule. 29 func createDerivativeCNP(ctx context.Context, cnp *cilium_v2.CiliumNetworkPolicy) (*cilium_v2.CiliumNetworkPolicy, error) { 30 // CNP informer may provide a CNP object without APIVersion or Kind. 31 // Setting manually to make sure that the derivative policy works ok. 32 derivativeCNP := &cilium_v2.CiliumNetworkPolicy{ 33 ObjectMeta: v1.ObjectMeta{ 34 Name: getDerivativeName(cnp), 35 Namespace: cnp.ObjectMeta.Namespace, 36 OwnerReferences: []v1.OwnerReference{{ 37 APIVersion: cilium_v2.SchemeGroupVersion.String(), 38 Kind: cilium_v2.CNPKindDefinition, 39 Name: cnp.ObjectMeta.Name, 40 UID: cnp.ObjectMeta.UID, 41 }}, 42 Labels: map[string]string{ 43 parentCNP: string(cnp.ObjectMeta.UID), 44 cnpKindKey: cnpKindName, 45 }, 46 }, 47 } 48 49 var ( 50 rules api.Rules 51 err error 52 ) 53 54 rules, err = cnp.Parse() 55 56 if err != nil { 57 // We return a valid pointer for derivative policy here instead of nil. 58 // This object is used to get generated name for the derivative policy 59 // when updating the status of the network policy. 60 return derivativeCNP, fmt.Errorf("cannot parse CNP: %w", err) 61 } 62 63 derivativeCNP.Specs, err = createAPIRules(ctx, rules) 64 65 return derivativeCNP, err 66 } 67 68 // createDerivativeCCNP will return a new CCNP based on the given rule. 69 func createDerivativeCCNP(ctx context.Context, cnp *cilium_v2.CiliumNetworkPolicy) (*cilium_v2.CiliumClusterwideNetworkPolicy, error) { 70 ccnp := &cilium_v2.CiliumClusterwideNetworkPolicy{ 71 TypeMeta: cnp.TypeMeta, 72 ObjectMeta: cnp.ObjectMeta, 73 Spec: cnp.Spec, 74 Specs: cnp.Specs, 75 Status: cnp.Status, 76 } 77 78 // CCNP informer may provide a CCNP object without APIVersion or Kind. 79 // Setting manually to make sure that the derivative policy works ok. 80 derivativeCCNP := &cilium_v2.CiliumClusterwideNetworkPolicy{ 81 ObjectMeta: v1.ObjectMeta{ 82 Name: getDerivativeName(ccnp), 83 Namespace: ccnp.ObjectMeta.Namespace, 84 OwnerReferences: []v1.OwnerReference{{ 85 APIVersion: cilium_v2.SchemeGroupVersion.String(), 86 Kind: cilium_v2.CCNPKindDefinition, 87 Name: ccnp.ObjectMeta.Name, 88 UID: ccnp.ObjectMeta.UID, 89 }}, 90 Labels: map[string]string{ 91 parentCNP: string(ccnp.ObjectMeta.UID), 92 cnpKindKey: cnpKindName, 93 }, 94 }, 95 } 96 97 var ( 98 rules api.Rules 99 err error 100 ) 101 102 rules, err = ccnp.Parse() 103 104 if err != nil { 105 // We return a valid pointer for derivative policy here instead of nil. 106 // This object is used to get generated name for the derivative policy 107 // when updating the status of the network policy. 108 return derivativeCCNP, fmt.Errorf("cannot parse CCNP: %w", err) 109 } 110 111 derivativeCCNP.Specs, err = createAPIRules(ctx, rules) 112 113 return derivativeCCNP, err 114 } 115 116 func createAPIRules(ctx context.Context, rules api.Rules) (api.Rules, error) { 117 specRules := make(api.Rules, len(rules)) 118 for i, rule := range rules { 119 if rule.RequiresDerivative() { 120 specRules[i] = denyEgressRule() 121 } 122 } 123 124 for i, rule := range rules { 125 if !rule.RequiresDerivative() { 126 specRules[i] = rule 127 continue 128 } 129 newRule, err := rule.CreateDerivative(ctx) 130 if err != nil { 131 return specRules, err 132 } 133 specRules[i] = newRule 134 } 135 return specRules, nil 136 } 137 138 func denyEgressRule() *api.Rule { 139 return &api.Rule{ 140 Egress: []api.EgressRule{}, 141 } 142 } 143 144 func updateOrCreateCNP(clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy) (*cilium_v2.CiliumNetworkPolicy, error) { 145 k8sCNP, err := clientset.CiliumV2().CiliumNetworkPolicies(cnp.ObjectMeta.Namespace). 146 Get(context.TODO(), cnp.ObjectMeta.Name, v1.GetOptions{}) 147 if err == nil { 148 k8sCNP.ObjectMeta.Labels = cnp.ObjectMeta.Labels 149 k8sCNP.Spec = cnp.Spec 150 k8sCNP.Specs = cnp.Specs 151 k8sCNP.Status = cilium_v2.CiliumNetworkPolicyStatus{} 152 return clientset.CiliumV2().CiliumNetworkPolicies(cnp.ObjectMeta.Namespace).Update(context.TODO(), k8sCNP, v1.UpdateOptions{}) 153 } 154 return clientset.CiliumV2().CiliumNetworkPolicies(cnp.ObjectMeta.Namespace).Create(context.TODO(), cnp, v1.CreateOptions{}) 155 } 156 157 func updateOrCreateCCNP(clientset client.Clientset, ccnp *cilium_v2.CiliumClusterwideNetworkPolicy) (*cilium_v2.CiliumClusterwideNetworkPolicy, error) { 158 k8sCCNP, err := clientset.CiliumV2().CiliumClusterwideNetworkPolicies(). 159 Get(context.TODO(), ccnp.ObjectMeta.Name, v1.GetOptions{}) 160 if err == nil { 161 k8sCCNP.ObjectMeta.Labels = ccnp.ObjectMeta.Labels 162 k8sCCNP.Spec = ccnp.Spec 163 k8sCCNP.Specs = ccnp.Specs 164 k8sCCNP.Status = cilium_v2.CiliumNetworkPolicyStatus{} 165 166 return clientset.CiliumV2().CiliumClusterwideNetworkPolicies().Update(context.TODO(), k8sCCNP, v1.UpdateOptions{}) 167 } 168 169 return clientset.CiliumV2().CiliumClusterwideNetworkPolicies(). 170 Create(context.TODO(), ccnp, v1.CreateOptions{}) 171 } 172 173 func updateDerivativeStatus(clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy, derivativeName string, err error, clusterScoped bool) error { 174 status := cilium_v2.CiliumNetworkPolicyNodeStatus{ 175 LastUpdated: slimv1.Now(), 176 Enforcing: false, 177 } 178 179 if err != nil { 180 status.OK = false 181 status.Error = err.Error() 182 } else { 183 status.OK = true 184 } 185 186 if clusterScoped { 187 return updateDerivativeCCNPStatus(clientset, cnp, status, derivativeName) 188 } 189 190 return updateDerivativeCNPStatus(clientset, cnp, status, derivativeName) 191 } 192 193 func updateDerivativeCNPStatus(clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy, status cilium_v2.CiliumNetworkPolicyNodeStatus, 194 derivativeName string) error { 195 // This CNP can be modified by cilium agent or operator. To be able to push 196 // the status correctly fetch the last version to avoid updates issues. 197 k8sCNP, clientErr := clientset.CiliumV2().CiliumNetworkPolicies(cnp.ObjectMeta.Namespace). 198 Get(context.TODO(), cnp.ObjectMeta.Name, v1.GetOptions{}) 199 200 if clientErr != nil { 201 return fmt.Errorf("cannot get Kubernetes policy: %w", clientErr) 202 } 203 204 if k8sCNP.ObjectMeta.UID != cnp.ObjectMeta.UID { 205 // This case should not happen, but if the UID does not match make sure 206 // that the new policy is not in the cache to not loop over it. The 207 // kubernetes watcher should take care about that. 208 groupsCNPCache.DeleteCNP(k8sCNP) 209 return fmt.Errorf("policy UID mismatch") 210 } 211 212 k8sCNP.SetDerivedPolicyStatus(derivativeName, status) 213 groupsCNPCache.UpdateCNP(k8sCNP) 214 215 // TODO: Switch to JSON patch. 216 _, err := clientset.CiliumV2().CiliumNetworkPolicies(cnp.ObjectMeta.Namespace). 217 UpdateStatus(context.TODO(), k8sCNP, v1.UpdateOptions{}) 218 219 return err 220 } 221 222 func updateDerivativeCCNPStatus(clientset client.Clientset, cnp *cilium_v2.CiliumNetworkPolicy, status cilium_v2.CiliumNetworkPolicyNodeStatus, 223 derivativeName string) error { 224 k8sCCNP, clientErr := clientset.CiliumV2().CiliumClusterwideNetworkPolicies(). 225 Get(context.TODO(), cnp.ObjectMeta.Name, v1.GetOptions{}) 226 227 if clientErr != nil { 228 return fmt.Errorf("cannot get Kubernetes policy: %w", clientErr) 229 } 230 231 if k8sCCNP.ObjectMeta.UID != cnp.ObjectMeta.UID { 232 // This case should not happen, but if the UID does not match make sure 233 // that the new policy is not in the cache to not loop over it. The 234 // kubernetes watcher should take care of that. 235 groupsCNPCache.DeleteCNP(&cilium_v2.CiliumNetworkPolicy{ 236 ObjectMeta: k8sCCNP.ObjectMeta, 237 }) 238 return fmt.Errorf("policy UID mismatch") 239 } 240 241 k8sCCNP.SetDerivedPolicyStatus(derivativeName, status) 242 groupsCNPCache.UpdateCNP(&cilium_v2.CiliumNetworkPolicy{ 243 TypeMeta: k8sCCNP.TypeMeta, 244 ObjectMeta: k8sCCNP.ObjectMeta, 245 Spec: k8sCCNP.Spec, 246 Specs: k8sCCNP.Specs, 247 Status: k8sCCNP.Status, 248 }) 249 250 // TODO: Switch to JSON patch 251 _, err := clientset.CiliumV2().CiliumClusterwideNetworkPolicies(). 252 UpdateStatus(context.TODO(), k8sCCNP, v1.UpdateOptions{}) 253 254 return err 255 256 }