github.phpd.cn/cilium/cilium@v1.6.12/pkg/k8s/apis/cilium.io/utils/utils.go (about) 1 // Copyright 2017-2018 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 utils 16 17 import ( 18 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 19 "github.com/cilium/cilium/pkg/labels" 20 "github.com/cilium/cilium/pkg/logging" 21 "github.com/cilium/cilium/pkg/logging/logfields" 22 "github.com/cilium/cilium/pkg/policy/api" 23 24 "github.com/sirupsen/logrus" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 ) 28 29 const ( 30 // subsysK8s is the value for logfields.LogSubsys 31 subsysK8s = "k8s" 32 // podPrefixLbl is the value the prefix used in the label selector to 33 // represent pods on the default namespace. 34 podPrefixLbl = labels.LabelSourceK8sKeyPrefix + k8sConst.PodNamespaceLabel 35 36 // podAnyPrefixLbl is the value of the prefix used in the label selector to 37 // represent pods in the default namespace for any source type. 38 podAnyPrefixLbl = labels.LabelSourceAnyKeyPrefix + k8sConst.PodNamespaceLabel 39 40 // podInitLbl is the label used in a label selector to match on 41 // initializing pods. 42 podInitLbl = labels.LabelSourceReservedKeyPrefix + labels.IDNameInit 43 44 // ResourceTypeCiliumNetworkPolicy is the resource type used for the 45 // PolicyLabelDerivedFrom label 46 ResourceTypeCiliumNetworkPolicy = "CiliumNetworkPolicy" 47 ) 48 49 var ( 50 // log is the k8s package logger object. 51 log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s) 52 ) 53 54 // GetPolicyLabels returns a LabelArray for the given namespace and name. 55 func GetPolicyLabels(ns, name string, uid types.UID, derivedFrom string) labels.LabelArray { 56 // Keep labels sorted by the key. 57 return labels.LabelArray{ 58 labels.NewLabel(k8sConst.PolicyLabelDerivedFrom, derivedFrom, labels.LabelSourceK8s), 59 labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceK8s), 60 labels.NewLabel(k8sConst.PolicyLabelNamespace, ns, labels.LabelSourceK8s), 61 labels.NewLabel(k8sConst.PolicyLabelUID, string(uid), labels.LabelSourceK8s), 62 } 63 } 64 65 // getEndpointSelector converts the provided labelSelector into an EndpointSelector, 66 // adding the relevant matches for namespaces based on the provided options. 67 func getEndpointSelector(namespace string, labelSelector *metav1.LabelSelector, addK8sPrefix, matchesInit bool) api.EndpointSelector { 68 es := api.NewESFromK8sLabelSelector("", labelSelector) 69 70 // The k8s prefix must not be added to reserved labels. 71 if addK8sPrefix && es.HasKeyPrefix(labels.LabelSourceReservedKeyPrefix) { 72 return es 73 } 74 75 // The user can explicitly specify the namespace in the 76 // FromEndpoints selector. If omitted, we limit the 77 // scope to the namespace the policy lives in. 78 // 79 // Policies applying on initializing pods are a special case. 80 // Those pods don't have any labels, so they don't have a namespace label either. 81 // Don't add a namespace label to those endpoint selectors, or we wouldn't be 82 // able to match on those pods. 83 if !matchesInit && !es.HasKey(podPrefixLbl) && !es.HasKey(podAnyPrefixLbl) { 84 es.AddMatch(podPrefixLbl, namespace) 85 } 86 87 return es 88 } 89 90 func parseToCiliumIngressRule(namespace string, inRule, retRule *api.Rule) { 91 matchesInit := retRule.EndpointSelector.HasKey(podInitLbl) 92 93 if inRule.Ingress != nil { 94 retRule.Ingress = make([]api.IngressRule, len(inRule.Ingress)) 95 for i, ing := range inRule.Ingress { 96 if ing.FromEndpoints != nil { 97 retRule.Ingress[i].FromEndpoints = make([]api.EndpointSelector, len(ing.FromEndpoints)) 98 for j, ep := range ing.FromEndpoints { 99 retRule.Ingress[i].FromEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit) 100 } 101 } 102 103 if ing.ToPorts != nil { 104 retRule.Ingress[i].ToPorts = make([]api.PortRule, len(ing.ToPorts)) 105 copy(retRule.Ingress[i].ToPorts, ing.ToPorts) 106 } 107 if ing.FromCIDR != nil { 108 retRule.Ingress[i].FromCIDR = make([]api.CIDR, len(ing.FromCIDR)) 109 copy(retRule.Ingress[i].FromCIDR, ing.FromCIDR) 110 } 111 112 if ing.FromCIDRSet != nil { 113 retRule.Ingress[i].FromCIDRSet = make([]api.CIDRRule, len(ing.FromCIDRSet)) 114 copy(retRule.Ingress[i].FromCIDRSet, ing.FromCIDRSet) 115 } 116 117 if ing.FromRequires != nil { 118 retRule.Ingress[i].FromRequires = make([]api.EndpointSelector, len(ing.FromRequires)) 119 for j, ep := range ing.FromRequires { 120 retRule.Ingress[i].FromRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit) 121 } 122 } 123 124 if ing.FromEntities != nil { 125 retRule.Ingress[i].FromEntities = make([]api.Entity, len(ing.FromEntities)) 126 copy(retRule.Ingress[i].FromEntities, ing.FromEntities) 127 } 128 129 retRule.Ingress[i].SetAggregatedSelectors() 130 } 131 } 132 } 133 134 func parseToCiliumEgressRule(namespace string, inRule, retRule *api.Rule) { 135 matchesInit := retRule.EndpointSelector.HasKey(podInitLbl) 136 137 if inRule.Egress != nil { 138 retRule.Egress = make([]api.EgressRule, len(inRule.Egress)) 139 140 for i, egr := range inRule.Egress { 141 if egr.ToEndpoints != nil { 142 retRule.Egress[i].ToEndpoints = make([]api.EndpointSelector, len(egr.ToEndpoints)) 143 for j, ep := range egr.ToEndpoints { 144 retRule.Egress[i].ToEndpoints[j] = getEndpointSelector(namespace, ep.LabelSelector, true, matchesInit) 145 } 146 } 147 148 if egr.ToPorts != nil { 149 retRule.Egress[i].ToPorts = make([]api.PortRule, len(egr.ToPorts)) 150 copy(retRule.Egress[i].ToPorts, egr.ToPorts) 151 } 152 if egr.ToCIDR != nil { 153 retRule.Egress[i].ToCIDR = make([]api.CIDR, len(egr.ToCIDR)) 154 copy(retRule.Egress[i].ToCIDR, egr.ToCIDR) 155 } 156 157 if egr.ToCIDRSet != nil { 158 retRule.Egress[i].ToCIDRSet = make(api.CIDRRuleSlice, len(egr.ToCIDRSet)) 159 copy(retRule.Egress[i].ToCIDRSet, egr.ToCIDRSet) 160 } 161 162 if egr.ToRequires != nil { 163 retRule.Egress[i].ToRequires = make([]api.EndpointSelector, len(egr.ToRequires)) 164 for j, ep := range egr.ToRequires { 165 retRule.Egress[i].ToRequires[j] = getEndpointSelector(namespace, ep.LabelSelector, false, matchesInit) 166 } 167 } 168 169 if egr.ToServices != nil { 170 retRule.Egress[i].ToServices = make([]api.Service, len(egr.ToServices)) 171 copy(retRule.Egress[i].ToServices, egr.ToServices) 172 } 173 174 if egr.ToEntities != nil { 175 retRule.Egress[i].ToEntities = make([]api.Entity, len(egr.ToEntities)) 176 copy(retRule.Egress[i].ToEntities, egr.ToEntities) 177 } 178 179 if egr.ToFQDNs != nil { 180 retRule.Egress[i].ToFQDNs = make([]api.FQDNSelector, len(egr.ToFQDNs)) 181 copy(retRule.Egress[i].ToFQDNs, egr.ToFQDNs) 182 } 183 184 if egr.ToGroups != nil { 185 retRule.Egress[i].ToGroups = make([]api.ToGroups, len(egr.ToGroups)) 186 copy(retRule.Egress[i].ToGroups, egr.ToGroups) 187 } 188 189 retRule.Egress[i].SetAggregatedSelectors() 190 } 191 } 192 } 193 194 // namespacesAreValid checks the set of namespaces from a rule returns true if 195 // they are not specified, or if they are specified and match the namespace 196 // where the rule is being inserted. 197 func namespacesAreValid(namespace string, userNamespaces []string) bool { 198 return len(userNamespaces) == 0 || 199 (len(userNamespaces) == 1 && userNamespaces[0] == namespace) 200 } 201 202 // ParseToCiliumRule returns an api.Rule with all the labels parsed into cilium 203 // labels. 204 func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api.Rule { 205 retRule := &api.Rule{} 206 if r.EndpointSelector.LabelSelector != nil { 207 retRule.EndpointSelector = api.NewESFromK8sLabelSelector("", r.EndpointSelector.LabelSelector) 208 // The PodSelector should only reflect to the same namespace 209 // the policy is being stored, thus we add the namespace to 210 // the MatchLabels map. 211 // 212 // Policies applying on initializing pods are a special case. 213 // Those pods don't have any labels, so they don't have a namespace label either. 214 // Don't add a namespace label to those endpoint selectors, or we wouldn't be 215 // able to match on those pods. 216 if !retRule.EndpointSelector.HasKey(podInitLbl) { 217 userNamespace, present := r.EndpointSelector.GetMatch(podPrefixLbl) 218 if present && !namespacesAreValid(namespace, userNamespace) { 219 log.WithFields(logrus.Fields{ 220 logfields.K8sNamespace: namespace, 221 logfields.CiliumNetworkPolicyName: name, 222 logfields.K8sNamespace + ".illegal": userNamespace, 223 }).Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector." + 224 " EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.") 225 } 226 retRule.EndpointSelector.AddMatch(podPrefixLbl, namespace) 227 } 228 } 229 230 parseToCiliumIngressRule(namespace, r, retRule) 231 parseToCiliumEgressRule(namespace, r, retRule) 232 233 retRule.Labels = ParseToCiliumLabels(namespace, name, uid, r.Labels) 234 235 retRule.Description = r.Description 236 237 return retRule 238 } 239 240 // ParseToCiliumLabels returns all ruleLbls appended with a specific label that 241 // represents the given namespace and name along with a label that specifies 242 // these labels were derived from a CiliumNetworkPolicy. 243 func ParseToCiliumLabels(namespace, name string, uid types.UID, ruleLbs labels.LabelArray) labels.LabelArray { 244 policyLbls := GetPolicyLabels(namespace, name, uid, ResourceTypeCiliumNetworkPolicy) 245 return append(policyLbls, ruleLbs...).Sort() 246 }