github.phpd.cn/cilium/cilium@v1.6.12/pkg/k8s/rule_translate.go (about) 1 // Copyright 2016-2017 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package k8s 16 17 import ( 18 "fmt" 19 "net" 20 21 "github.com/cilium/cilium/pkg/ipcache" 22 "github.com/cilium/cilium/pkg/policy" 23 "github.com/cilium/cilium/pkg/policy/api" 24 25 "k8s.io/apimachinery/pkg/labels" 26 ) 27 28 var _ policy.Translator = RuleTranslator{} 29 30 // RuleTranslator implements pkg/policy.Translator interface 31 // Translate populates/depopulates given rule with ToCIDR rules 32 // Based on provided service/endpoint 33 type RuleTranslator struct { 34 Service ServiceID 35 Endpoint Endpoints 36 ServiceLabels map[string]string 37 Revert bool 38 IPCache ipcache.Implementation 39 } 40 41 // Translate calls TranslateEgress on all r.Egress rules 42 func (k RuleTranslator) Translate(r *api.Rule, result *policy.TranslationResult) error { 43 for egressIndex := range r.Egress { 44 err := k.TranslateEgress(&r.Egress[egressIndex], result) 45 if err != nil { 46 return err 47 } 48 } 49 return nil 50 } 51 52 // TranslateEgress populates/depopulates egress rules with ToCIDR entries based 53 // on toService entries 54 func (k RuleTranslator) TranslateEgress(r *api.EgressRule, result *policy.TranslationResult) error { 55 56 defer r.SetAggregatedSelectors() 57 err := k.depopulateEgress(r, result) 58 if err != nil { 59 return err 60 } 61 if !k.Revert { 62 err := k.populateEgress(r, result) 63 if err != nil { 64 return err 65 } 66 } 67 return nil 68 } 69 70 func (k RuleTranslator) populateEgress(r *api.EgressRule, result *policy.TranslationResult) error { 71 for _, service := range r.ToServices { 72 if k.serviceMatches(service) { 73 if err := generateToCidrFromEndpoint(r, k.Endpoint, k.IPCache); err != nil { 74 return err 75 } 76 // TODO: generateToPortsFromEndpoint when ToPorts and ToCIDR are compatible 77 } 78 } 79 return nil 80 } 81 82 func (k RuleTranslator) depopulateEgress(r *api.EgressRule, result *policy.TranslationResult) error { 83 for _, service := range r.ToServices { 84 // NumToServicesRules are only counted in depopulate to avoid 85 // counting rules twice 86 result.NumToServicesRules++ 87 if k.serviceMatches(service) { 88 if err := deleteToCidrFromEndpoint(r, k.Endpoint, k.IPCache); err != nil { 89 return err 90 } 91 // TODO: generateToPortsFromEndpoint when ToPorts and ToCIDR are compatible 92 } 93 } 94 return nil 95 } 96 97 func (k RuleTranslator) serviceMatches(service api.Service) bool { 98 if service.K8sServiceSelector != nil { 99 es := api.EndpointSelector(service.K8sServiceSelector.Selector) 100 es.SyncRequirementsWithLabelSelector() 101 esMatches := es.Matches(labels.Set(k.ServiceLabels)) 102 return esMatches && 103 (service.K8sServiceSelector.Namespace == k.Service.Namespace || service.K8sServiceSelector.Namespace == "") 104 } 105 106 if service.K8sService != nil { 107 return service.K8sService.ServiceName == k.Service.Name && 108 (service.K8sService.Namespace == k.Service.Namespace || service.K8sService.Namespace == "") 109 } 110 111 return false 112 } 113 114 // generateToCidrFromEndpoint takes an egress rule and populates it with 115 // ToCIDR rules based on provided endpoint object 116 func generateToCidrFromEndpoint( 117 egress *api.EgressRule, 118 endpoint Endpoints, 119 impl ipcache.Implementation) error { 120 121 // Non-nil implementation here implies that this translation is 122 // occurring after policy import. This means that the CIDRs were not 123 // known at that time, so the IPCache hasn't been informed about them. 124 // In this case, it's the job of this Translator to notify the IPCache. 125 if impl != nil { 126 prefixes, err := endpoint.CIDRPrefixes() 127 if err != nil { 128 return err 129 } 130 if _, err := ipcache.AllocateCIDRs(impl, prefixes); err != nil { 131 return err 132 } 133 } 134 135 // This will generate one-address CIDRs consisting of endpoint backend ip 136 mask := net.CIDRMask(128, 128) 137 for ip := range endpoint.Backends { 138 epIP := net.ParseIP(ip) 139 if epIP == nil { 140 return fmt.Errorf("unable to parse ip: %s", ip) 141 } 142 143 found := false 144 for _, c := range egress.ToCIDRSet { 145 _, cidr, err := net.ParseCIDR(string(c.Cidr)) 146 if err != nil { 147 return err 148 } 149 if cidr.Contains(epIP) { 150 found = true 151 break 152 } 153 } 154 if !found { 155 cidr := net.IPNet{IP: epIP.Mask(mask), Mask: mask} 156 egress.ToCIDRSet = append(egress.ToCIDRSet, api.CIDRRule{ 157 Cidr: api.CIDR(cidr.String()), 158 Generated: true, 159 }) 160 } 161 } 162 return nil 163 } 164 165 // deleteToCidrFromEndpoint takes an egress rule and removes ToCIDR rules 166 // matching endpoint. Returns an error if any of the backends are malformed. 167 // 168 // If all backends are valid, attempts to remove any ipcache CIDR mappings (and 169 // CIDR Identities) from the kvstore for backends in 'endpoint' that are being 170 // removed from the policy. On failure to release such kvstore mappings, errors 171 // will be logged but this function will return nil to allow subsequent 172 // processing to proceed. 173 func deleteToCidrFromEndpoint( 174 egress *api.EgressRule, 175 endpoint Endpoints, 176 impl ipcache.Implementation) error { 177 178 delCIDRRules := make(map[int]*api.CIDRRule, len(egress.ToCIDRSet)) 179 180 for ip := range endpoint.Backends { 181 epIP := net.ParseIP(ip) 182 if epIP == nil { 183 return fmt.Errorf("unable to parse ip: %s", ip) 184 } 185 186 for i, c := range egress.ToCIDRSet { 187 if _, ok := delCIDRRules[i]; ok { 188 // it's already going to be deleted so we can continue 189 continue 190 } 191 _, cidr, err := net.ParseCIDR(string(c.Cidr)) 192 if err != nil { 193 return err 194 } 195 // delete all generated CIDRs for a CIDR that match the given 196 // endpoint 197 if c.Generated && cidr.Contains(epIP) { 198 delCIDRRules[i] = &egress.ToCIDRSet[i] 199 } 200 } 201 if len(delCIDRRules) == len(egress.ToCIDRSet) { 202 break 203 } 204 } 205 206 // If no rules were deleted we can do an early return here and avoid doing 207 // the useless 'append' below. 208 if len(delCIDRRules) == 0 { 209 return nil 210 } 211 212 if impl != nil { 213 delSlice := make([]api.CIDRRule, 0, len(egress.ToCIDRSet)) 214 for _, delCIDRRule := range delCIDRRules { 215 delSlice = append(delSlice, *delCIDRRule) 216 } 217 prefixes := policy.GetPrefixesFromCIDRSet(delSlice) 218 ipcache.ReleaseCIDRs(prefixes) 219 } 220 221 // if endpoint is not in CIDR or it's not generated it's ok to retain it 222 newCIDRRules := make([]api.CIDRRule, 0, len(egress.ToCIDRSet)-len(delCIDRRules)) 223 for i, c := range egress.ToCIDRSet { 224 // If the rule was deleted then it shouldn't be re-added 225 if _, ok := delCIDRRules[i]; ok { 226 continue 227 } 228 newCIDRRules = append(newCIDRRules, c) 229 } 230 231 egress.ToCIDRSet = newCIDRRules 232 233 return nil 234 } 235 236 // PreprocessRules translates rules that apply to headless services 237 func PreprocessRules(r api.Rules, cache *ServiceCache) error { 238 239 // Headless services are translated prior to policy import, so the 240 // policy will contain all of the CIDRs and can handle ipcache 241 // interactions when the policy is imported. Ignore the IPCache 242 // interaction here and just set the implementation to nil. 243 ipcache := ipcache.Implementation(nil) 244 245 cache.mutex.Lock() 246 defer cache.mutex.Unlock() 247 248 for _, rule := range r { 249 for ns, ep := range cache.endpoints { 250 svc, ok := cache.services[ns] 251 if ok && svc.IsExternal() { 252 t := NewK8sTranslator(ns, *ep, false, svc.Labels, ipcache) 253 err := t.Translate(rule, &policy.TranslationResult{}) 254 if err != nil { 255 return err 256 } 257 } 258 } 259 } 260 return nil 261 } 262 263 // NewK8sTranslator returns RuleTranslator 264 func NewK8sTranslator( 265 serviceInfo ServiceID, 266 endpoint Endpoints, 267 revert bool, 268 labels map[string]string, 269 ipcache ipcache.Implementation) RuleTranslator { 270 271 return RuleTranslator{serviceInfo, endpoint, labels, revert, ipcache} 272 }