istio

Форк
0
252 строки · 9.2 Кб
1
// Copyright 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

15
package kube
16

17
import (
18
	"fmt"
19
	"strings"
20

21
	corev1 "k8s.io/api/core/v1"
22
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23
	"sigs.k8s.io/gateway-api/apis/v1beta1"
24

25
	"istio.io/api/annotation"
26
	"istio.io/api/label"
27
	"istio.io/istio/pilot/pkg/features"
28
	"istio.io/istio/pilot/pkg/model"
29
	"istio.io/istio/pilot/pkg/serviceregistry/provider"
30
	"istio.io/istio/pkg/cluster"
31
	"istio.io/istio/pkg/config/constants"
32
	"istio.io/istio/pkg/config/host"
33
	"istio.io/istio/pkg/config/kube"
34
	"istio.io/istio/pkg/config/visibility"
35
	"istio.io/istio/pkg/spiffe"
36
	"istio.io/istio/pkg/util/sets"
37
)
38

39
func convertPort(port corev1.ServicePort) *model.Port {
40
	return &model.Port{
41
		Name:     port.Name,
42
		Port:     int(port.Port),
43
		Protocol: kube.ConvertProtocol(port.Port, port.Name, port.Protocol, port.AppProtocol),
44
	}
45
}
46

47
func ConvertService(svc corev1.Service, domainSuffix string, clusterID cluster.ID) *model.Service {
48
	addrs := []string{constants.UnspecifiedIP}
49
	resolution := model.ClientSideLB
50
	externalName := ""
51
	nodeLocal := false
52

53
	if svc.Spec.Type == corev1.ServiceTypeExternalName && svc.Spec.ExternalName != "" {
54
		externalName = svc.Spec.ExternalName
55
		if features.EnableExternalNameAlias {
56
			resolution = model.Alias
57
		} else {
58
			resolution = model.DNSLB
59
		}
60
	}
61
	if svc.Spec.InternalTrafficPolicy != nil && *svc.Spec.InternalTrafficPolicy == corev1.ServiceInternalTrafficPolicyLocal {
62
		nodeLocal = true
63
	}
64

65
	if svc.Spec.ClusterIP == corev1.ClusterIPNone { // headless services should not be load balanced
66
		resolution = model.Passthrough
67
	} else if svc.Spec.ClusterIP != "" {
68
		addrs[0] = svc.Spec.ClusterIP
69
		if len(svc.Spec.ClusterIPs) > 1 {
70
			addrs = svc.Spec.ClusterIPs
71
		}
72
	}
73

74
	ports := make([]*model.Port, 0, len(svc.Spec.Ports))
75
	for _, port := range svc.Spec.Ports {
76
		ports = append(ports, convertPort(port))
77
	}
78

79
	var exportTo sets.Set[visibility.Instance]
80
	serviceaccounts := make([]string, 0)
81
	if svc.Annotations[annotation.AlphaCanonicalServiceAccounts.Name] != "" {
82
		serviceaccounts = append(serviceaccounts, strings.Split(svc.Annotations[annotation.AlphaCanonicalServiceAccounts.Name], ",")...)
83
	}
84
	if svc.Annotations[annotation.AlphaKubernetesServiceAccounts.Name] != "" {
85
		for _, ksa := range strings.Split(svc.Annotations[annotation.AlphaKubernetesServiceAccounts.Name], ",") {
86
			serviceaccounts = append(serviceaccounts, kubeToIstioServiceAccount(ksa, svc.Namespace))
87
		}
88
	}
89
	if svc.Annotations[annotation.NetworkingExportTo.Name] != "" {
90
		namespaces := strings.Split(svc.Annotations[annotation.NetworkingExportTo.Name], ",")
91
		exportTo = sets.NewWithLength[visibility.Instance](len(namespaces))
92
		for _, ns := range namespaces {
93
			exportTo.Insert(visibility.Instance(ns))
94
		}
95
	}
96

97
	istioService := &model.Service{
98
		Hostname: ServiceHostname(svc.Name, svc.Namespace, domainSuffix),
99
		ClusterVIPs: model.AddressMap{
100
			Addresses: map[cluster.ID][]string{
101
				clusterID: addrs,
102
			},
103
		},
104
		Ports:           ports,
105
		DefaultAddress:  addrs[0],
106
		ServiceAccounts: serviceaccounts,
107
		MeshExternal:    len(externalName) > 0,
108
		Resolution:      resolution,
109
		CreationTime:    svc.CreationTimestamp.Time,
110
		ResourceVersion: svc.ResourceVersion,
111
		Attributes: model.ServiceAttributes{
112
			ServiceRegistry: provider.Kubernetes,
113
			Name:            svc.Name,
114
			Namespace:       svc.Namespace,
115
			Labels:          svc.Labels,
116
			ExportTo:        exportTo,
117
			LabelSelectors:  svc.Spec.Selector,
118
		},
119
	}
120

121
	switch svc.Spec.Type {
122
	case corev1.ServiceTypeNodePort:
123
		if _, ok := svc.Annotations[annotation.TrafficNodeSelector.Name]; !ok {
124
			// only do this for istio ingress-gateway services
125
			break
126
		}
127
		// store the service port to node port mappings
128
		portMap := make(map[uint32]uint32)
129
		for _, p := range svc.Spec.Ports {
130
			portMap[uint32(p.Port)] = uint32(p.NodePort)
131
		}
132
		istioService.Attributes.ClusterExternalPorts = map[cluster.ID]map[uint32]uint32{clusterID: portMap}
133
		// address mappings will be done elsewhere
134
	case corev1.ServiceTypeLoadBalancer:
135
		if len(svc.Status.LoadBalancer.Ingress) > 0 {
136
			var lbAddrs []string
137
			for _, ingress := range svc.Status.LoadBalancer.Ingress {
138
				if len(ingress.IP) > 0 {
139
					lbAddrs = append(lbAddrs, ingress.IP)
140
				} else if len(ingress.Hostname) > 0 {
141
					// DO NOT resolve the DNS here. In environments like AWS, the ELB hostname
142
					// does not have a repeatable DNS address and IPs resolved at an earlier point
143
					// in time may not work. So, when we get just hostnames instead of IPs, we need
144
					// to smartly switch from EDS to strict_dns rather than doing the naive thing of
145
					// resolving the DNS name and hoping the resolution is one-time task.
146
					lbAddrs = append(lbAddrs, ingress.Hostname)
147
				}
148
			}
149
			if len(lbAddrs) > 0 {
150
				if istioService.Attributes.ClusterExternalAddresses == nil {
151
					istioService.Attributes.ClusterExternalAddresses = &model.AddressMap{}
152
				}
153
				istioService.Attributes.ClusterExternalAddresses.SetAddressesFor(clusterID, lbAddrs)
154
			}
155
		}
156
	}
157

158
	istioService.Attributes.Type = string(svc.Spec.Type)
159
	istioService.Attributes.ExternalName = externalName
160
	istioService.Attributes.NodeLocal = nodeLocal
161
	if len(svc.Spec.ExternalIPs) > 0 {
162
		if istioService.Attributes.ClusterExternalAddresses == nil {
163
			istioService.Attributes.ClusterExternalAddresses = &model.AddressMap{}
164
		}
165
		istioService.Attributes.ClusterExternalAddresses.AddAddressesFor(clusterID, svc.Spec.ExternalIPs)
166
	}
167

168
	return istioService
169
}
170

171
func ExternalNameEndpoints(svc *model.Service) []*model.IstioEndpoint {
172
	if svc.Attributes.ExternalName == "" {
173
		return nil
174
	}
175
	out := make([]*model.IstioEndpoint, 0, len(svc.Ports))
176

177
	discoverabilityPolicy := model.AlwaysDiscoverable
178
	if features.EnableMCSServiceDiscovery {
179
		// MCS spec does not allow export of external name services.
180
		// See https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#exporting-services.
181
		discoverabilityPolicy = model.DiscoverableFromSameCluster
182
	}
183
	for _, portEntry := range svc.Ports {
184
		out = append(out, &model.IstioEndpoint{
185
			Address:               svc.Attributes.ExternalName,
186
			EndpointPort:          uint32(portEntry.Port),
187
			ServicePortName:       portEntry.Name,
188
			Labels:                svc.Attributes.Labels,
189
			DiscoverabilityPolicy: discoverabilityPolicy,
190
		})
191
	}
192
	return out
193
}
194

195
// ServiceHostname produces FQDN for a k8s service
196
func ServiceHostname(name, namespace, domainSuffix string) host.Name {
197
	return host.Name(name + "." + namespace + "." + "svc" + "." + domainSuffix) // Format: "%s.%s.svc.%s"
198
}
199

200
// ServiceHostnameForKR calls ServiceHostname with the name and namespace of the given kubernetes resource.
201
func ServiceHostnameForKR(obj metav1.Object, domainSuffix string) host.Name {
202
	return ServiceHostname(obj.GetName(), obj.GetNamespace(), domainSuffix)
203
}
204

205
// kubeToIstioServiceAccount converts a K8s service account to an Istio service account
206
func kubeToIstioServiceAccount(saname string, ns string) string {
207
	return spiffe.MustGenSpiffeURI(ns, saname)
208
}
209

210
// SecureNamingSAN creates the secure naming used for SAN verification from pod metadata
211
func SecureNamingSAN(pod *corev1.Pod) string {
212
	return spiffe.MustGenSpiffeURI(pod.Namespace, pod.Spec.ServiceAccountName)
213
}
214

215
// PodTLSMode returns the tls mode associated with the pod if pod has been injected with sidecar
216
func PodTLSMode(pod *corev1.Pod) string {
217
	if pod == nil {
218
		return model.DisabledTLSModeLabel
219
	}
220
	return model.GetTLSModeFromEndpointLabels(pod.Labels)
221
}
222

223
// IsAutoPassthrough determines if a listener should use auto passthrough mode. This is used for
224
// multi-network. In the Istio API, this is an explicit tls.Mode. However, this mode is not part of
225
// the gateway-api, and leaks implementation details. We already have an API to declare a Gateway as
226
// a multi-network gateway, so we will use this as a signal.
227
// A user who wishes to expose multi-network connectivity should create a listener named "tls-passthrough"
228
// with TLS.Mode Passthrough.
229
// For some backwards compatibility, we assume any listener with TLS specified and a port matching
230
// 15443 (or the label-override for gateway port) is auto-passthrough as well.
231
func IsAutoPassthrough(gwLabels map[string]string, l v1beta1.Listener) bool {
232
	if l.TLS == nil {
233
		return false
234
	}
235
	if hasListenerMode(l, constants.ListenerModeAutoPassthrough) {
236
		return true
237
	}
238
	_, networkSet := gwLabels[label.TopologyNetwork.Name]
239
	if !networkSet {
240
		return false
241
	}
242
	expectedPort := "15443"
243
	if port, f := gwLabels[label.NetworkingGatewayPort.Name]; f {
244
		expectedPort = port
245
	}
246
	return fmt.Sprint(l.Port) == expectedPort
247
}
248

249
func hasListenerMode(l v1beta1.Listener, mode string) bool {
250
	// TODO if we add a hybrid mode for detecting HBONE/passthrough, also check that here
251
	return l.TLS != nil && l.TLS.Options != nil && string(l.TLS.Options[constants.ListenerModeOption]) == mode
252
}
253

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.