istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/pilot/forwardproxy/envoy_config_generator.go (about)

     1  //go:build integ
     2  // +build integ
     3  
     4  // Copyright Istio Authors
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package forwardproxy
    19  
    20  import (
    21  	"fmt"
    22  
    23  	envoy_accesslogv3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3"
    24  	envoy_bootstrap "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
    25  	envoy_cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    26  	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    27  	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    28  	envoy_route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    29  	envoy_fileaccesslogv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3"
    30  	envoy_clusters_dynamic_forward_proxy "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3"
    31  	envoy_common_dynamic_forward_proxy "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3"
    32  	envoy_filters_dynamic_forward_proxy "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3"
    33  	envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    34  	envoy_dns_cares "github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/cares/v3"
    35  	envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    36  
    37  	"istio.io/istio/pilot/pkg/util/protoconv"
    38  	"istio.io/istio/pkg/util/protomarshal"
    39  )
    40  
    41  const (
    42  	HTTP1 = "HTTP1"
    43  	HTTP2 = "HTTP2"
    44  )
    45  
    46  type ListenerSettings struct {
    47  	Port        uint32
    48  	HTTPVersion string
    49  	TLSEnabled  bool
    50  }
    51  
    52  func (l ListenerSettings) TLSEnabledStr() string {
    53  	if l.TLSEnabled {
    54  		return "TLS"
    55  	}
    56  	return "noTLS"
    57  }
    58  
    59  func GenerateForwardProxyBootstrapConfig(listeners []ListenerSettings) (string, error) {
    60  	bootstrap := &envoy_bootstrap.Bootstrap{
    61  		Admin: &envoy_bootstrap.Admin{
    62  			Address: createSocketAddress("127.0.0.1", 9902),
    63  		},
    64  		StaticResources: &envoy_bootstrap.Bootstrap_StaticResources{
    65  			Listeners: []*envoy_listener.Listener{},
    66  			Clusters: []*envoy_cluster.Cluster{
    67  				{
    68  					Name:     "dynamic_forward_proxy_cluster",
    69  					LbPolicy: envoy_cluster.Cluster_CLUSTER_PROVIDED,
    70  					ClusterDiscoveryType: &envoy_cluster.Cluster_ClusterType{
    71  						ClusterType: &envoy_cluster.Cluster_CustomClusterType{
    72  							Name: "envoy.clusters.dynamic_forward_proxy",
    73  							TypedConfig: protoconv.MessageToAny(&envoy_clusters_dynamic_forward_proxy.ClusterConfig{
    74  								ClusterImplementationSpecifier: &envoy_clusters_dynamic_forward_proxy.ClusterConfig_DnsCacheConfig{
    75  									DnsCacheConfig: dynamicForwardProxyCacheConfig,
    76  								},
    77  							}),
    78  						},
    79  					},
    80  				},
    81  			},
    82  		},
    83  	}
    84  	for _, listenerSettings := range listeners {
    85  		listenerName := fmt.Sprintf("http_forward_proxy_%d", listenerSettings.Port)
    86  		hcm := createHTTPConnectionManager(listenerName, listenerSettings.HTTPVersion)
    87  		bootstrap.StaticResources.Listeners = append(bootstrap.StaticResources.Listeners, &envoy_listener.Listener{
    88  			Name:    listenerName,
    89  			Address: createSocketAddress("::", listenerSettings.Port),
    90  			FilterChains: []*envoy_listener.FilterChain{
    91  				{
    92  					Filters: []*envoy_listener.Filter{
    93  						{
    94  							Name: "envoy.filters.network.http_connection_manager",
    95  							ConfigType: &envoy_listener.Filter_TypedConfig{
    96  								TypedConfig: protoconv.MessageToAny(hcm),
    97  							},
    98  						},
    99  					},
   100  					TransportSocket: createTransportSocket(listenerSettings.TLSEnabled),
   101  				},
   102  			},
   103  			StatPrefix: fmt.Sprintf("http_forward_proxy_%d", listenerSettings.Port),
   104  		})
   105  	}
   106  	return protomarshal.ToYAML(bootstrap)
   107  }
   108  
   109  var dynamicForwardProxyCacheConfig = &envoy_common_dynamic_forward_proxy.DnsCacheConfig{
   110  	Name: "dynamic_forward_proxy_cache_config",
   111  	TypedDnsResolverConfig: &envoy_core.TypedExtensionConfig{
   112  		Name: "envoy.network.dns_resolver.cares",
   113  		TypedConfig: protoconv.MessageToAny(&envoy_dns_cares.CaresDnsResolverConfig{
   114  			Resolvers: []*envoy_core.Address{
   115  				createSocketAddress("8.8.8.8", 53),
   116  			},
   117  			DnsResolverOptions: &envoy_core.DnsResolverOptions{
   118  				UseTcpForDnsLookups:   true,
   119  				NoDefaultSearchDomain: true,
   120  			},
   121  			UseResolversAsFallback: true,
   122  		}),
   123  	},
   124  }
   125  
   126  func createAccessLog(listenerName string) []*envoy_accesslogv3.AccessLog {
   127  	return []*envoy_accesslogv3.AccessLog{
   128  		{
   129  			Name: "envoy.access_loggers.file",
   130  			ConfigType: &envoy_accesslogv3.AccessLog_TypedConfig{
   131  				TypedConfig: protoconv.MessageToAny(&envoy_fileaccesslogv3.FileAccessLog{
   132  					Path: "/dev/stdout",
   133  					AccessLogFormat: &envoy_fileaccesslogv3.FileAccessLog_LogFormat{
   134  						LogFormat: &envoy_core.SubstitutionFormatString{
   135  							Format: &envoy_core.SubstitutionFormatString_TextFormatSource{
   136  								TextFormatSource: &envoy_core.DataSource{
   137  									Specifier: &envoy_core.DataSource_InlineString{
   138  										InlineString: createAccessLogFormat(listenerName),
   139  									},
   140  								},
   141  							},
   142  						},
   143  					},
   144  				}),
   145  			},
   146  		},
   147  	}
   148  }
   149  
   150  func createHTTPConnectionManager(listenerName, httpVersion string) *envoy_hcm.HttpConnectionManager {
   151  	hcm := &envoy_hcm.HttpConnectionManager{
   152  		AccessLog: createAccessLog(listenerName),
   153  		HttpFilters: []*envoy_hcm.HttpFilter{
   154  			{
   155  				Name: "envoy.filters.http.dynamic_forward_proxy",
   156  				ConfigType: &envoy_hcm.HttpFilter_TypedConfig{
   157  					TypedConfig: protoconv.MessageToAny(&envoy_filters_dynamic_forward_proxy.FilterConfig{
   158  						ImplementationSpecifier: &envoy_filters_dynamic_forward_proxy.FilterConfig_DnsCacheConfig{
   159  							DnsCacheConfig: dynamicForwardProxyCacheConfig,
   160  						},
   161  					}),
   162  				},
   163  			},
   164  			{
   165  				Name: "envoy.filters.http.router",
   166  			},
   167  		},
   168  		RouteSpecifier: &envoy_hcm.HttpConnectionManager_RouteConfig{
   169  			RouteConfig: &envoy_route.RouteConfiguration{
   170  				Name: "default",
   171  				VirtualHosts: []*envoy_route.VirtualHost{
   172  					{
   173  						Name:    "http_forward_proxy",
   174  						Domains: []string{"*"},
   175  						Routes: []*envoy_route.Route{
   176  							{
   177  								Action: &envoy_route.Route_Route{
   178  									Route: &envoy_route.RouteAction{
   179  										ClusterSpecifier: &envoy_route.RouteAction_Cluster{
   180  											Cluster: "dynamic_forward_proxy_cluster",
   181  										},
   182  										UpgradeConfigs: []*envoy_route.RouteAction_UpgradeConfig{
   183  											{
   184  												UpgradeType:   "CONNECT",
   185  												ConnectConfig: &envoy_route.RouteAction_UpgradeConfig_ConnectConfig{},
   186  											},
   187  										},
   188  									},
   189  								},
   190  								Match: &envoy_route.RouteMatch{
   191  									PathSpecifier: &envoy_route.RouteMatch_ConnectMatcher_{},
   192  								},
   193  							},
   194  						},
   195  					},
   196  				},
   197  			},
   198  		},
   199  		StatPrefix: "http_forward_proxy",
   200  	}
   201  	if httpVersion == HTTP1 {
   202  		hcm.CodecType = envoy_hcm.HttpConnectionManager_HTTP1
   203  		hcm.HttpProtocolOptions = &envoy_core.Http1ProtocolOptions{}
   204  	}
   205  	if httpVersion == HTTP2 {
   206  		hcm.CodecType = envoy_hcm.HttpConnectionManager_HTTP2
   207  		hcm.Http2ProtocolOptions = &envoy_core.Http2ProtocolOptions{
   208  			AllowConnect: true,
   209  		}
   210  	}
   211  	return hcm
   212  }
   213  
   214  func createTransportSocket(tlsEnabled bool) *envoy_core.TransportSocket {
   215  	if !tlsEnabled {
   216  		return nil
   217  	}
   218  	return &envoy_core.TransportSocket{
   219  		Name: "envoy.transport_sockets.tls",
   220  		ConfigType: &envoy_core.TransportSocket_TypedConfig{
   221  			TypedConfig: protoconv.MessageToAny(&envoy_tls.DownstreamTlsContext{
   222  				CommonTlsContext: &envoy_tls.CommonTlsContext{
   223  					TlsCertificates: []*envoy_tls.TlsCertificate{
   224  						{
   225  							CertificateChain: &envoy_core.DataSource{
   226  								Specifier: &envoy_core.DataSource_Filename{
   227  									Filename: "/etc/envoy/external-forward-proxy-cert.pem",
   228  								},
   229  							},
   230  							PrivateKey: &envoy_core.DataSource{
   231  								Specifier: &envoy_core.DataSource_Filename{
   232  									Filename: "/etc/envoy/external-forward-proxy-key.pem",
   233  								},
   234  							},
   235  						},
   236  					},
   237  				},
   238  			}),
   239  		},
   240  	}
   241  }
   242  
   243  func createSocketAddress(addr string, port uint32) *envoy_core.Address {
   244  	return &envoy_core.Address{
   245  		Address: &envoy_core.Address_SocketAddress{
   246  			SocketAddress: &envoy_core.SocketAddress{
   247  				Address: addr,
   248  				PortSpecifier: &envoy_core.SocketAddress_PortValue{
   249  					PortValue: port,
   250  				},
   251  				Ipv4Compat: true,
   252  			},
   253  		},
   254  	}
   255  }
   256  
   257  func createAccessLogFormat(listenerName string) string {
   258  	return "[%START_TIME%] " + listenerName + " \"%PROTOCOL% %REQ(:METHOD)% %REQ(:AUTHORITY)%\" " +
   259  		"%RESPONSE_CODE% %RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS% " +
   260  		"%CONNECTION_TERMINATION_DETAILS% \"%UPSTREAM_TRANSPORT_FAILURE_REASON%\" " +
   261  		"\"%UPSTREAM_HOST%\" %UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS%\n"
   262  }