istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/cluster_waypoint.go (about) 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 core 16 17 import ( 18 "time" 19 20 cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 21 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 22 endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 23 tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 24 http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" 25 matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 26 metadata "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3" 27 "google.golang.org/protobuf/types/known/anypb" 28 "google.golang.org/protobuf/types/known/durationpb" 29 "google.golang.org/protobuf/types/known/structpb" 30 wrappers "google.golang.org/protobuf/types/known/wrapperspb" 31 32 networking "istio.io/api/networking/v1alpha3" 33 "istio.io/istio/pilot/pkg/model" 34 "istio.io/istio/pilot/pkg/networking/util" 35 sec_model "istio.io/istio/pilot/pkg/security/model" 36 "istio.io/istio/pilot/pkg/util/protoconv" 37 "istio.io/istio/pilot/pkg/xds/endpoints" 38 v3 "istio.io/istio/pilot/pkg/xds/v3" 39 "istio.io/istio/pkg/config/host" 40 "istio.io/istio/pkg/config/protocol" 41 "istio.io/istio/pkg/log" 42 "istio.io/istio/pkg/spiffe" 43 ) 44 45 // buildInternalUpstreamCluster builds a single endpoint cluster to the internal listener. 46 func buildInternalUpstreamCluster(name string, internalListener string) *cluster.Cluster { 47 return &cluster.Cluster{ 48 Name: name, 49 ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC}, 50 LoadAssignment: &endpoint.ClusterLoadAssignment{ 51 ClusterName: name, 52 Endpoints: util.BuildInternalEndpoint(internalListener, nil), 53 }, 54 TransportSocket: util.DefaultInternalUpstreamTransportSocket, 55 TypedExtensionProtocolOptions: map[string]*anypb.Any{ 56 v3.HttpProtocolOptionsType: passthroughHttpProtocolOptions, 57 }, 58 } 59 } 60 61 var ( 62 MainInternalCluster = buildInternalUpstreamCluster(MainInternalName, MainInternalName) 63 EncapCluster = buildInternalUpstreamCluster(EncapClusterName, ConnectOriginate) 64 ) 65 66 func (configgen *ConfigGeneratorImpl) buildInboundHBONEClusters() *cluster.Cluster { 67 return MainInternalCluster 68 } 69 70 func (configgen *ConfigGeneratorImpl) buildWaypointInboundClusters( 71 cb *ClusterBuilder, 72 proxy *model.Proxy, 73 push *model.PushContext, 74 svcs map[host.Name]*model.Service, 75 ) []*cluster.Cluster { 76 clusters := make([]*cluster.Cluster, 0) 77 // Creates "main_internal" cluster to route to the main internal listener. 78 // Creates "encap" cluster to route to the encap listener. 79 clusters = append(clusters, MainInternalCluster, EncapCluster) 80 // Creates per-VIP load balancing upstreams. 81 clusters = append(clusters, cb.buildWaypointInboundVIP(proxy, svcs)...) 82 // Upstream of the "encap" listener. 83 clusters = append(clusters, cb.buildWaypointConnectOriginate(proxy, push)) 84 85 for _, c := range clusters { 86 if c.TransportSocket != nil && c.TransportSocketMatches != nil { 87 log.Errorf("invalid cluster, multiple matches: %v", c.Name) 88 } 89 } 90 return clusters 91 } 92 93 // `inbound-vip||hostname|port`. EDS routing to the internal listener for each pod in the VIP. 94 func (cb *ClusterBuilder) buildWaypointInboundVIPCluster(proxy *model.Proxy, svc *model.Service, port model.Port, subset string) *clusterWrapper { 95 clusterName := model.BuildSubsetKey(model.TrafficDirectionInboundVIP, subset, svc.Hostname, port.Port) 96 97 discoveryType := convertResolution(cb.proxyType, svc) 98 var lbEndpoints []*endpoint.LocalityLbEndpoints 99 if discoveryType == cluster.Cluster_STRICT_DNS || discoveryType == cluster.Cluster_LOGICAL_DNS { 100 lbEndpoints = endpoints.NewCDSEndpointBuilder( 101 proxy, 102 cb.req.Push, 103 clusterName, 104 model.TrafficDirectionInboundVIP, subset, svc.Hostname, port.Port, 105 svc, nil, 106 ).FromServiceEndpoints() 107 } 108 localCluster := cb.buildCluster(clusterName, discoveryType, lbEndpoints, 109 model.TrafficDirectionInboundVIP, &port, svc, nil, subset) 110 111 // Ensure VIP cluster has services metadata for stats filter usage 112 im := getOrCreateIstioMetadata(localCluster.cluster) 113 im.Fields["services"] = &structpb.Value{ 114 Kind: &structpb.Value_ListValue{ 115 ListValue: &structpb.ListValue{ 116 Values: []*structpb.Value{}, 117 }, 118 }, 119 } 120 svcMetaList := im.Fields["services"].GetListValue() 121 svcMetaList.Values = append(svcMetaList.Values, buildServiceMetadata(svc)) 122 123 // no TLS, we are just going to internal address 124 localCluster.cluster.TransportSocketMatches = nil 125 localCluster.cluster.TransportSocket = util.TunnelHostInternalUpstreamTransportSocket 126 maybeApplyEdsConfig(localCluster.cluster) 127 return localCluster 128 } 129 130 // `inbound-vip|protocol|hostname|port`. EDS routing to the internal listener for each pod in the VIP. 131 func (cb *ClusterBuilder) buildWaypointInboundVIP(proxy *model.Proxy, svcs map[host.Name]*model.Service) []*cluster.Cluster { 132 clusters := []*cluster.Cluster{} 133 134 for _, svc := range svcs { 135 for _, port := range svc.Ports { 136 if port.Protocol == protocol.UDP { 137 continue 138 } 139 if port.Protocol.IsUnsupported() || port.Protocol.IsTCP() { 140 clusters = append(clusters, cb.buildWaypointInboundVIPCluster(proxy, svc, *port, "tcp").build()) 141 } 142 if port.Protocol.IsUnsupported() || port.Protocol.IsHTTP() { 143 clusters = append(clusters, cb.buildWaypointInboundVIPCluster(proxy, svc, *port, "http").build()) 144 } 145 cfg := cb.sidecarScope.DestinationRule(model.TrafficDirectionInbound, proxy, svc.Hostname).GetRule() 146 if cfg != nil { 147 destinationRule := cfg.Spec.(*networking.DestinationRule) 148 for _, ss := range destinationRule.Subsets { 149 if port.Protocol.IsUnsupported() || port.Protocol.IsTCP() { 150 clusters = append(clusters, cb.buildWaypointInboundVIPCluster(proxy, svc, *port, "tcp/"+ss.Name).build()) 151 } 152 if port.Protocol.IsUnsupported() || port.Protocol.IsHTTP() { 153 clusters = append(clusters, cb.buildWaypointInboundVIPCluster(proxy, svc, *port, "http/"+ss.Name).build()) 154 } 155 } 156 } 157 } 158 } 159 return clusters 160 } 161 162 func (cb *ClusterBuilder) buildWaypointConnectOriginate(proxy *model.Proxy, push *model.PushContext) *cluster.Cluster { 163 m := &matcher.StringMatcher{} 164 m.MatchPattern = &matcher.StringMatcher_Prefix{ 165 Prefix: spiffe.URIPrefix + spiffe.GetTrustDomain() + "/ns/" + proxy.Metadata.Namespace + "/sa/", 166 } 167 return cb.buildConnectOriginate(proxy, push, m) 168 } 169 170 func (cb *ClusterBuilder) buildConnectOriginate(proxy *model.Proxy, push *model.PushContext, uriSanMatchers ...*matcher.StringMatcher) *cluster.Cluster { 171 ctx := buildCommonConnectTLSContext(proxy, push) 172 validationCtx := ctx.GetCombinedValidationContext().DefaultValidationContext 173 for _, uriSanMatcher := range uriSanMatchers { 174 if uriSanMatcher != nil { 175 validationCtx.MatchTypedSubjectAltNames = append(validationCtx.MatchTypedSubjectAltNames, &tls.SubjectAltNameMatcher{ 176 SanType: tls.SubjectAltNameMatcher_URI, 177 Matcher: uriSanMatcher, 178 }) 179 } 180 } 181 // Compliance for Envoy tunnel upstreams. 182 sec_model.EnforceCompliance(ctx) 183 return &cluster.Cluster{ 184 Name: ConnectOriginate, 185 ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST}, 186 LbPolicy: cluster.Cluster_CLUSTER_PROVIDED, 187 ConnectTimeout: durationpb.New(2 * time.Second), 188 CleanupInterval: durationpb.New(60 * time.Second), 189 TypedExtensionProtocolOptions: h2connectUpgrade(), 190 LbConfig: &cluster.Cluster_OriginalDstLbConfig_{ 191 OriginalDstLbConfig: &cluster.Cluster_OriginalDstLbConfig{ 192 UpstreamPortOverride: &wrappers.UInt32Value{ 193 Value: model.HBoneInboundListenPort, 194 }, 195 // Used to override destination pods with waypoints. 196 MetadataKey: &metadata.MetadataKey{ 197 Key: util.OriginalDstMetadataKey, 198 Path: []*metadata.MetadataKey_PathSegment{{ 199 Segment: &metadata.MetadataKey_PathSegment_Key{ 200 Key: "waypoint", 201 }, 202 }}, 203 }, 204 }, 205 }, 206 TransportSocket: &core.TransportSocket{ 207 Name: "tls", 208 ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: protoconv.MessageToAny(&tls.UpstreamTlsContext{ 209 CommonTlsContext: ctx, 210 })}, 211 }, 212 } 213 } 214 215 func h2connectUpgrade() map[string]*anypb.Any { 216 return map[string]*anypb.Any{ 217 v3.HttpProtocolOptionsType: protoconv.MessageToAny(&http.HttpProtocolOptions{ 218 UpstreamProtocolOptions: &http.HttpProtocolOptions_ExplicitHttpConfig_{ExplicitHttpConfig: &http.HttpProtocolOptions_ExplicitHttpConfig{ 219 ProtocolConfig: &http.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{ 220 Http2ProtocolOptions: &core.Http2ProtocolOptions{ 221 AllowConnect: true, 222 }, 223 }, 224 }}, 225 }), 226 } 227 }