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  }