1// Copyright 2018 Istio Authors
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
15package model
16
17import (
18	"encoding/json"
19	"net"
20	"sort"
21	"strings"
22	"sync"
23	"time"
24
25	meshconfig "istio.io/api/mesh/v1alpha1"
26	networking "istio.io/api/networking/v1alpha3"
27	"istio.io/pkg/monitoring"
28
29	"istio.io/istio/pilot/pkg/features"
30	"istio.io/istio/pkg/config/constants"
31	"istio.io/istio/pkg/config/host"
32	"istio.io/istio/pkg/config/labels"
33	"istio.io/istio/pkg/config/protocol"
34	"istio.io/istio/pkg/config/schema/collections"
35	"istio.io/istio/pkg/config/visibility"
36)
37
38var (
39	defaultClusterLocalNamespaces = []string{"kube-system"}
40)
41
42// Metrics is an interface for capturing metrics on a per-node basis.
43type Metrics interface {
44	// AddMetric will add an case to the metric for the given node.
45	AddMetric(metric monitoring.Metric, key string, proxy *Proxy, msg string)
46}
47
48var _ Metrics = &PushContext{}
49
50// PushContext tracks the status of a push - metrics and errors.
51// Metrics are reset after a push - at the beginning all
52// values are zero, and when push completes the status is reset.
53// The struct is exposed in a debug endpoint - fields public to allow
54// easy serialization as json.
55type PushContext struct {
56	proxyStatusMutex sync.RWMutex
57	// ProxyStatus is keyed by the error code, and holds a map keyed
58	// by the ID.
59	ProxyStatus map[string]map[string]ProxyPushStatus
60
61	// Mutex is used to protect the below store.
62	// All data is set when the PushContext object is populated in `InitContext`,
63	// data should not be changed by plugins.
64	Mutex sync.Mutex `json:"-"`
65
66	// Synthesized from env.Mesh
67	defaultServiceExportTo         map[visibility.Instance]bool
68	defaultVirtualServiceExportTo  map[visibility.Instance]bool
69	defaultDestinationRuleExportTo map[visibility.Instance]bool
70
71	// Service related
72	// TODO: move to a sub struct
73
74	// privateServices are reachable within the same namespace.
75	privateServicesByNamespace map[string][]*Service
76	// publicServices are services reachable within the mesh.
77	publicServices []*Service
78	// ServiceByHostnameAndNamespace has all services, indexed by hostname then namespace.
79	ServiceByHostnameAndNamespace map[host.Name]map[string]*Service `json:"-"`
80	// ServiceAccounts contains a map of hostname and port to service accounts.
81	ServiceAccounts map[host.Name]map[int][]string `json:"-"`
82	// QuotaSpec has all quota specs
83	QuotaSpec []Config `json:"-"`
84	// QuotaSpecBindings has all quota bindings
85	QuotaSpecBinding []Config `json:"-"`
86
87	// VirtualService related
88	// this contains all the virtual services with exportTo "." and current namespace. The keys are namespace,gateway.
89	privateVirtualServicesByNamespaceAndGateway map[string]map[string][]Config
90	// This contains all virtual services whose exportTo is "*", keyed by gateway
91	publicVirtualServicesByGateway map[string][]Config
92
93	// destination rules are of two types:
94	//  namespaceLocalDestRules: all public/private dest rules pertaining to a service defined in a given namespace
95	//  namespaceExportedDestRules: all public dest rules pertaining to a service defined in a namespace
96	namespaceLocalDestRules    map[string]*processedDestRules
97	namespaceExportedDestRules map[string]*processedDestRules
98
99	// clusterLocalHosts extracted from the MeshConfig
100	clusterLocalHosts host.Names
101
102	// sidecars for each namespace
103	sidecarsByNamespace map[string][]*SidecarScope
104	// envoy filters for each namespace including global config namespace
105	envoyFiltersByNamespace map[string][]*EnvoyFilterWrapper
106	// gateways for each namespace
107	gatewaysByNamespace map[string][]Config
108	allGateways         []Config
109	////////// END ////////
110
111	// The following data is either a global index or used in the inbound path.
112	// Namespace specific views do not apply here.
113
114	// AuthzPolicies stores the existing authorization policies in the cluster. Could be nil if there
115	// are no authorization policies in the cluster.
116	AuthzPolicies *AuthorizationPolicies `json:"-"`
117
118	// Mesh configuration for the mesh.
119	Mesh *meshconfig.MeshConfig `json:"-"`
120
121	// Networks configuration.
122	Networks *meshconfig.MeshNetworks `json:"-"`
123
124	// Discovery interface for listing services and instances.
125	ServiceDiscovery `json:"-"`
126
127	// Config interface for listing routing rules
128	IstioConfigStore `json:"-"`
129
130	// AuthnBetaPolicies contains (beta) Authn policies by namespace.
131	AuthnBetaPolicies *AuthenticationPolicies `json:"-"`
132
133	initDone bool
134
135	Version string
136
137	// cache gateways addresses for each network
138	// this is mainly used for kubernetes multi-cluster scenario
139	networkGateways map[string][]*Gateway
140}
141
142// Gateway is the gateway of a network
143type Gateway struct {
144	// gateway ip address
145	Addr string
146	// gateway port
147	Port uint32
148}
149
150type processedDestRules struct {
151	// List of dest rule hosts. We match with the most specific host first
152	hosts []host.Name
153	// Map of dest rule host and the merged destination rules for that host
154	destRule map[host.Name]*Config
155}
156
157// XDSUpdater is used for direct updates of the xDS model and incremental push.
158// Pilot uses multiple registries - for example each K8S cluster is a registry instance,
159// as well as consul and future EDS or MCP sources. Each registry is responsible for
160// tracking a set of endpoints associated with mesh services, and calling the EDSUpdate
161// on changes. A registry may group endpoints for a service in smaller subsets - for
162// example by deployment, or to deal with very large number of endpoints for a service.
163// We want to avoid passing around large objects - like full list of endpoints for a registry,
164// or the full list of endpoints for a service across registries, since it limits scalability.
165//
166// Future optimizations will include grouping the endpoints by labels, gateway or region to
167// reduce the time when subsetting or split-horizon is used. This design assumes pilot
168// tracks all endpoints in the mesh and they fit in RAM - so limit is few M endpoints.
169// It is possible to split the endpoint tracking in future.
170type XDSUpdater interface {
171
172	// EDSUpdate is called when the list of endpoints or labels in a ServiceEntry is
173	// changed. For each cluster and hostname, the full list of active endpoints (including empty list)
174	// must be sent. The shard name is used as a key - current implementation is using the registry
175	// name.
176	EDSUpdate(shard, hostname string, namespace string, entry []*IstioEndpoint) error
177
178	// SvcUpdate is called when a service definition is updated/deleted.
179	SvcUpdate(shard, hostname string, namespace string, event Event)
180
181	// ConfigUpdate is called to notify the XDS server of config updates and request a push.
182	// The requests may be collapsed and throttled.
183	// This replaces the 'cache invalidation' model.
184	ConfigUpdate(req *PushRequest)
185
186	// ProxyUpdate is called to notify the XDS server to send a push to the specified proxy.
187	// The requests may be collapsed and throttled.
188	ProxyUpdate(clusterID, ip string)
189}
190
191// PushRequest defines a request to push to proxies
192// It is used to send updates to the config update debouncer and pass to the PushQueue.
193type PushRequest struct {
194	// Full determines whether a full push is required or not. If set to false, only endpoints will be sent.
195	Full bool
196
197	// ConfigsUpdated keeps track of configs that have changed.
198	// This is used as an optimization to avoid unnecessary pushes to proxies that are scoped with a Sidecar.
199	// If this is empty, then all proxies will get an update.
200	// Otherwise only proxies depend on these configs will get an update.
201	// The kind of resources are defined in pkg/config/schemas.
202	ConfigsUpdated map[ConfigKey]struct{}
203
204	// Push stores the push context to use for the update. This may initially be nil, as we will
205	// debounce changes before a PushContext is eventually created.
206	Push *PushContext
207
208	// Start represents the time a push was started. This represents the time of adding to the PushQueue.
209	// Note that this does not include time spent debouncing.
210	Start time.Time
211
212	// Reason represents the reason for requesting a push. This should only be a fixed set of values,
213	// to avoid unbounded cardinality in metrics. If this is not set, it may be automatically filled in later.
214	// There should only be multiple reasons if the push request is the result of two distinct triggers, rather than
215	// classifying a single trigger as having multiple reasons.
216	Reason []TriggerReason
217}
218
219type TriggerReason string
220
221const (
222	// Describes a push triggered by an Endpoint change
223	EndpointUpdate TriggerReason = "endpoint"
224	// Describes a push triggered by a config (generally and Istio CRD) change.
225	ConfigUpdate TriggerReason = "config"
226	// Describes a push triggered by a Service change
227	ServiceUpdate TriggerReason = "service"
228	// Describes a push triggered by a change to an individual proxy (such as label change)
229	ProxyUpdate TriggerReason = "proxy"
230	// Describes a push triggered by a change to global config, such as mesh config
231	GlobalUpdate TriggerReason = "global"
232	// Describes a push triggered by an unknown reason
233	UnknownTrigger TriggerReason = "unknown"
234	// Describes a push triggered for debugging
235	DebugTrigger TriggerReason = "debug"
236)
237
238var (
239	ServiceEntryKind    = collections.IstioNetworkingV1Alpha3Serviceentries.Resource().GroupVersionKind()
240	VirtualServiceKind  = collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind()
241	DestinationRuleKind = collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind()
242)
243
244// Merge two update requests together
245func (first *PushRequest) Merge(other *PushRequest) *PushRequest {
246	if first == nil {
247		return other
248	}
249	if other == nil {
250		return first
251	}
252
253	merged := &PushRequest{
254		// Keep the first (older) start time
255		Start: first.Start,
256
257		// If either is full we need a full push
258		Full: first.Full || other.Full,
259
260		// The other push context is presumed to be later and more up to date
261		Push: other.Push,
262
263		// Merge the two reasons. Note that we shouldn't deduplicate here, or we would under count
264		Reason: append(first.Reason, other.Reason...),
265	}
266
267	// Do not merge when any one is empty
268	if len(first.ConfigsUpdated) > 0 && len(other.ConfigsUpdated) > 0 {
269		merged.ConfigsUpdated = make(map[ConfigKey]struct{}, len(first.ConfigsUpdated)+len(other.ConfigsUpdated))
270		for conf := range first.ConfigsUpdated {
271			merged.ConfigsUpdated[conf] = struct{}{}
272		}
273		for conf := range other.ConfigsUpdated {
274			merged.ConfigsUpdated[conf] = struct{}{}
275		}
276	}
277
278	return merged
279}
280
281// ProxyPushStatus represents an event captured during config push to proxies.
282// It may contain additional message and the affected proxy.
283type ProxyPushStatus struct {
284	Proxy   string `json:"proxy,omitempty"`
285	Message string `json:"message,omitempty"`
286}
287
288// IsMixerEnabled returns true if mixer is enabled in the Mesh config.
289func (ps *PushContext) IsMixerEnabled() bool {
290	return ps != nil && ps.Mesh != nil && (ps.Mesh.MixerCheckServer != "" || ps.Mesh.MixerReportServer != "")
291}
292
293// AddMetric will add an case to the metric.
294func (ps *PushContext) AddMetric(metric monitoring.Metric, key string, proxy *Proxy, msg string) {
295	if ps == nil {
296		log.Infof("Metric without context %s %v %s", key, proxy, msg)
297		return
298	}
299	ps.proxyStatusMutex.Lock()
300	defer ps.proxyStatusMutex.Unlock()
301
302	metricMap, f := ps.ProxyStatus[metric.Name()]
303	if !f {
304		metricMap = map[string]ProxyPushStatus{}
305		ps.ProxyStatus[metric.Name()] = metricMap
306	}
307	ev := ProxyPushStatus{Message: msg}
308	if proxy != nil {
309		ev.Proxy = proxy.ID
310	}
311	metricMap[key] = ev
312}
313
314var (
315
316	// EndpointNoPod tracks endpoints without an associated pod. This is an error condition, since
317	// we can't figure out the labels. It may be a transient problem, if endpoint is processed before
318	// pod.
319	EndpointNoPod = monitoring.NewGauge(
320		"endpoint_no_pod",
321		"Endpoints without an associated pod.",
322	)
323
324	// ProxyStatusNoService represents proxies not selected by any service
325	// This can be normal - for workloads that act only as client, or are not covered by a Service.
326	// It can also be an error, for example in cases the Endpoint list of a service was not updated by the time
327	// the sidecar calls.
328	// Updated by GetProxyServiceInstances
329	ProxyStatusNoService = monitoring.NewGauge(
330		"pilot_no_ip",
331		"Pods not found in the endpoint table, possibly invalid.",
332	)
333
334	// ProxyStatusEndpointNotReady represents proxies found not be ready.
335	// Updated by GetProxyServiceInstances. Normal condition when starting
336	// an app with readiness, error if it doesn't change to 0.
337	ProxyStatusEndpointNotReady = monitoring.NewGauge(
338		"pilot_endpoint_not_ready",
339		"Endpoint found in unready state.",
340	)
341
342	// ProxyStatusConflictOutboundListenerTCPOverHTTP metric tracks number of
343	// wildcard TCP listeners that conflicted with existing wildcard HTTP listener on same port
344	ProxyStatusConflictOutboundListenerTCPOverHTTP = monitoring.NewGauge(
345		"pilot_conflict_outbound_listener_tcp_over_current_http",
346		"Number of conflicting wildcard tcp listeners with current wildcard http listener.",
347	)
348
349	// ProxyStatusConflictOutboundListenerTCPOverTCP metric tracks number of
350	// TCP listeners that conflicted with existing TCP listeners on same port
351	ProxyStatusConflictOutboundListenerTCPOverTCP = monitoring.NewGauge(
352		"pilot_conflict_outbound_listener_tcp_over_current_tcp",
353		"Number of conflicting tcp listeners with current tcp listener.",
354	)
355
356	// ProxyStatusConflictOutboundListenerTCPOverThrift metric tracks number of
357	// TCP listeners that conflicted with existing Thrift listeners on same port
358	ProxyStatusConflictOutboundListenerTCPOverThrift = monitoring.NewGauge(
359		"pilot_conflict_outbound_listener_tcp_over_current_thrift",
360		"Number of conflicting tcp listeners with current thrift listener.",
361	)
362
363	// ProxyStatusConflictOutboundListenerHTTPOverTCP metric tracks number of
364	// wildcard HTTP listeners that conflicted with existing wildcard TCP listener on same port
365	ProxyStatusConflictOutboundListenerHTTPOverTCP = monitoring.NewGauge(
366		"pilot_conflict_outbound_listener_http_over_current_tcp",
367		"Number of conflicting wildcard http listeners with current wildcard tcp listener.",
368	)
369
370	// ProxyStatusConflictInboundListener tracks cases of multiple inbound
371	// listeners - 2 services selecting the same port of the pod.
372	ProxyStatusConflictInboundListener = monitoring.NewGauge(
373		"pilot_conflict_inbound_listener",
374		"Number of conflicting inbound listeners.",
375	)
376
377	// DuplicatedClusters tracks duplicate clusters seen while computing CDS
378	DuplicatedClusters = monitoring.NewGauge(
379		"pilot_duplicate_envoy_clusters",
380		"Duplicate envoy clusters caused by service entries with same hostname",
381	)
382
383	// DNSNoEndpointClusters tracks dns clusters without endpoints
384	DNSNoEndpointClusters = monitoring.NewGauge(
385		"pilot_dns_cluster_without_endpoints",
386		"DNS clusters without endpoints caused by the endpoint field in "+
387			"STRICT_DNS type cluster is not set or the corresponding subset cannot select any endpoint",
388	)
389
390	// ProxyStatusClusterNoInstances tracks clusters (services) without workloads.
391	ProxyStatusClusterNoInstances = monitoring.NewGauge(
392		"pilot_eds_no_instances",
393		"Number of clusters without instances.",
394	)
395
396	// DuplicatedDomains tracks rejected VirtualServices due to duplicated hostname.
397	DuplicatedDomains = monitoring.NewGauge(
398		"pilot_vservice_dup_domain",
399		"Virtual services with dup domains.",
400	)
401
402	// DuplicatedSubsets tracks duplicate subsets that we rejected while merging multiple destination rules for same host
403	DuplicatedSubsets = monitoring.NewGauge(
404		"pilot_destrule_subsets",
405		"Duplicate subsets across destination rules for same host",
406	)
407
408	// totalVirtualServices tracks the total number of virtual service
409	totalVirtualServices = monitoring.NewGauge(
410		"pilot_virt_services",
411		"Total virtual services known to pilot.",
412	)
413
414	// LastPushStatus preserves the metrics and data collected during lasts global push.
415	// It can be used by debugging tools to inspect the push event. It will be reset after each push with the
416	// new version.
417	LastPushStatus *PushContext
418	// LastPushMutex will protect the LastPushStatus
419	LastPushMutex sync.Mutex
420
421	// All metrics we registered.
422	metrics = []monitoring.Metric{
423		EndpointNoPod,
424		ProxyStatusNoService,
425		ProxyStatusEndpointNotReady,
426		ProxyStatusConflictOutboundListenerTCPOverHTTP,
427		ProxyStatusConflictOutboundListenerTCPOverTCP,
428		ProxyStatusConflictOutboundListenerHTTPOverTCP,
429		ProxyStatusConflictInboundListener,
430		DuplicatedClusters,
431		ProxyStatusClusterNoInstances,
432		DuplicatedDomains,
433		DuplicatedSubsets,
434	}
435)
436
437func init() {
438	for _, m := range metrics {
439		monitoring.MustRegister(m)
440	}
441	monitoring.MustRegister(totalVirtualServices)
442}
443
444// NewPushContext creates a new PushContext structure to track push status.
445func NewPushContext() *PushContext {
446	// TODO: detect push in progress, don't update status if set
447	return &PushContext{
448		publicServices:                              []*Service{},
449		privateServicesByNamespace:                  map[string][]*Service{},
450		publicVirtualServicesByGateway:              map[string][]Config{},
451		privateVirtualServicesByNamespaceAndGateway: map[string]map[string][]Config{},
452		namespaceLocalDestRules:                     map[string]*processedDestRules{},
453		namespaceExportedDestRules:                  map[string]*processedDestRules{},
454		sidecarsByNamespace:                         map[string][]*SidecarScope{},
455		envoyFiltersByNamespace:                     map[string][]*EnvoyFilterWrapper{},
456		gatewaysByNamespace:                         map[string][]Config{},
457		allGateways:                                 []Config{},
458		ServiceByHostnameAndNamespace:               map[host.Name]map[string]*Service{},
459		ProxyStatus:                                 map[string]map[string]ProxyPushStatus{},
460		ServiceAccounts:                             map[host.Name]map[int][]string{},
461	}
462}
463
464// JSON implements json.Marshaller, with a lock.
465func (ps *PushContext) StatusJSON() ([]byte, error) {
466	if ps == nil {
467		return []byte{'{', '}'}, nil
468	}
469	ps.proxyStatusMutex.RLock()
470	defer ps.proxyStatusMutex.RUnlock()
471	return json.MarshalIndent(ps.ProxyStatus, "", "    ")
472}
473
474// OnConfigChange is called when a config change is detected.
475func (ps *PushContext) OnConfigChange() {
476	LastPushMutex.Lock()
477	LastPushStatus = ps
478	LastPushMutex.Unlock()
479	ps.UpdateMetrics()
480}
481
482// UpdateMetrics will update the prometheus metrics based on the
483// current status of the push.
484func (ps *PushContext) UpdateMetrics() {
485	ps.proxyStatusMutex.RLock()
486	defer ps.proxyStatusMutex.RUnlock()
487
488	for _, pm := range metrics {
489		mmap := ps.ProxyStatus[pm.Name()]
490		pm.Record(float64(len(mmap)))
491	}
492}
493
494func virtualServiceDestinations(v *networking.VirtualService) []*networking.Destination {
495	if v == nil {
496		return nil
497	}
498
499	var ds []*networking.Destination
500
501	for _, h := range v.Http {
502		for _, r := range h.Route {
503			if r.Destination != nil {
504				ds = append(ds, r.Destination)
505			}
506		}
507		if h.Mirror != nil {
508			ds = append(ds, h.Mirror)
509		}
510	}
511	for _, t := range v.Tcp {
512		for _, r := range t.Route {
513			if r.Destination != nil {
514				ds = append(ds, r.Destination)
515			}
516		}
517	}
518	for _, t := range v.Tls {
519		for _, r := range t.Route {
520			if r.Destination != nil {
521				ds = append(ds, r.Destination)
522			}
523		}
524	}
525
526	return ds
527}
528
529// GatewayServices returns the set of services which are referred from the proxy gateways.
530func (ps *PushContext) GatewayServices(proxy *Proxy) []*Service {
531	svcs := ps.Services(proxy)
532	// host set.
533	hostsFromGateways := map[string]struct{}{}
534
535	// MergedGateway will be nil when there are no configs in the
536	// system during initial installation.
537	if proxy.MergedGateway == nil {
538		return nil
539	}
540
541	for _, gw := range proxy.MergedGateway.GatewayNameForServer {
542		for _, vsConfig := range ps.VirtualServicesForGateway(proxy, gw) {
543			vs, ok := vsConfig.Spec.(*networking.VirtualService)
544			if !ok { // should never happen
545				log.Errorf("Failed in getting a virtual service: %v", vsConfig.Labels)
546				return svcs
547			}
548
549			for _, d := range virtualServiceDestinations(vs) {
550				hostsFromGateways[d.Host] = struct{}{}
551			}
552		}
553	}
554
555	log.Debugf("GatewayServices: gateway %v is exposing these hosts:%v", proxy.ID, hostsFromGateways)
556
557	gwSvcs := make([]*Service, 0, len(svcs))
558
559	for _, s := range svcs {
560		svcHost := string(s.Hostname)
561
562		if _, ok := hostsFromGateways[svcHost]; ok {
563			gwSvcs = append(gwSvcs, s)
564		}
565	}
566
567	log.Debugf("GatewayServices:: gateways len(services)=%d, len(filtered)=%d", len(svcs), len(gwSvcs))
568
569	return gwSvcs
570}
571
572// Services returns the list of services that are visible to a Proxy in a given config namespace
573func (ps *PushContext) Services(proxy *Proxy) []*Service {
574	// If proxy has a sidecar scope that is user supplied, then get the services from the sidecar scope
575	// sidecarScope.config is nil if there is no sidecar scope for the namespace
576	if proxy != nil && proxy.SidecarScope != nil && proxy.Type == SidecarProxy {
577		return proxy.SidecarScope.Services()
578	}
579
580	out := make([]*Service, 0)
581
582	// First add private services
583	if proxy == nil {
584		for _, privateServices := range ps.privateServicesByNamespace {
585			out = append(out, privateServices...)
586		}
587	} else {
588		out = append(out, ps.privateServicesByNamespace[proxy.ConfigNamespace]...)
589	}
590
591	// Second add public services
592	out = append(out, ps.publicServices...)
593
594	return out
595}
596
597func (ps *PushContext) VirtualServicesForGateway(proxy *Proxy, gateway string) []Config {
598	res := ps.privateVirtualServicesByNamespaceAndGateway[proxy.ConfigNamespace][gateway]
599	res = append(res, ps.publicVirtualServicesByGateway[gateway]...)
600	return res
601}
602
603// getSidecarScope returns a SidecarScope object associated with the
604// proxy. The SidecarScope object is a semi-processed view of the service
605// registry, and config state associated with the sidecar crd. The scope contains
606// a set of inbound and outbound listeners, services/configs per listener,
607// etc. The sidecar scopes are precomputed in the initSidecarContext
608// function based on the Sidecar API objects in each namespace. If there is
609// no sidecar api object, a default sidecarscope is assigned to the
610// namespace which enables connectivity to all services in the mesh.
611//
612// Callers can check if the sidecarScope is from user generated object or not
613// by checking the sidecarScope.Config field, that contains the user provided config
614func (ps *PushContext) getSidecarScope(proxy *Proxy, workloadLabels labels.Collection) *SidecarScope {
615
616	// Find the most specific matching sidecar config from the proxy's
617	// config namespace If none found, construct a sidecarConfig on the fly
618	// that allows the sidecar to talk to any namespace (the default
619	// behavior in the absence of sidecars).
620	if sidecars, ok := ps.sidecarsByNamespace[proxy.ConfigNamespace]; ok {
621		// TODO: logic to merge multiple sidecar resources
622		// Currently we assume that there will be only one sidecar config for a namespace.
623		var defaultSidecar *SidecarScope
624		for _, wrapper := range sidecars {
625			if wrapper.Config != nil {
626				sidecar := wrapper.Config.Spec.(*networking.Sidecar)
627				// if there is no workload selector, the config applies to all workloads
628				// if there is a workload selector, check for matching workload labels
629				if sidecar.GetWorkloadSelector() != nil {
630					workloadSelector := labels.Instance(sidecar.GetWorkloadSelector().GetLabels())
631					if !workloadLabels.IsSupersetOf(workloadSelector) {
632						continue
633					}
634					return wrapper
635				}
636				defaultSidecar = wrapper
637				continue
638			}
639			// Not sure when this can happen (Config = nil ?)
640			if defaultSidecar != nil {
641				return defaultSidecar // still return the valid one
642			}
643			return wrapper
644		}
645		if defaultSidecar != nil {
646			return defaultSidecar // still return the valid one
647		}
648	}
649
650	return DefaultSidecarScopeForNamespace(ps, proxy.ConfigNamespace)
651}
652
653// GetAllSidecarScopes returns a map of namespace and the set of SidecarScope
654// object associated with the namespace. This will be used by the CDS code to
655// precompute CDS output for each sidecar scope. Since we have a default sidecarscope
656// for namespaces that do not explicitly have one, we are guaranteed to
657// have the CDS output cached for every namespace/sidecar scope combo.
658func (ps *PushContext) GetAllSidecarScopes() map[string][]*SidecarScope {
659	return ps.sidecarsByNamespace
660}
661
662// DestinationRule returns a destination rule for a service name in a given domain.
663func (ps *PushContext) DestinationRule(proxy *Proxy, service *Service) *Config {
664	// If proxy has a sidecar scope that is user supplied, then get the destination rules from the sidecar scope
665	// sidecarScope.config is nil if there is no sidecar scope for the namespace
666	if proxy.SidecarScope != nil && proxy.Type == SidecarProxy {
667		// If there is a sidecar scope for this proxy, return the destination rule
668		// from the sidecar scope.
669		return proxy.SidecarScope.DestinationRule(service.Hostname)
670	}
671
672	// If the proxy config namespace is same as the root config namespace
673	// look for dest rules in the service's namespace first. This hack is needed
674	// because sometimes, istio-system tends to become the root config namespace.
675	// Destination rules are defined here for global purposes. We do not want these
676	// catch all destination rules to be the only dest rule, when processing CDS for
677	// proxies like the istio-ingressgateway or istio-egressgateway.
678	// If there are no service specific dest rules, we will end up picking up the same
679	// rules anyway, later in the code
680
681	// 1. select destination rule from proxy config namespace
682	if proxy.ConfigNamespace != ps.Mesh.RootNamespace {
683		// search through the DestinationRules in proxy's namespace first
684		if ps.namespaceLocalDestRules[proxy.ConfigNamespace] != nil {
685			if hostname, ok := MostSpecificHostMatch(service.Hostname,
686				ps.namespaceLocalDestRules[proxy.ConfigNamespace].hosts); ok {
687				return ps.namespaceLocalDestRules[proxy.ConfigNamespace].destRule[hostname]
688			}
689		}
690	}
691
692	// 2. select destination rule from service namespace
693	svcNs := service.Attributes.Namespace
694
695	// This can happen when finding the subset labels for a proxy in root namespace.
696	// Because based on a pure cluster name, we do not know the service and
697	// construct a fake service without setting Attributes at all.
698	if svcNs == "" {
699		for _, svc := range ps.Services(proxy) {
700			if service.Hostname == svc.Hostname && svc.Attributes.Namespace != "" {
701				svcNs = svc.Attributes.Namespace
702				break
703			}
704		}
705	}
706
707	// 3. if no private/public rule matched in the calling proxy's namespace,
708	// check the target service's namespace for public rules
709	if svcNs != "" && ps.namespaceExportedDestRules[svcNs] != nil {
710		if hostname, ok := MostSpecificHostMatch(service.Hostname,
711			ps.namespaceExportedDestRules[svcNs].hosts); ok {
712			return ps.namespaceExportedDestRules[svcNs].destRule[hostname]
713		}
714	}
715
716	// 4. if no public/private rule in calling proxy's namespace matched, and no public rule in the
717	// target service's namespace matched, search for any public destination rule in the config root namespace
718	// NOTE: This does mean that we are effectively ignoring private dest rules in the config root namespace
719	if ps.namespaceExportedDestRules[ps.Mesh.RootNamespace] != nil {
720		if hostname, ok := MostSpecificHostMatch(service.Hostname,
721			ps.namespaceExportedDestRules[ps.Mesh.RootNamespace].hosts); ok {
722			return ps.namespaceExportedDestRules[ps.Mesh.RootNamespace].destRule[hostname]
723		}
724	}
725
726	return nil
727}
728
729// IsClusterLocal indicates whether the endpoints for the service should only be accessible to clients
730// within the cluster.
731func (ps *PushContext) IsClusterLocal(service *Service) bool {
732	_, ok := MostSpecificHostMatch(service.Hostname, ps.clusterLocalHosts)
733	return ok
734}
735
736// SubsetToLabels returns the labels associated with a subset of a given service.
737func (ps *PushContext) SubsetToLabels(proxy *Proxy, subsetName string, hostname host.Name) labels.Collection {
738	// empty subset
739	if subsetName == "" {
740		return nil
741	}
742
743	cfg := ps.DestinationRule(proxy, &Service{Hostname: hostname})
744	if cfg == nil {
745		return nil
746	}
747
748	rule := cfg.Spec.(*networking.DestinationRule)
749	for _, subset := range rule.Subsets {
750		if subset.Name == subsetName {
751			if len(subset.Labels) == 0 {
752				return nil
753			}
754			return []labels.Instance{subset.Labels}
755		}
756	}
757
758	return nil
759}
760
761// InitContext will initialize the data structures used for code generation.
762// This should be called before starting the push, from the thread creating
763// the push context.
764func (ps *PushContext) InitContext(env *Environment, oldPushContext *PushContext, pushReq *PushRequest) error {
765	ps.Mutex.Lock()
766	defer ps.Mutex.Unlock()
767	if ps.initDone {
768		return nil
769	}
770
771	ps.Mesh = env.Mesh()
772	ps.Networks = env.Networks()
773	ps.ServiceDiscovery = env
774	ps.IstioConfigStore = env
775	ps.Version = env.Version()
776
777	// Must be initialized first
778	// as initServiceRegistry/VirtualServices/Destrules
779	// use the default export map
780	ps.initDefaultExportMaps()
781
782	// create new or incremental update
783	if pushReq == nil || oldPushContext == nil || !oldPushContext.initDone || len(pushReq.ConfigsUpdated) == 0 {
784		if err := ps.createNewContext(env); err != nil {
785			return err
786		}
787	} else {
788		if err := ps.updateContext(env, oldPushContext, pushReq); err != nil {
789			return err
790		}
791	}
792
793	// TODO: only do this when meshnetworks or gateway service changed
794	ps.initMeshNetworks()
795
796	ps.initClusterLocalHosts(env)
797
798	ps.initDone = true
799	return nil
800}
801
802func (ps *PushContext) createNewContext(env *Environment) error {
803	if err := ps.initServiceRegistry(env); err != nil {
804		return err
805	}
806
807	if err := ps.initVirtualServices(env); err != nil {
808		return err
809	}
810
811	if err := ps.initDestinationRules(env); err != nil {
812		return err
813	}
814
815	if err := ps.initAuthnPolicies(env); err != nil {
816		return err
817	}
818
819	if err := ps.initAuthorizationPolicies(env); err != nil {
820		authzLog.Errorf("failed to initialize authorization policies: %v", err)
821		return err
822	}
823
824	if err := ps.initEnvoyFilters(env); err != nil {
825		return err
826	}
827
828	if err := ps.initGateways(env); err != nil {
829		return err
830	}
831
832	if err := ps.initQuotaSpecs(env); err != nil {
833		return err
834	}
835
836	if err := ps.initQuotaSpecBindings(env); err != nil {
837		return err
838	}
839
840	// Must be initialized in the end
841	if err := ps.initSidecarScopes(env); err != nil {
842		return err
843	}
844	return nil
845}
846
847func (ps *PushContext) updateContext(
848	env *Environment,
849	oldPushContext *PushContext,
850	pushReq *PushRequest) error {
851
852	var servicesChanged, virtualServicesChanged, destinationRulesChanged, gatewayChanged,
853		authnChanged, authzChanged, envoyFiltersChanged, sidecarsChanged, quotasChanged bool
854
855	for conf := range pushReq.ConfigsUpdated {
856		switch conf.Kind {
857		case ServiceEntryKind:
858			servicesChanged = true
859		case DestinationRuleKind:
860			destinationRulesChanged = true
861		case VirtualServiceKind:
862			virtualServicesChanged = true
863		case collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind():
864			gatewayChanged = true
865		case collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind():
866			sidecarsChanged = true
867		case collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind():
868			envoyFiltersChanged = true
869		case collections.IstioRbacV1Alpha1Servicerolebindings.Resource().GroupVersionKind(),
870			collections.IstioRbacV1Alpha1Serviceroles.Resource().GroupVersionKind(),
871			collections.IstioRbacV1Alpha1Clusterrbacconfigs.Resource().GroupVersionKind(),
872			collections.IstioRbacV1Alpha1Rbacconfigs.Resource().GroupVersionKind(),
873			collections.IstioSecurityV1Beta1Authorizationpolicies.Resource().GroupVersionKind():
874			authzChanged = true
875		case collections.IstioSecurityV1Beta1Requestauthentications.Resource().GroupVersionKind(),
876			collections.IstioSecurityV1Beta1Peerauthentications.Resource().GroupVersionKind():
877			authnChanged = true
878		case collections.IstioMixerV1ConfigClientQuotaspecbindings.Resource().GroupVersionKind(),
879			collections.IstioMixerV1ConfigClientQuotaspecs.Resource().GroupVersionKind():
880			quotasChanged = true
881		case collections.K8SServiceApisV1Alpha1Trafficsplits.Resource().GroupVersionKind(),
882			collections.K8SServiceApisV1Alpha1Httproutes.Resource().GroupVersionKind(),
883			collections.K8SServiceApisV1Alpha1Tcproutes.Resource().GroupVersionKind(),
884			collections.K8SServiceApisV1Alpha1Gateways.Resource().GroupVersionKind(),
885			collections.K8SServiceApisV1Alpha1Gatewayclasses.Resource().GroupVersionKind():
886			virtualServicesChanged = true
887			gatewayChanged = true
888		}
889	}
890
891	if servicesChanged {
892		// Services have changed. initialize service registry
893		if err := ps.initServiceRegistry(env); err != nil {
894			return err
895		}
896	} else {
897		ps.privateServicesByNamespace = oldPushContext.privateServicesByNamespace
898		ps.publicServices = oldPushContext.publicServices
899		ps.ServiceByHostnameAndNamespace = oldPushContext.ServiceByHostnameAndNamespace
900		ps.ServiceAccounts = oldPushContext.ServiceAccounts
901	}
902
903	if virtualServicesChanged {
904		if err := ps.initVirtualServices(env); err != nil {
905			return err
906		}
907	} else {
908		ps.privateVirtualServicesByNamespaceAndGateway = oldPushContext.privateVirtualServicesByNamespaceAndGateway
909		ps.publicVirtualServicesByGateway = oldPushContext.publicVirtualServicesByGateway
910	}
911
912	if destinationRulesChanged {
913		if err := ps.initDestinationRules(env); err != nil {
914			return err
915		}
916	} else {
917		ps.namespaceLocalDestRules = oldPushContext.namespaceLocalDestRules
918		ps.namespaceExportedDestRules = oldPushContext.namespaceExportedDestRules
919	}
920
921	if authnChanged {
922		if err := ps.initAuthnPolicies(env); err != nil {
923			return err
924		}
925	} else {
926		ps.AuthnBetaPolicies = oldPushContext.AuthnBetaPolicies
927	}
928
929	if authzChanged {
930		if err := ps.initAuthorizationPolicies(env); err != nil {
931			authzLog.Errorf("failed to initialize authorization policies: %v", err)
932			return err
933		}
934	} else {
935		ps.AuthzPolicies = oldPushContext.AuthzPolicies
936	}
937
938	if envoyFiltersChanged {
939		if err := ps.initEnvoyFilters(env); err != nil {
940			return err
941		}
942	} else {
943		ps.envoyFiltersByNamespace = oldPushContext.envoyFiltersByNamespace
944	}
945
946	if gatewayChanged {
947		if err := ps.initGateways(env); err != nil {
948			return err
949		}
950	} else {
951		ps.gatewaysByNamespace = oldPushContext.gatewaysByNamespace
952		ps.allGateways = oldPushContext.allGateways
953	}
954
955	if quotasChanged {
956		if err := ps.initQuotaSpecs(env); err != nil {
957			return err
958		}
959		if err := ps.initQuotaSpecBindings(env); err != nil {
960			return err
961		}
962	} else {
963		ps.QuotaSpec = oldPushContext.QuotaSpec
964		ps.QuotaSpecBinding = oldPushContext.QuotaSpecBinding
965	}
966
967	// Must be initialized in the end
968	// Sidecars need to be updated if services, virtual services, destination rules, or the sidecar configs change
969	if servicesChanged || virtualServicesChanged || destinationRulesChanged || sidecarsChanged {
970		if err := ps.initSidecarScopes(env); err != nil {
971			return err
972		}
973	} else {
974		ps.sidecarsByNamespace = oldPushContext.sidecarsByNamespace
975	}
976
977	return nil
978}
979
980// Caches list of services in the registry, and creates a map
981// of hostname to service
982func (ps *PushContext) initServiceRegistry(env *Environment) error {
983	services, err := env.Services()
984	if err != nil {
985		return err
986	}
987	// Sort the services in order of creation.
988	allServices := sortServicesByCreationTime(services)
989	for _, s := range allServices {
990		ns := s.Attributes.Namespace
991		if len(s.Attributes.ExportTo) == 0 {
992			if ps.defaultServiceExportTo[visibility.Private] {
993				ps.privateServicesByNamespace[ns] = append(ps.privateServicesByNamespace[ns], s)
994			} else if ps.defaultServiceExportTo[visibility.Public] {
995				ps.publicServices = append(ps.publicServices, s)
996			}
997		} else {
998			if s.Attributes.ExportTo[visibility.Private] {
999				ps.privateServicesByNamespace[ns] = append(ps.privateServicesByNamespace[ns], s)
1000			} else {
1001				ps.publicServices = append(ps.publicServices, s)
1002			}
1003		}
1004		if _, f := ps.ServiceByHostnameAndNamespace[s.Hostname]; !f {
1005			ps.ServiceByHostnameAndNamespace[s.Hostname] = map[string]*Service{}
1006		}
1007		ps.ServiceByHostnameAndNamespace[s.Hostname][s.Attributes.Namespace] = s
1008	}
1009
1010	ps.initServiceAccounts(env, allServices)
1011
1012	return nil
1013}
1014
1015// sortServicesByCreationTime sorts the list of services in ascending order by their creation time (if available).
1016func sortServicesByCreationTime(services []*Service) []*Service {
1017	sort.SliceStable(services, func(i, j int) bool {
1018		return services[i].CreationTime.Before(services[j].CreationTime)
1019	})
1020	return services
1021}
1022
1023// Caches list of service accounts in the registry
1024func (ps *PushContext) initServiceAccounts(env *Environment, services []*Service) {
1025	for _, svc := range services {
1026		if ps.ServiceAccounts[svc.Hostname] == nil {
1027			ps.ServiceAccounts[svc.Hostname] = map[int][]string{}
1028		}
1029		for _, port := range svc.Ports {
1030			if port.Protocol == protocol.UDP {
1031				continue
1032			}
1033			ps.ServiceAccounts[svc.Hostname][port.Port] = env.GetIstioServiceAccounts(svc, []int{port.Port})
1034		}
1035	}
1036}
1037
1038// Caches list of authentication policies
1039func (ps *PushContext) initAuthnPolicies(env *Environment) error {
1040	// Init beta policy.
1041	var initBetaPolicyErro error
1042	if ps.AuthnBetaPolicies, initBetaPolicyErro = initAuthenticationPolicies(env); initBetaPolicyErro != nil {
1043		return initBetaPolicyErro
1044	}
1045
1046	return nil
1047}
1048
1049// Caches list of virtual services
1050func (ps *PushContext) initVirtualServices(env *Environment) error {
1051	ps.privateVirtualServicesByNamespaceAndGateway = map[string]map[string][]Config{}
1052	ps.publicVirtualServicesByGateway = map[string][]Config{}
1053
1054	virtualServices, err := env.List(collections.IstioNetworkingV1Alpha3Virtualservices.Resource().GroupVersionKind(), NamespaceAll)
1055	if err != nil {
1056		return err
1057	}
1058
1059	// values returned from ConfigStore.List are immutable.
1060	// Therefore, we make a copy
1061	vservices := make([]Config, len(virtualServices))
1062
1063	for i := range vservices {
1064		vservices[i] = virtualServices[i].DeepCopy()
1065	}
1066
1067	totalVirtualServices.Record(float64(len(virtualServices)))
1068
1069	// TODO(rshriram): parse each virtual service and maintain a map of the
1070	// virtualservice name, the list of registry hosts in the VS and non
1071	// registry DNS names in the VS.  This should cut down processing in
1072	// the RDS code. See separateVSHostsAndServices in route/route.go
1073	sortConfigByCreationTime(vservices)
1074
1075	vservices = mergeVirtualServicesIfNeeded(vservices)
1076
1077	// convert all shortnames in virtual services into FQDNs
1078	for _, r := range vservices {
1079		rule := r.Spec.(*networking.VirtualService)
1080		// resolve top level hosts
1081		for i, h := range rule.Hosts {
1082			rule.Hosts[i] = string(ResolveShortnameToFQDN(h, r.ConfigMeta))
1083		}
1084		// resolve gateways to bind to
1085		for i, g := range rule.Gateways {
1086			if g != constants.IstioMeshGateway {
1087				rule.Gateways[i] = resolveGatewayName(g, r.ConfigMeta)
1088			}
1089		}
1090		// resolve host in http route.destination, route.mirror
1091		for _, d := range rule.Http {
1092			for _, m := range d.Match {
1093				for i, g := range m.Gateways {
1094					if g != constants.IstioMeshGateway {
1095						m.Gateways[i] = resolveGatewayName(g, r.ConfigMeta)
1096					}
1097				}
1098			}
1099			for _, w := range d.Route {
1100				if w.Destination != nil {
1101					w.Destination.Host = string(ResolveShortnameToFQDN(w.Destination.Host, r.ConfigMeta))
1102				}
1103			}
1104			if d.Mirror != nil {
1105				d.Mirror.Host = string(ResolveShortnameToFQDN(d.Mirror.Host, r.ConfigMeta))
1106			}
1107		}
1108		// resolve host in tcp route.destination
1109		for _, d := range rule.Tcp {
1110			for _, m := range d.Match {
1111				for i, g := range m.Gateways {
1112					if g != constants.IstioMeshGateway {
1113						m.Gateways[i] = resolveGatewayName(g, r.ConfigMeta)
1114					}
1115				}
1116			}
1117			for _, w := range d.Route {
1118				if w.Destination != nil {
1119					w.Destination.Host = string(ResolveShortnameToFQDN(w.Destination.Host, r.ConfigMeta))
1120				}
1121			}
1122		}
1123		//resolve host in tls route.destination
1124		for _, tls := range rule.Tls {
1125			for _, m := range tls.Match {
1126				for i, g := range m.Gateways {
1127					if g != constants.IstioMeshGateway {
1128						m.Gateways[i] = resolveGatewayName(g, r.ConfigMeta)
1129					}
1130				}
1131			}
1132			for _, w := range tls.Route {
1133				if w.Destination != nil {
1134					w.Destination.Host = string(ResolveShortnameToFQDN(w.Destination.Host, r.ConfigMeta))
1135				}
1136			}
1137		}
1138	}
1139
1140	for _, virtualService := range vservices {
1141		ns := virtualService.Namespace
1142		rule := virtualService.Spec.(*networking.VirtualService)
1143		gwNames := getGatewayNames(rule, virtualService.ConfigMeta)
1144		if len(rule.ExportTo) == 0 {
1145			// No exportTo in virtualService. Use the global default
1146			// TODO: We currently only honor ., * and ~
1147			if ps.defaultVirtualServiceExportTo[visibility.Private] {
1148				if _, f := ps.privateVirtualServicesByNamespaceAndGateway[ns]; !f {
1149					ps.privateVirtualServicesByNamespaceAndGateway[ns] = map[string][]Config{}
1150				}
1151				// add to local namespace only
1152				for _, gw := range gwNames {
1153					ps.privateVirtualServicesByNamespaceAndGateway[ns][gw] = append(ps.privateVirtualServicesByNamespaceAndGateway[ns][gw], virtualService)
1154				}
1155			} else if ps.defaultVirtualServiceExportTo[visibility.Public] {
1156				for _, gw := range gwNames {
1157					ps.publicVirtualServicesByGateway[gw] = append(ps.publicVirtualServicesByGateway[gw], virtualService)
1158				}
1159			}
1160		} else {
1161			// TODO: we currently only process the first element in the array
1162			// and currently only consider . or * which maps to public/private
1163			if visibility.Instance(rule.ExportTo[0]) == visibility.Private {
1164				if _, f := ps.privateVirtualServicesByNamespaceAndGateway[ns]; !f {
1165					ps.privateVirtualServicesByNamespaceAndGateway[ns] = map[string][]Config{}
1166				}
1167				// add to local namespace only
1168				for _, gw := range gwNames {
1169					ps.privateVirtualServicesByNamespaceAndGateway[ns][gw] = append(ps.privateVirtualServicesByNamespaceAndGateway[ns][gw], virtualService)
1170				}
1171			} else {
1172				// ~ is not valid in the exportTo fields in virtualServices, services, destination rules
1173				// and we currently only allow . or *. So treat this as public export
1174				for _, gw := range gwNames {
1175					ps.publicVirtualServicesByGateway[gw] = append(ps.publicVirtualServicesByGateway[gw], virtualService)
1176				}
1177			}
1178		}
1179	}
1180
1181	return nil
1182}
1183
1184var meshGateways = []string{constants.IstioMeshGateway}
1185
1186func getGatewayNames(vs *networking.VirtualService, meta ConfigMeta) []string {
1187	if len(vs.Gateways) == 0 {
1188		return meshGateways
1189	}
1190	res := make([]string, 0, len(vs.Gateways))
1191	for _, g := range vs.Gateways {
1192		if g == constants.IstioMeshGateway {
1193			res = append(res, constants.IstioMeshGateway)
1194		} else {
1195			name := resolveGatewayName(g, meta)
1196			res = append(res, name)
1197		}
1198	}
1199	return res
1200}
1201
1202func (ps *PushContext) initDefaultExportMaps() {
1203	ps.defaultDestinationRuleExportTo = make(map[visibility.Instance]bool)
1204	if ps.Mesh.DefaultDestinationRuleExportTo != nil {
1205		for _, e := range ps.Mesh.DefaultDestinationRuleExportTo {
1206			ps.defaultDestinationRuleExportTo[visibility.Instance(e)] = true
1207		}
1208	} else {
1209		// default to *
1210		ps.defaultDestinationRuleExportTo[visibility.Public] = true
1211	}
1212
1213	ps.defaultServiceExportTo = make(map[visibility.Instance]bool)
1214	if ps.Mesh.DefaultServiceExportTo != nil {
1215		for _, e := range ps.Mesh.DefaultServiceExportTo {
1216			ps.defaultServiceExportTo[visibility.Instance(e)] = true
1217		}
1218	} else {
1219		ps.defaultServiceExportTo[visibility.Public] = true
1220	}
1221
1222	ps.defaultVirtualServiceExportTo = make(map[visibility.Instance]bool)
1223	if ps.Mesh.DefaultVirtualServiceExportTo != nil {
1224		for _, e := range ps.Mesh.DefaultVirtualServiceExportTo {
1225			ps.defaultVirtualServiceExportTo[visibility.Instance(e)] = true
1226		}
1227	} else {
1228		ps.defaultVirtualServiceExportTo[visibility.Public] = true
1229	}
1230}
1231
1232// initSidecarScopes synthesizes Sidecar CRDs into objects called
1233// SidecarScope.  The SidecarScope object is a semi-processed view of the
1234// service registry, and config state associated with the sidecar CRD. The
1235// scope contains a set of inbound and outbound listeners, services/configs
1236// per listener, etc. The sidecar scopes are precomputed based on the
1237// Sidecar API objects in each namespace. If there is no sidecar api object
1238// for a namespace, a default sidecarscope is assigned to the namespace
1239// which enables connectivity to all services in the mesh.
1240//
1241// When proxies connect to Pilot, we identify the sidecar scope associated
1242// with the proxy and derive listeners/routes/clusters based on the sidecar
1243// scope.
1244func (ps *PushContext) initSidecarScopes(env *Environment) error {
1245	sidecarConfigs, err := env.List(collections.IstioNetworkingV1Alpha3Sidecars.Resource().GroupVersionKind(), NamespaceAll)
1246	if err != nil {
1247		return err
1248	}
1249
1250	sortConfigByCreationTime(sidecarConfigs)
1251
1252	sidecarConfigWithSelector := make([]Config, 0)
1253	sidecarConfigWithoutSelector := make([]Config, 0)
1254	sidecarsWithoutSelectorByNamespace := make(map[string]struct{})
1255	for _, sidecarConfig := range sidecarConfigs {
1256		sidecar := sidecarConfig.Spec.(*networking.Sidecar)
1257		if sidecar.WorkloadSelector != nil {
1258			sidecarConfigWithSelector = append(sidecarConfigWithSelector, sidecarConfig)
1259		} else {
1260			sidecarsWithoutSelectorByNamespace[sidecarConfig.Namespace] = struct{}{}
1261			sidecarConfigWithoutSelector = append(sidecarConfigWithoutSelector, sidecarConfig)
1262		}
1263	}
1264
1265	sidecarNum := len(sidecarConfigs)
1266	sidecarConfigs = make([]Config, 0, sidecarNum)
1267	sidecarConfigs = append(sidecarConfigs, sidecarConfigWithSelector...)
1268	sidecarConfigs = append(sidecarConfigs, sidecarConfigWithoutSelector...)
1269
1270	ps.sidecarsByNamespace = make(map[string][]*SidecarScope, sidecarNum)
1271	for _, sidecarConfig := range sidecarConfigs {
1272		sidecarConfig := sidecarConfig
1273		ps.sidecarsByNamespace[sidecarConfig.Namespace] = append(ps.sidecarsByNamespace[sidecarConfig.Namespace],
1274			ConvertToSidecarScope(ps, &sidecarConfig, sidecarConfig.Namespace))
1275	}
1276
1277	// Hold reference root namespace's sidecar config
1278	// Root namespace can have only one sidecar config object
1279	// Currently we expect that it has no workloadSelectors
1280	var rootNSConfig *Config
1281	if ps.Mesh.RootNamespace != "" {
1282		for _, sidecarConfig := range sidecarConfigs {
1283			if sidecarConfig.Namespace == ps.Mesh.RootNamespace &&
1284				sidecarConfig.Spec.(*networking.Sidecar).WorkloadSelector == nil {
1285				rootNSConfig = &sidecarConfig
1286				break
1287			}
1288		}
1289	}
1290
1291	// build sidecar scopes for namespaces that do not have a non-workloadSelector sidecar CRD object.
1292	// Derive the sidecar scope from the root namespace's sidecar object if present. Else fallback
1293	// to the default Istio behavior mimicked by the DefaultSidecarScopeForNamespace function.
1294	for _, nsMap := range ps.ServiceByHostnameAndNamespace {
1295		for ns := range nsMap {
1296			if _, exist := sidecarsWithoutSelectorByNamespace[ns]; !exist {
1297				ps.sidecarsByNamespace[ns] = append(ps.sidecarsByNamespace[ns], ConvertToSidecarScope(ps, rootNSConfig, ns))
1298			}
1299		}
1300	}
1301
1302	return nil
1303}
1304
1305// Split out of DestinationRule expensive conversions - once per push.
1306func (ps *PushContext) initDestinationRules(env *Environment) error {
1307	configs, err := env.List(collections.IstioNetworkingV1Alpha3Destinationrules.Resource().GroupVersionKind(), NamespaceAll)
1308	if err != nil {
1309		return err
1310	}
1311
1312	// values returned from ConfigStore.List are immutable.
1313	// Therefore, we make a copy
1314	destRules := make([]Config, len(configs))
1315	for i := range destRules {
1316		destRules[i] = configs[i].DeepCopy()
1317	}
1318
1319	ps.SetDestinationRules(destRules)
1320	return nil
1321}
1322
1323// SetDestinationRules is updates internal structures using a set of configs.
1324// Split out of DestinationRule expensive conversions, computed once per push.
1325// This also allows tests to inject a config without having the mock.
1326// This will not work properly for Sidecars, which will precompute their destination rules on init
1327func (ps *PushContext) SetDestinationRules(configs []Config) {
1328	// Sort by time first. So if two destination rule have top level traffic policies
1329	// we take the first one.
1330	sortConfigByCreationTime(configs)
1331	namespaceLocalDestRules := make(map[string]*processedDestRules)
1332	namespaceExportedDestRules := make(map[string]*processedDestRules)
1333
1334	for i := range configs {
1335		rule := configs[i].Spec.(*networking.DestinationRule)
1336		rule.Host = string(ResolveShortnameToFQDN(rule.Host, configs[i].ConfigMeta))
1337		// Store in an index for the config's namespace
1338		// a proxy from this namespace will first look here for the destination rule for a given service
1339		// This pool consists of both public/private destination rules.
1340		// TODO: when exportTo is fully supported, only add the rule here if exportTo is '.'
1341		// The global exportTo doesn't matter here (its either . or * - both of which are applicable here)
1342		if _, exist := namespaceLocalDestRules[configs[i].Namespace]; !exist {
1343			namespaceLocalDestRules[configs[i].Namespace] = &processedDestRules{
1344				hosts:    make([]host.Name, 0),
1345				destRule: map[host.Name]*Config{},
1346			}
1347		}
1348		// Merge this destination rule with any public/private dest rules for same host in the same namespace
1349		// If there are no duplicates, the dest rule will be added to the list
1350		ps.mergeDestinationRule(namespaceLocalDestRules[configs[i].Namespace], configs[i])
1351		isPubliclyExported := false
1352		if len(rule.ExportTo) == 0 {
1353			// No exportTo in destinationRule. Use the global default
1354			// TODO: We currently only honor ., * and ~
1355			if ps.defaultDestinationRuleExportTo[visibility.Public] {
1356				isPubliclyExported = true
1357			}
1358		} else {
1359			// TODO: we currently only process the first element in the array
1360			// and currently only consider . or * which maps to public/private
1361			if visibility.Instance(rule.ExportTo[0]) != visibility.Private {
1362				// ~ is not valid in the exportTo fields in virtualServices, services, destination rules
1363				// and we currently only allow . or *. So treat this as public export
1364				isPubliclyExported = true
1365			}
1366		}
1367
1368		if isPubliclyExported {
1369			if _, exist := namespaceExportedDestRules[configs[i].Namespace]; !exist {
1370				namespaceExportedDestRules[configs[i].Namespace] = &processedDestRules{
1371					hosts:    make([]host.Name, 0),
1372					destRule: map[host.Name]*Config{},
1373				}
1374			}
1375			// Merge this destination rule with any public dest rule for the same host in the same namespace
1376			// If there are no duplicates, the dest rule will be added to the list
1377			ps.mergeDestinationRule(namespaceExportedDestRules[configs[i].Namespace], configs[i])
1378		}
1379	}
1380
1381	// presort it so that we don't sort it for each DestinationRule call.
1382	// sort.Sort for Hostnames will automatically sort from the most specific to least specific
1383	for ns := range namespaceLocalDestRules {
1384		sort.Sort(host.Names(namespaceLocalDestRules[ns].hosts))
1385	}
1386	for ns := range namespaceExportedDestRules {
1387		sort.Sort(host.Names(namespaceExportedDestRules[ns].hosts))
1388	}
1389
1390	ps.namespaceLocalDestRules = namespaceLocalDestRules
1391	ps.namespaceExportedDestRules = namespaceExportedDestRules
1392}
1393
1394func (ps *PushContext) initAuthorizationPolicies(env *Environment) error {
1395	var err error
1396	if ps.AuthzPolicies, err = GetAuthorizationPolicies(env); err != nil {
1397		authzLog.Errorf("failed to initialize authorization policies: %v", err)
1398		return err
1399	}
1400	return nil
1401}
1402
1403// pre computes envoy filters per namespace
1404func (ps *PushContext) initEnvoyFilters(env *Environment) error {
1405	envoyFilterConfigs, err := env.List(collections.IstioNetworkingV1Alpha3Envoyfilters.Resource().GroupVersionKind(), NamespaceAll)
1406	if err != nil {
1407		return err
1408	}
1409
1410	sortConfigByCreationTime(envoyFilterConfigs)
1411
1412	ps.envoyFiltersByNamespace = make(map[string][]*EnvoyFilterWrapper)
1413	for _, envoyFilterConfig := range envoyFilterConfigs {
1414		efw := convertToEnvoyFilterWrapper(&envoyFilterConfig)
1415		if _, exists := ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace]; !exists {
1416			ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace] = make([]*EnvoyFilterWrapper, 0)
1417		}
1418		ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace] = append(ps.envoyFiltersByNamespace[envoyFilterConfig.Namespace], efw)
1419	}
1420	return nil
1421}
1422
1423// EnvoyFilters return the merged EnvoyFilterWrapper of a proxy
1424func (ps *PushContext) EnvoyFilters(proxy *Proxy) *EnvoyFilterWrapper {
1425	// this should never happen
1426	if proxy == nil {
1427		return nil
1428	}
1429	matchedEnvoyFilters := make([]*EnvoyFilterWrapper, 0)
1430	// EnvoyFilters supports inheritance (global ones plus namespace local ones).
1431	// First get all the filter configs from the config root namespace
1432	// and then add the ones from proxy's own namespace
1433	if ps.Mesh.RootNamespace != "" {
1434		// if there is no workload selector, the config applies to all workloads
1435		// if there is a workload selector, check for matching workload labels
1436		for _, efw := range ps.envoyFiltersByNamespace[ps.Mesh.RootNamespace] {
1437			var workloadLabels labels.Collection
1438			// This should never happen except in tests.
1439			if proxy.Metadata != nil && len(proxy.Metadata.Labels) > 0 {
1440				workloadLabels = labels.Collection{proxy.Metadata.Labels}
1441			}
1442			if efw.workloadSelector == nil || workloadLabels.IsSupersetOf(efw.workloadSelector) {
1443				matchedEnvoyFilters = append(matchedEnvoyFilters, efw)
1444			}
1445		}
1446	}
1447
1448	// To prevent duplicate envoyfilters in case root namespace equals proxy's namespace
1449	if proxy.ConfigNamespace != ps.Mesh.RootNamespace {
1450		for _, efw := range ps.envoyFiltersByNamespace[proxy.ConfigNamespace] {
1451			var workloadLabels labels.Collection
1452			// This should never happen except in tests.
1453			if proxy.Metadata != nil && len(proxy.Metadata.Labels) > 0 {
1454				workloadLabels = labels.Collection{proxy.Metadata.Labels}
1455			}
1456			if efw.workloadSelector == nil || workloadLabels.IsSupersetOf(efw.workloadSelector) {
1457				matchedEnvoyFilters = append(matchedEnvoyFilters, efw)
1458			}
1459		}
1460	}
1461
1462	var out *EnvoyFilterWrapper
1463	if len(matchedEnvoyFilters) > 0 {
1464		out = &EnvoyFilterWrapper{
1465			// no need populate workloadSelector, as it is not used later.
1466			Patches: make(map[networking.EnvoyFilter_ApplyTo][]*EnvoyFilterConfigPatchWrapper),
1467		}
1468	}
1469	// merge EnvoyFilterWrapper
1470	for _, efw := range matchedEnvoyFilters {
1471		for applyTo, cps := range efw.Patches {
1472			if out.Patches[applyTo] == nil {
1473				out.Patches[applyTo] = []*EnvoyFilterConfigPatchWrapper{}
1474			}
1475			for _, cp := range cps {
1476				if proxyMatch(proxy, cp) {
1477					out.Patches[applyTo] = append(out.Patches[applyTo], cp)
1478				}
1479			}
1480		}
1481	}
1482
1483	return out
1484}
1485
1486// pre computes gateways per namespace
1487func (ps *PushContext) initGateways(env *Environment) error {
1488	gatewayConfigs, err := env.List(collections.IstioNetworkingV1Alpha3Gateways.Resource().GroupVersionKind(), NamespaceAll)
1489	if err != nil {
1490		return err
1491	}
1492
1493	sortConfigByCreationTime(gatewayConfigs)
1494
1495	ps.allGateways = gatewayConfigs
1496	ps.gatewaysByNamespace = make(map[string][]Config)
1497	for _, gatewayConfig := range gatewayConfigs {
1498		if _, exists := ps.gatewaysByNamespace[gatewayConfig.Namespace]; !exists {
1499			ps.gatewaysByNamespace[gatewayConfig.Namespace] = make([]Config, 0)
1500		}
1501		ps.gatewaysByNamespace[gatewayConfig.Namespace] = append(ps.gatewaysByNamespace[gatewayConfig.Namespace], gatewayConfig)
1502	}
1503	return nil
1504}
1505
1506func (ps *PushContext) mergeGateways(proxy *Proxy) *MergedGateway {
1507	// this should never happen
1508	if proxy == nil {
1509		return nil
1510	}
1511	out := make([]Config, 0)
1512
1513	var configs []Config
1514	if features.ScopeGatewayToNamespace {
1515		configs = ps.gatewaysByNamespace[proxy.ConfigNamespace]
1516	} else {
1517		configs = ps.allGateways
1518	}
1519
1520	for _, cfg := range configs {
1521		gw := cfg.Spec.(*networking.Gateway)
1522		if gw.GetSelector() == nil {
1523			// no selector. Applies to all workloads asking for the gateway
1524			out = append(out, cfg)
1525		} else {
1526			gatewaySelector := labels.Instance(gw.GetSelector())
1527			var workloadLabels labels.Collection
1528			// This should never happen except in tests.
1529			if proxy.Metadata != nil && len(proxy.Metadata.Labels) > 0 {
1530				workloadLabels = labels.Collection{proxy.Metadata.Labels}
1531			}
1532			if workloadLabels.IsSupersetOf(gatewaySelector) {
1533				out = append(out, cfg)
1534			}
1535		}
1536	}
1537
1538	if len(out) == 0 {
1539		return nil
1540	}
1541	return MergeGateways(out...)
1542}
1543
1544// pre computes gateways for each network
1545func (ps *PushContext) initMeshNetworks() {
1546	if ps.Networks == nil || len(ps.Networks.Networks) == 0 {
1547		return
1548	}
1549
1550	ps.networkGateways = map[string][]*Gateway{}
1551	for network, networkConf := range ps.Networks.Networks {
1552		gws := networkConf.Gateways
1553		if len(gws) == 0 {
1554			// all endpoints in this network are reachable directly from others. nothing to do.
1555			continue
1556		}
1557
1558		registryNames := getNetworkRegistries(networkConf)
1559		gateways := []*Gateway{}
1560
1561		for _, gw := range gws {
1562			gateways = append(gateways, getGatewayAddresses(gw, registryNames, ps.ServiceDiscovery)...)
1563		}
1564
1565		log.Debugf("Endpoints from registries %v on network %v reachable through %d gateways",
1566			registryNames, network, len(gateways))
1567
1568		ps.networkGateways[network] = gateways
1569	}
1570}
1571
1572func (ps *PushContext) initClusterLocalHosts(e *Environment) {
1573	// Create the default list of cluster-local hosts.
1574	domainSuffix := e.GetDomainSuffix()
1575	defaultClusterLocalHosts := make([]host.Name, 0, len(defaultClusterLocalNamespaces))
1576	for _, n := range defaultClusterLocalNamespaces {
1577		defaultClusterLocalHosts = append(defaultClusterLocalHosts, host.Name("*."+n+".svc."+domainSuffix))
1578	}
1579
1580	if discoveryHost, err := e.GetDiscoveryHost(); err != nil {
1581		log.Errorf("failed to make discoveryAddress cluster-local: %v", err)
1582	} else {
1583		if !strings.HasSuffix(string(discoveryHost), domainSuffix) {
1584			discoveryHost += host.Name("." + domainSuffix)
1585		}
1586		defaultClusterLocalHosts = append(defaultClusterLocalHosts, discoveryHost)
1587	}
1588
1589	// Collect the cluster-local hosts.
1590	clusterLocalHosts := make([]host.Name, 0)
1591	for _, serviceSettings := range ps.Mesh.ServiceSettings {
1592		if serviceSettings.Settings.ClusterLocal {
1593			for _, h := range serviceSettings.Hosts {
1594				clusterLocalHosts = append(clusterLocalHosts, host.Name(h))
1595			}
1596		} else {
1597			// Remove defaults if specified to be non-cluster-local.
1598			for _, h := range serviceSettings.Hosts {
1599				for i, defaultClusterLocalHost := range defaultClusterLocalHosts {
1600					if len(defaultClusterLocalHost) > 0 && strings.HasSuffix(h, string(defaultClusterLocalHost[1:])) {
1601						// This default was explicitly overridden, so remove it.
1602						defaultClusterLocalHosts[i] = ""
1603					}
1604				}
1605			}
1606		}
1607	}
1608
1609	// Add any remaining defaults to the end of the list.
1610	for _, defaultClusterLocalHost := range defaultClusterLocalHosts {
1611		if len(defaultClusterLocalHost) > 0 {
1612			clusterLocalHosts = append(clusterLocalHosts, defaultClusterLocalHost)
1613		}
1614	}
1615
1616	sort.Sort(host.Names(clusterLocalHosts))
1617	ps.clusterLocalHosts = clusterLocalHosts
1618}
1619
1620func (ps *PushContext) initQuotaSpecs(env *Environment) error {
1621	var err error
1622	ps.QuotaSpec, err = env.List(collections.IstioMixerV1ConfigClientQuotaspecs.Resource().GroupVersionKind(), NamespaceAll)
1623	return err
1624}
1625
1626func (ps *PushContext) initQuotaSpecBindings(env *Environment) error {
1627	var err error
1628	ps.QuotaSpecBinding, err = env.List(collections.IstioMixerV1ConfigClientQuotaspecbindings.Resource().GroupVersionKind(), NamespaceAll)
1629	return err
1630}
1631
1632func getNetworkRegistries(network *meshconfig.Network) []string {
1633	var registryNames []string
1634	for _, eps := range network.Endpoints {
1635		if eps != nil && len(eps.GetFromRegistry()) > 0 {
1636			registryNames = append(registryNames, eps.GetFromRegistry())
1637		}
1638	}
1639	return registryNames
1640}
1641
1642func getGatewayAddresses(gw *meshconfig.Network_IstioNetworkGateway, registryNames []string, discovery ServiceDiscovery) []*Gateway {
1643	// First, if a gateway address is provided in the configuration use it. If the gateway address
1644	// in the config was a hostname it got already resolved and replaced with an IP address
1645	// when loading the config
1646	if gwIP := net.ParseIP(gw.GetAddress()); gwIP != nil {
1647		return []*Gateway{{gw.GetAddress(), gw.Port}}
1648	}
1649
1650	// Second, try to find the gateway addresses by the provided service name
1651	if gwSvcName := gw.GetRegistryServiceName(); gwSvcName != "" {
1652		svc, _ := discovery.GetService(host.Name(gwSvcName))
1653		if svc == nil {
1654			return nil
1655		}
1656		// No need lock here as the service returned is a new one
1657		if svc.Attributes.ClusterExternalAddresses != nil {
1658			var gateways []*Gateway
1659			for _, clusterName := range registryNames {
1660				remotePort := gw.Port
1661				// check if we have node port mappings
1662				if svc.Attributes.ClusterExternalPorts != nil {
1663					if nodePortMap, exists := svc.Attributes.ClusterExternalPorts[clusterName]; exists {
1664						// what we now have is a service port. If there is a mapping for cluster external ports,
1665						// look it up and get the node port for the remote port
1666						if nodePort, exists := nodePortMap[remotePort]; exists {
1667							remotePort = nodePort
1668						}
1669					}
1670				}
1671				ips := svc.Attributes.ClusterExternalAddresses[clusterName]
1672				for _, ip := range ips {
1673					gateways = append(gateways, &Gateway{ip, remotePort})
1674				}
1675			}
1676			return gateways
1677		}
1678	}
1679
1680	return nil
1681}
1682
1683func (ps *PushContext) NetworkGateways() map[string][]*Gateway {
1684	return ps.networkGateways
1685}
1686
1687func (ps *PushContext) NetworkGatewaysByNetwork(network string) []*Gateway {
1688	if ps.networkGateways != nil {
1689		return ps.networkGateways[network]
1690	}
1691
1692	return nil
1693}
1694
1695func (ps *PushContext) QuotaSpecByDestination(hostname host.Name) []Config {
1696	return filterQuotaSpecsByDestination(hostname, ps.QuotaSpecBinding, ps.QuotaSpec)
1697}
1698
1699// BestEffortInferServiceMTLSMode infers the mTLS mode for the service + port from all authentication
1700// policies (both alpha and beta) in the system. The function always returns MTLSUnknown for external service.
1701// The resulst is a best effort. It is because the PeerAuthentication is workload-based, this function is unable
1702// to compute the correct service mTLS mode without knowing service to workload binding. For now, this
1703// function uses only mesh and namespace level PeerAuthentication and ignore workload & port level policies.
1704// This function is used to give a hint for auto-mTLS configuration on client side.
1705func (ps *PushContext) BestEffortInferServiceMTLSMode(service *Service, port *Port) MutualTLSMode {
1706	if service.MeshExternal {
1707		// Only need the authentication MTLS mode when service is not external.
1708		return MTLSUnknown
1709	}
1710
1711	// First , check mTLS settings from beta policy (i.e PeerAuthentication) at namespace / mesh level.
1712	// If the mode is not unknown, use it.
1713	if serviceMTLSMode := ps.AuthnBetaPolicies.GetNamespaceMutualTLSMode(service.Attributes.Namespace); serviceMTLSMode != MTLSUnknown {
1714		return serviceMTLSMode
1715	}
1716
1717	// When all are failed, default to permissive.
1718	return MTLSPermissive
1719}
1720