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 }