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  }