istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/xds/filters/filters.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 filters 16 17 import ( 18 cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 19 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 20 listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 21 route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 22 sfsvalue "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/set_filter_state/v3" 23 cors "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" 24 fault "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3" 25 grpcstats "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3" 26 grpcweb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3" 27 router "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" 28 sfs "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/set_filter_state/v3" 29 statefulsession "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/stateful_session/v3" 30 httpinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3" 31 originaldst "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3" 32 originalsrc "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_src/v3" 33 proxy_proto "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3" 34 tlsinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" 35 hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 36 sfsnetwork "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/set_filter_state/v3" 37 previoushost "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3" 38 resourcedetectors "github.com/envoyproxy/go-control-plane/envoy/extensions/tracers/opentelemetry/resource_detectors/v3" 39 rawbuffer "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3" 40 "google.golang.org/protobuf/types/known/wrapperspb" 41 42 alpn "istio.io/api/envoy/config/filter/http/alpn/v2alpha1" 43 "istio.io/istio/pilot/pkg/networking/util" 44 "istio.io/istio/pilot/pkg/util/protoconv" 45 "istio.io/istio/pkg/wellknown" 46 ) 47 48 const ( 49 TLSTransportProtocol = "tls" 50 RawBufferTransportProtocol = "raw_buffer" 51 52 // Alpn HTTP filter name which will override the ALPN for upstream TLS connection. 53 AlpnFilterName = "istio.alpn" 54 55 MxFilterName = "istio.metadata_exchange" 56 57 // AuthnFilterName is the name for the Istio AuthN filter. This should be the same 58 // as the name defined in 59 // https://github.com/istio/proxy/blob/master/src/envoy/http/authn/http_filter_factory.cc#L30 60 AuthnFilterName = "istio_authn" 61 62 // EnvoyJwtFilterName is the name of the Envoy JWT filter. 63 EnvoyJwtFilterName = "envoy.filters.http.jwt_authn" 64 65 // EnvoyJwtFilterPayload is the struct field for the payload in dynamic metadata in Envoy JWT filter. 66 EnvoyJwtFilterPayload = "payload" 67 ) 68 69 // Define static filters to be reused across the codebase. This avoids duplicate marshaling/unmarshaling 70 // This should not be used for filters that will be mutated 71 var ( 72 RetryPreviousHosts = &route.RetryPolicy_RetryHostPredicate{ 73 Name: "envoy.retry_host_predicates.previous_hosts", 74 ConfigType: &route.RetryPolicy_RetryHostPredicate_TypedConfig{ 75 TypedConfig: protoconv.MessageToAny(&previoushost.PreviousHostsPredicate{}), 76 }, 77 } 78 RawBufferTransportSocket = &core.TransportSocket{ 79 Name: wellknown.TransportSocketRawBuffer, 80 ConfigType: &core.TransportSocket_TypedConfig{ 81 TypedConfig: protoconv.MessageToAny(&rawbuffer.RawBuffer{}), 82 }, 83 } 84 Cors = &hcm.HttpFilter{ 85 Name: wellknown.CORS, 86 ConfigType: &hcm.HttpFilter_TypedConfig{ 87 TypedConfig: protoconv.MessageToAny(&cors.Cors{}), 88 }, 89 } 90 Fault = &hcm.HttpFilter{ 91 Name: wellknown.Fault, 92 ConfigType: &hcm.HttpFilter_TypedConfig{ 93 TypedConfig: protoconv.MessageToAny(&fault.HTTPFault{}), 94 }, 95 } 96 GrpcWeb = &hcm.HttpFilter{ 97 Name: wellknown.GRPCWeb, 98 ConfigType: &hcm.HttpFilter_TypedConfig{ 99 TypedConfig: protoconv.MessageToAny(&grpcweb.GrpcWeb{}), 100 }, 101 } 102 GrpcStats = &hcm.HttpFilter{ 103 Name: wellknown.HTTPGRPCStats, 104 ConfigType: &hcm.HttpFilter_TypedConfig{ 105 TypedConfig: protoconv.MessageToAny(&grpcstats.FilterConfig{ 106 EmitFilterState: true, 107 PerMethodStatSpecifier: &grpcstats.FilterConfig_StatsForAllMethods{ 108 StatsForAllMethods: &wrapperspb.BoolValue{Value: false}, 109 }, 110 }), 111 }, 112 } 113 TLSInspector = &listener.ListenerFilter{ 114 Name: wellknown.TLSInspector, 115 ConfigType: &listener.ListenerFilter_TypedConfig{ 116 TypedConfig: protoconv.MessageToAny(&tlsinspector.TlsInspector{}), 117 }, 118 } 119 HTTPInspector = &listener.ListenerFilter{ 120 Name: wellknown.HTTPInspector, 121 ConfigType: &listener.ListenerFilter_TypedConfig{ 122 TypedConfig: protoconv.MessageToAny(&httpinspector.HttpInspector{}), 123 }, 124 } 125 OriginalDestination = &listener.ListenerFilter{ 126 Name: wellknown.OriginalDestination, 127 ConfigType: &listener.ListenerFilter_TypedConfig{ 128 TypedConfig: protoconv.MessageToAny(&originaldst.OriginalDst{}), 129 }, 130 } 131 OriginalSrc = &listener.ListenerFilter{ 132 Name: wellknown.OriginalSource, 133 ConfigType: &listener.ListenerFilter_TypedConfig{ 134 TypedConfig: protoconv.MessageToAny(&originalsrc.OriginalSrc{ 135 Mark: 1337, 136 }), 137 }, 138 } 139 ProxyProtocol = &listener.ListenerFilter{ 140 Name: wellknown.ProxyProtocol, 141 ConfigType: &listener.ListenerFilter_TypedConfig{ 142 TypedConfig: protoconv.MessageToAny(&proxy_proto.ProxyProtocol{}), 143 }, 144 } 145 EmptySessionFilter = &hcm.HttpFilter{ 146 Name: util.StatefulSessionFilter, 147 ConfigType: &hcm.HttpFilter_TypedConfig{ 148 TypedConfig: protoconv.MessageToAny(&statefulsession.StatefulSession{}), 149 }, 150 } 151 Alpn = &hcm.HttpFilter{ 152 Name: AlpnFilterName, 153 ConfigType: &hcm.HttpFilter_TypedConfig{ 154 TypedConfig: protoconv.MessageToAny(&alpn.FilterConfig{ 155 AlpnOverride: []*alpn.FilterConfig_AlpnOverride{ 156 { 157 UpstreamProtocol: alpn.FilterConfig_HTTP10, 158 AlpnOverride: mtlsHTTP10ALPN, 159 }, 160 { 161 UpstreamProtocol: alpn.FilterConfig_HTTP11, 162 AlpnOverride: mtlsHTTP11ALPN, 163 }, 164 { 165 UpstreamProtocol: alpn.FilterConfig_HTTP2, 166 AlpnOverride: mtlsHTTP2ALPN, 167 }, 168 }, 169 }), 170 }, 171 } 172 173 // TCP MX is an Istio filter defined in https://github.com/istio/proxy/tree/master/source/extensions/filters/network/metadata_exchange. 174 tcpMx = protoconv.TypedStructWithFields("type.googleapis.com/envoy.tcp.metadataexchange.config.MetadataExchange", 175 map[string]any{ 176 "protocol": "istio-peer-exchange", 177 "enable_discovery": true, 178 }) 179 180 TCPListenerMx = &listener.Filter{ 181 Name: MxFilterName, 182 ConfigType: &listener.Filter_TypedConfig{TypedConfig: tcpMx}, 183 } 184 185 TCPClusterMx = &cluster.Filter{ 186 Name: MxFilterName, 187 TypedConfig: tcpMx, 188 } 189 190 WaypointDownstreamMetadataFilter = &hcm.HttpFilter{ 191 Name: "waypoint_downstream_peer_metadata", 192 ConfigType: &hcm.HttpFilter_TypedConfig{ 193 TypedConfig: protoconv.TypedStructWithFields("type.googleapis.com/io.istio.http.peer_metadata.Config", 194 map[string]any{ 195 "downstream_discovery": []any{ 196 map[string]any{ 197 "workload_discovery": map[string]any{}, 198 }, 199 }, 200 "shared_with_upstream": true, 201 }), 202 }, 203 } 204 205 WaypointUpstreamMetadataFilter = &hcm.HttpFilter{ 206 Name: "waypoint_upstream_peer_metadata", 207 ConfigType: &hcm.HttpFilter_TypedConfig{ 208 TypedConfig: protoconv.TypedStructWithFields("type.googleapis.com/io.istio.http.peer_metadata.Config", 209 map[string]any{ 210 "upstream_discovery": []any{ 211 map[string]any{ 212 "workload_discovery": map[string]any{}, 213 }, 214 }, 215 }), 216 }, 217 } 218 219 SidecarInboundMetadataFilter = &hcm.HttpFilter{ 220 Name: MxFilterName, 221 ConfigType: &hcm.HttpFilter_TypedConfig{ 222 TypedConfig: protoconv.TypedStructWithFields("type.googleapis.com/io.istio.http.peer_metadata.Config", 223 map[string]any{ 224 "downstream_discovery": []any{ 225 map[string]any{ 226 "istio_headers": map[string]any{}, 227 }, 228 map[string]any{ 229 "workload_discovery": map[string]any{}, 230 }, 231 }, 232 "downstream_propagation": []any{ 233 map[string]any{ 234 "istio_headers": map[string]any{}, 235 }, 236 }, 237 }), 238 }, 239 } 240 241 SidecarOutboundMetadataFilter = &hcm.HttpFilter{ 242 Name: MxFilterName, 243 ConfigType: &hcm.HttpFilter_TypedConfig{ 244 TypedConfig: protoconv.TypedStructWithFields("type.googleapis.com/io.istio.http.peer_metadata.Config", 245 map[string]any{ 246 "upstream_discovery": []any{ 247 map[string]any{ 248 "istio_headers": map[string]any{}, 249 }, 250 map[string]any{ 251 "workload_discovery": map[string]any{}, 252 }, 253 }, 254 "upstream_propagation": []any{ 255 map[string]any{ 256 "istio_headers": map[string]any{}, 257 }, 258 }, 259 }), 260 }, 261 } 262 // TODO https://github.com/istio/istio/issues/46740 263 // false values can be omitted in protobuf, results in diff JSON values between control plane and envoy config dumps 264 // long term fix will be to add the metadata config to istio/api and use that over TypedStruct 265 SidecarOutboundMetadataFilterSkipHeaders = &hcm.HttpFilter{ 266 Name: MxFilterName, 267 ConfigType: &hcm.HttpFilter_TypedConfig{ 268 TypedConfig: protoconv.TypedStructWithFields("type.googleapis.com/io.istio.http.peer_metadata.Config", 269 map[string]any{ 270 "upstream_discovery": []any{ 271 map[string]any{ 272 "istio_headers": map[string]any{}, 273 }, 274 map[string]any{ 275 "workload_discovery": map[string]any{}, 276 }, 277 }, 278 "upstream_propagation": []any{ 279 map[string]any{ 280 "istio_headers": map[string]any{ 281 "skip_external_clusters": true, 282 }, 283 }, 284 }, 285 }), 286 }, 287 } 288 289 ConnectAuthorityFilter = &hcm.HttpFilter{ 290 Name: "connect_authority", 291 ConfigType: &hcm.HttpFilter_TypedConfig{ 292 TypedConfig: protoconv.MessageToAny(&sfs.Config{ 293 OnRequestHeaders: []*sfsvalue.FilterStateValue{ 294 { 295 Key: &sfsvalue.FilterStateValue_ObjectKey{ 296 ObjectKey: "envoy.filters.listener.original_dst.local_ip", 297 }, 298 Value: &sfsvalue.FilterStateValue_FormatString{ 299 FormatString: &core.SubstitutionFormatString{ 300 Format: &core.SubstitutionFormatString_TextFormatSource{ 301 TextFormatSource: &core.DataSource{ 302 Specifier: &core.DataSource_InlineString{ 303 InlineString: "%REQ(:AUTHORITY)%", 304 }, 305 }, 306 }, 307 }, 308 }, 309 SharedWithUpstream: sfsvalue.FilterStateValue_ONCE, 310 }, { 311 Key: &sfsvalue.FilterStateValue_ObjectKey{ 312 ObjectKey: "envoy.filters.listener.original_dst.remote_ip", 313 }, 314 Value: &sfsvalue.FilterStateValue_FormatString{ 315 FormatString: &core.SubstitutionFormatString{ 316 Format: &core.SubstitutionFormatString_TextFormatSource{ 317 TextFormatSource: &core.DataSource{ 318 Specifier: &core.DataSource_InlineString{ 319 InlineString: "%DOWNSTREAM_REMOTE_ADDRESS%", 320 }, 321 }, 322 }, 323 }, 324 }, 325 SharedWithUpstream: sfsvalue.FilterStateValue_ONCE, 326 }, { 327 Key: &sfsvalue.FilterStateValue_ObjectKey{ 328 ObjectKey: "io.istio.peer_principal", 329 }, 330 FactoryKey: "envoy.string", 331 Value: &sfsvalue.FilterStateValue_FormatString{ 332 FormatString: &core.SubstitutionFormatString{ 333 Format: &core.SubstitutionFormatString_TextFormatSource{ 334 TextFormatSource: &core.DataSource{ 335 Specifier: &core.DataSource_InlineString{ 336 InlineString: "%DOWNSTREAM_PEER_URI_SAN%", 337 }, 338 }, 339 }, 340 }, 341 }, 342 SharedWithUpstream: sfsvalue.FilterStateValue_ONCE, 343 }, { 344 Key: &sfsvalue.FilterStateValue_ObjectKey{ 345 ObjectKey: "io.istio.local_principal", 346 }, 347 FactoryKey: "envoy.string", 348 Value: &sfsvalue.FilterStateValue_FormatString{ 349 FormatString: &core.SubstitutionFormatString{ 350 Format: &core.SubstitutionFormatString_TextFormatSource{ 351 TextFormatSource: &core.DataSource{ 352 Specifier: &core.DataSource_InlineString{ 353 InlineString: "%DOWNSTREAM_LOCAL_URI_SAN%", 354 }, 355 }, 356 }, 357 }, 358 }, 359 SharedWithUpstream: sfsvalue.FilterStateValue_ONCE, 360 }, 361 }, 362 }), 363 }, 364 } 365 366 ConnectAuthorityNetworkFilter = &listener.Filter{ 367 Name: "connect_authority", 368 ConfigType: &listener.Filter_TypedConfig{ 369 TypedConfig: protoconv.MessageToAny(&sfsnetwork.Config{ 370 OnNewConnection: []*sfsvalue.FilterStateValue{{ 371 Key: &sfsvalue.FilterStateValue_ObjectKey{ 372 ObjectKey: "envoy.filters.listener.original_dst.local_ip", 373 }, 374 Value: &sfsvalue.FilterStateValue_FormatString{ 375 FormatString: &core.SubstitutionFormatString{ 376 Format: &core.SubstitutionFormatString_TextFormatSource{ 377 TextFormatSource: &core.DataSource{ 378 Specifier: &core.DataSource_InlineString{ 379 InlineString: "%FILTER_STATE(envoy.filters.listener.original_dst.local_ip:PLAIN)%", 380 }, 381 }, 382 }, 383 }, 384 }, 385 SharedWithUpstream: sfsvalue.FilterStateValue_ONCE, 386 }}, 387 }), 388 }, 389 } 390 ) 391 392 // Router is used a bunch, so its worth precomputing even though we have a few options. 393 // Since there are only 4 possible options, just precompute them all 394 var routers = func() map[RouterFilterContext]*hcm.HttpFilter { 395 res := map[RouterFilterContext]*hcm.HttpFilter{} 396 for _, startSpan := range []bool{true, false} { 397 for _, suppressHeaders := range []bool{true, false} { 398 res[RouterFilterContext{ 399 StartChildSpan: startSpan, 400 SuppressDebugHeaders: suppressHeaders, 401 }] = &hcm.HttpFilter{ 402 Name: wellknown.Router, 403 ConfigType: &hcm.HttpFilter_TypedConfig{ 404 TypedConfig: protoconv.MessageToAny(&router.Router{ 405 StartChildSpan: startSpan, 406 SuppressEnvoyHeaders: suppressHeaders, 407 }), 408 }, 409 } 410 } 411 } 412 return res 413 }() 414 415 func BuildRouterFilter(ctx RouterFilterContext) *hcm.HttpFilter { 416 return routers[ctx] 417 } 418 419 var ( 420 // These ALPNs are injected in the client side by the ALPN filter. 421 // "istio" is added for each upstream protocol in order to make it 422 // backward compatible. e.g., 1.4 proxy -> 1.3 proxy. 423 // Non istio-* variants are added to ensure that traffic sent out of the mesh has a valid ALPN; 424 // ideally this would not be added, but because the override filter is in the HCM, rather than cluster, 425 // we do not yet know the upstream so we cannot determine if its in or out of the mesh 426 mtlsHTTP10ALPN = []string{"istio-http/1.0", "istio", "http/1.0"} 427 mtlsHTTP11ALPN = []string{"istio-http/1.1", "istio", "http/1.1"} 428 mtlsHTTP2ALPN = []string{"istio-h2", "istio", "h2"} 429 ) 430 431 // OpenTelemetry Resource Detectors 432 var ( 433 EnvironmentResourceDetector = &core.TypedExtensionConfig{ 434 Name: "envoy.tracers.opentelemetry.resource_detectors.environment", 435 TypedConfig: protoconv.MessageToAny(&resourcedetectors.EnvironmentResourceDetectorConfig{}), 436 } 437 DynatraceResourceDetector = &core.TypedExtensionConfig{ 438 Name: "envoy.tracers.opentelemetry.resource_detectors.dynatrace", 439 TypedConfig: protoconv.MessageToAny(&resourcedetectors.DynatraceResourceDetectorConfig{}), 440 } 441 )