github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/rules.go (about)

     1  // Copyright 2016-2019 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 policy
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  
    21  	"github.com/cilium/cilium/pkg/policy/api"
    22  
    23  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  )
    25  
    26  // ruleSlice is a wrapper around a slice of *rule, which allows for functions
    27  // to be written with []*rule as a receiver.
    28  type ruleSlice []*rule
    29  
    30  func (rules ruleSlice) wildcardL3L4Rules(ingress bool, l4Policy L4PolicyMap, requirements []v1.LabelSelectorRequirement, selectorCache *SelectorCache) {
    31  	// Duplicate L3-only rules into wildcard L7 rules.
    32  	for _, r := range rules {
    33  		if ingress {
    34  			for _, rule := range r.Ingress {
    35  				// Non-label-based rule. Ignore.
    36  				if !rule.IsLabelBased() {
    37  					continue
    38  				}
    39  
    40  				fromEndpoints := rule.GetSourceEndpointSelectorsWithRequirements(requirements)
    41  				ruleLabels := r.Rule.Labels.DeepCopy()
    42  
    43  				// L3-only rule.
    44  				if len(rule.ToPorts) == 0 && len(fromEndpoints) > 0 {
    45  					wildcardL3L4Rule(api.ProtoTCP, 0, fromEndpoints, ruleLabels, l4Policy, selectorCache)
    46  					wildcardL3L4Rule(api.ProtoUDP, 0, fromEndpoints, ruleLabels, l4Policy, selectorCache)
    47  				} else {
    48  					// L4-only or L3-dependent L4 rule.
    49  					//
    50  					// "fromEndpoints" may be empty here, which indicates that all L3 peers should
    51  					// be selected. If so, add the wildcard selector.
    52  					if len(fromEndpoints) == 0 {
    53  						fromEndpoints = append(fromEndpoints, api.WildcardEndpointSelector)
    54  					}
    55  					for _, toPort := range rule.ToPorts {
    56  						// L3/L4-only rule
    57  						if toPort.Rules.IsEmpty() {
    58  							for _, p := range toPort.Ports {
    59  								// Already validated via PortRule.Validate().
    60  								port, _ := strconv.ParseUint(p.Port, 0, 16)
    61  								wildcardL3L4Rule(p.Protocol, int(port), fromEndpoints, ruleLabels, l4Policy, selectorCache)
    62  							}
    63  						}
    64  					}
    65  				}
    66  			}
    67  		} else {
    68  			for _, rule := range r.Egress {
    69  				// Non-label-based rule. Ignore.
    70  				if !rule.IsLabelBased() {
    71  					continue
    72  				}
    73  
    74  				toEndpoints := rule.GetDestinationEndpointSelectorsWithRequirements(requirements)
    75  				ruleLabels := r.Rule.Labels.DeepCopy()
    76  
    77  				// L3-only rule.
    78  				if len(rule.ToPorts) == 0 && len(toEndpoints) > 0 {
    79  					wildcardL3L4Rule(api.ProtoTCP, 0, toEndpoints, ruleLabels, l4Policy, selectorCache)
    80  					wildcardL3L4Rule(api.ProtoUDP, 0, toEndpoints, ruleLabels, l4Policy, selectorCache)
    81  				} else {
    82  					// L4-only or L3-dependent L4 rule.
    83  					//
    84  					// "toEndpoints" may be empty here, which indicates that all L3 peers should
    85  					// be selected. If so, add the wildcard selector.
    86  					if len(toEndpoints) == 0 {
    87  						toEndpoints = append(toEndpoints, api.WildcardEndpointSelector)
    88  					}
    89  					for _, toPort := range rule.ToPorts {
    90  						// L3/L4-only rule
    91  						if toPort.Rules.IsEmpty() {
    92  							for _, p := range toPort.Ports {
    93  								// Already validated via PortRule.Validate().
    94  								port, _ := strconv.ParseUint(p.Port, 0, 16)
    95  								wildcardL3L4Rule(p.Protocol, int(port), toEndpoints, ruleLabels, l4Policy, selectorCache)
    96  							}
    97  						}
    98  					}
    99  				}
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  func (rules ruleSlice) resolveL4IngressPolicy(ctx *SearchContext, revision uint64, selectorCache *SelectorCache) (L4PolicyMap, error) {
   106  	result := L4PolicyMap{}
   107  
   108  	ctx.PolicyTrace("\n")
   109  	ctx.PolicyTrace("Resolving ingress policy for %+v\n", ctx.To)
   110  
   111  	state := traceState{}
   112  	var matchedRules ruleSlice
   113  	var requirements []v1.LabelSelectorRequirement
   114  
   115  	// Iterate over all FromRequires which select ctx.To. These requirements
   116  	// will be appended to each EndpointSelector's MatchExpressions in
   117  	// each FromEndpoints for all ingress rules. This ensures that FromRequires
   118  	// is taken into account when evaluating policy at L4.
   119  	for _, r := range rules {
   120  		if ctx.rulesSelect || r.EndpointSelector.Matches(ctx.To) {
   121  			matchedRules = append(matchedRules, r)
   122  			for _, ingressRule := range r.Ingress {
   123  				for _, requirement := range ingressRule.FromRequires {
   124  					requirements = append(requirements, requirement.ConvertToLabelSelectorRequirementSlice()...)
   125  				}
   126  			}
   127  		}
   128  	}
   129  
   130  	// Only dealing with matching rules from now on. Mark it in the ctx
   131  	oldRulesSelect := ctx.rulesSelect
   132  	ctx.rulesSelect = true
   133  
   134  	for _, r := range matchedRules {
   135  		found, err := r.resolveIngressPolicy(ctx, &state, result, requirements, selectorCache)
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  		state.ruleID++
   140  		if found != nil {
   141  			state.matchedRules++
   142  		}
   143  	}
   144  
   145  	matchedRules.wildcardL3L4Rules(true, result, requirements, selectorCache)
   146  
   147  	state.trace(len(rules), ctx)
   148  
   149  	// Restore ctx in case caller uses it again.
   150  	ctx.rulesSelect = oldRulesSelect
   151  
   152  	return result, nil
   153  }
   154  
   155  func (rules ruleSlice) resolveL4EgressPolicy(ctx *SearchContext, revision uint64, selectorCache *SelectorCache) (L4PolicyMap, error) {
   156  	result := L4PolicyMap{}
   157  
   158  	ctx.PolicyTrace("\n")
   159  	ctx.PolicyTrace("Resolving egress policy for %+v\n", ctx.From)
   160  
   161  	state := traceState{}
   162  	var matchedRules ruleSlice
   163  	var requirements []v1.LabelSelectorRequirement
   164  
   165  	// Iterate over all ToRequires which select ctx.To. These requirements will
   166  	// be appended to each EndpointSelector's MatchExpressions in each
   167  	// ToEndpoints for all egress rules. This ensures that ToRequires is
   168  	// taken into account when evaluating policy at L4.
   169  	for _, r := range rules {
   170  		if ctx.rulesSelect || r.EndpointSelector.Matches(ctx.From) {
   171  			matchedRules = append(matchedRules, r)
   172  			for _, egressRule := range r.Egress {
   173  				for _, requirement := range egressRule.ToRequires {
   174  					requirements = append(requirements, requirement.ConvertToLabelSelectorRequirementSlice()...)
   175  				}
   176  			}
   177  		}
   178  	}
   179  
   180  	// Only dealing with matching rules from now on. Mark it in the ctx
   181  	oldRulesSelect := ctx.rulesSelect
   182  	ctx.rulesSelect = true
   183  
   184  	for i, r := range matchedRules {
   185  		state.ruleID = i
   186  		found, err := r.resolveEgressPolicy(ctx, &state, result, requirements, selectorCache)
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  		state.ruleID++
   191  		if found != nil {
   192  			state.matchedRules++
   193  		}
   194  	}
   195  
   196  	matchedRules.wildcardL3L4Rules(false, result, requirements, selectorCache)
   197  
   198  	state.trace(len(rules), ctx)
   199  
   200  	// Restore ctx in case caller uses it again.
   201  	ctx.rulesSelect = oldRulesSelect
   202  
   203  	return result, nil
   204  }
   205  
   206  func (rules ruleSlice) resolveCIDRPolicy(ctx *SearchContext) *CIDRPolicy {
   207  	result := NewCIDRPolicy()
   208  
   209  	ctx.PolicyTrace("Resolving L3 (CIDR) policy for %+v\n", ctx.To)
   210  
   211  	state := traceState{}
   212  	for _, r := range rules {
   213  		r.resolveCIDRPolicy(ctx, &state, result)
   214  		state.ruleID++
   215  	}
   216  
   217  	state.trace(len(rules), ctx)
   218  	return result
   219  }
   220  
   221  // updateEndpointsCaches iterates over a given list of rules to update the cache
   222  // within the rule which determines whether or not the given identity is
   223  // selected by that rule. If a rule in the list does select said identity, it is
   224  // added to epSet. Note that epSet can be shared across goroutines!
   225  // Returns whether the endpoint was selected by one of the rules, or if the
   226  // endpoint is nil.
   227  func (rules ruleSlice) updateEndpointsCaches(ep Endpoint) (bool, error) {
   228  	if ep == nil {
   229  		return false, fmt.Errorf("cannot update caches in rules because endpoint is nil")
   230  	}
   231  	id := ep.GetID16()
   232  	if err := ep.RLockAlive(); err != nil {
   233  		return false, fmt.Errorf("cannot update caches in rules for endpoint %d because it is being deleted: %s", id, err)
   234  	}
   235  	securityIdentity := ep.GetSecurityIdentity()
   236  	ep.RUnlock()
   237  
   238  	if securityIdentity == nil {
   239  		return false, fmt.Errorf("cannot update caches in rules for endpoint %d because it has a nil identity", id)
   240  	}
   241  	endpointSelected := false
   242  	for _, r := range rules {
   243  		// Update the matches cache of each rule, and note if
   244  		// the ep is selected by any of them.
   245  		if ruleMatches := r.matches(securityIdentity); ruleMatches {
   246  			endpointSelected = true
   247  		}
   248  	}
   249  
   250  	return endpointSelected, nil
   251  }