github.com/cilium/cilium@v1.16.2/operator/pkg/ciliumenvoyconfig/envoy_config.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ciliumenvoyconfig
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	envoy_config_cluster_v3 "github.com/cilium/proxy/go/envoy/config/cluster/v3"
    11  	envoy_config_core_v3 "github.com/cilium/proxy/go/envoy/config/core/v3"
    12  	envoy_config_listener "github.com/cilium/proxy/go/envoy/config/listener/v3"
    13  	envoy_config_route_v3 "github.com/cilium/proxy/go/envoy/config/route/v3"
    14  	envoy_extensions_filters_http_router_v3 "github.com/cilium/proxy/go/envoy/extensions/filters/http/router/v3"
    15  	envoy_extensions_listener_tls_inspector_v3 "github.com/cilium/proxy/go/envoy/extensions/filters/listener/tls_inspector/v3"
    16  	envoy_extensions_filters_network_http_connection_manager_v3 "github.com/cilium/proxy/go/envoy/extensions/filters/network/http_connection_manager/v3"
    17  	envoy_config_upstream "github.com/cilium/proxy/go/envoy/extensions/upstreams/http/v3"
    18  	"google.golang.org/protobuf/proto"
    19  	"google.golang.org/protobuf/types/known/anypb"
    20  	"google.golang.org/protobuf/types/known/durationpb"
    21  	"google.golang.org/protobuf/types/known/wrapperspb"
    22  	corev1 "k8s.io/api/core/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  
    25  	"github.com/cilium/cilium/pkg/envoy"
    26  	ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    27  )
    28  
    29  func (r *ciliumEnvoyConfigReconciler) getEnvoyConfigForService(svc *corev1.Service) (*ciliumv2.CiliumEnvoyConfig, error) {
    30  	resources, err := r.getResources(svc)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	return &ciliumv2.CiliumEnvoyConfig{
    35  		TypeMeta: metav1.TypeMeta{
    36  			Kind:       ciliumv2.CECKindDefinition,
    37  			APIVersion: ciliumv2.SchemeGroupVersion.String(),
    38  		},
    39  		ObjectMeta: metav1.ObjectMeta{
    40  			Name:      fmt.Sprintf("%s-%s", ciliumEnvoyLBPrefix, svc.GetName()),
    41  			Namespace: svc.GetNamespace(),
    42  		},
    43  		Spec: ciliumv2.CiliumEnvoyConfigSpec{
    44  			Services: []*ciliumv2.ServiceListener{
    45  				{
    46  					Name:      svc.GetName(),
    47  					Namespace: svc.GetNamespace(),
    48  				},
    49  			},
    50  			Resources: resources,
    51  		},
    52  	}, nil
    53  }
    54  
    55  func (r *ciliumEnvoyConfigReconciler) getResources(svc *corev1.Service) ([]ciliumv2.XDSResource, error) {
    56  	var resources []ciliumv2.XDSResource
    57  	listener, err := r.getListenerResource(svc)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	resources = append(resources, listener)
    62  
    63  	routeConfig, err := r.getRouteConfigurationResource(svc)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	resources = append(resources, routeConfig)
    68  
    69  	clusters, err := r.getClusterResources(svc)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	resources = append(resources, clusters...)
    74  	return resources, nil
    75  }
    76  
    77  func (r *ciliumEnvoyConfigReconciler) getClusterResources(svc *corev1.Service) ([]ciliumv2.XDSResource, error) {
    78  	lbPolicy, ok := envoy_config_cluster_v3.Cluster_LbPolicy_value[strings.ToUpper(r.algorithm)]
    79  	if !ok {
    80  		lbPolicy = int32(envoy_config_cluster_v3.Cluster_ROUND_ROBIN)
    81  	}
    82  	cluster := &envoy_config_cluster_v3.Cluster{
    83  		Name:           getName(svc),
    84  		ConnectTimeout: &durationpb.Duration{Seconds: 5},
    85  		LbPolicy:       envoy_config_cluster_v3.Cluster_LbPolicy(lbPolicy),
    86  		TypedExtensionProtocolOptions: map[string]*anypb.Any{
    87  			"envoy.extensions.upstreams.http.v3.HttpProtocolOptions": r.toAny(&envoy_config_upstream.HttpProtocolOptions{
    88  				CommonHttpProtocolOptions: &envoy_config_core_v3.HttpProtocolOptions{
    89  					IdleTimeout: &durationpb.Duration{Seconds: int64(r.idleTimeoutSeconds)},
    90  				},
    91  				UpstreamProtocolOptions: &envoy_config_upstream.HttpProtocolOptions_UseDownstreamProtocolConfig{
    92  					UseDownstreamProtocolConfig: &envoy_config_upstream.HttpProtocolOptions_UseDownstreamHttpConfig{
    93  						Http2ProtocolOptions: &envoy_config_core_v3.Http2ProtocolOptions{},
    94  					},
    95  				},
    96  			}),
    97  		},
    98  		OutlierDetection: &envoy_config_cluster_v3.OutlierDetection{
    99  			SplitExternalLocalOriginErrors: true,
   100  			// The number of consecutive locally originated failures before ejection occurs.
   101  			ConsecutiveLocalOriginFailure: &wrapperspb.UInt32Value{Value: 2},
   102  		},
   103  		ClusterDiscoveryType: &envoy_config_cluster_v3.Cluster_Type{
   104  			Type: envoy_config_cluster_v3.Cluster_EDS,
   105  		},
   106  	}
   107  
   108  	mutatorFuncs := []clusterMutator{
   109  		lbModeClusterMutator(svc),
   110  	}
   111  	for _, fn := range mutatorFuncs {
   112  		cluster = fn(cluster)
   113  	}
   114  
   115  	clusterBytes, err := proto.Marshal(cluster)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	return []ciliumv2.XDSResource{
   120  		{
   121  			Any: &anypb.Any{
   122  				TypeUrl: envoy.ClusterTypeURL,
   123  				Value:   clusterBytes,
   124  			},
   125  		},
   126  	}, nil
   127  }
   128  
   129  func (r *ciliumEnvoyConfigReconciler) getRouteConfigurationResource(svc *corev1.Service) (ciliumv2.XDSResource, error) {
   130  	routeConfig := &envoy_config_route_v3.RouteConfiguration{
   131  		Name:         getName(svc),
   132  		VirtualHosts: []*envoy_config_route_v3.VirtualHost{r.getVirtualHost(svc)},
   133  	}
   134  
   135  	mutatorFuncs := []routeConfigMutator{}
   136  	for _, fn := range mutatorFuncs {
   137  		routeConfig = fn(routeConfig)
   138  	}
   139  
   140  	routeBytes, err := proto.Marshal(routeConfig)
   141  	if err != nil {
   142  		return ciliumv2.XDSResource{}, err
   143  	}
   144  	return ciliumv2.XDSResource{
   145  		Any: &anypb.Any{
   146  			TypeUrl: envoy.RouteTypeURL,
   147  			Value:   routeBytes,
   148  		},
   149  	}, nil
   150  }
   151  
   152  func (r *ciliumEnvoyConfigReconciler) getListenerResource(svc *corev1.Service) (ciliumv2.XDSResource, error) {
   153  	defaultHttpConnectionManager, err := r.getConnectionManager(svc)
   154  	if err != nil {
   155  		return ciliumv2.XDSResource{}, nil
   156  	}
   157  
   158  	var filterChains []*envoy_config_listener.FilterChain = []*envoy_config_listener.FilterChain{
   159  		{
   160  			FilterChainMatch: &envoy_config_listener.FilterChainMatch{
   161  				TransportProtocol: "raw_buffer",
   162  			},
   163  			Filters: []*envoy_config_listener.Filter{
   164  				{
   165  					Name: "envoy.filters.network.http_connection_manager",
   166  					ConfigType: &envoy_config_listener.Filter_TypedConfig{
   167  						TypedConfig: defaultHttpConnectionManager.Any,
   168  					},
   169  				},
   170  			},
   171  		},
   172  	}
   173  
   174  	listener := &envoy_config_listener.Listener{
   175  		Name:         getName(svc),
   176  		FilterChains: filterChains,
   177  		ListenerFilters: []*envoy_config_listener.ListenerFilter{
   178  			{
   179  				Name: "envoy.filters.listener.tls_inspector",
   180  				ConfigType: &envoy_config_listener.ListenerFilter_TypedConfig{
   181  					TypedConfig: r.toAny(&envoy_extensions_listener_tls_inspector_v3.TlsInspector{}),
   182  				},
   183  			},
   184  		},
   185  	}
   186  
   187  	mutatorFuncs := []listenerMutator{}
   188  	for _, fn := range mutatorFuncs {
   189  		listener = fn(listener)
   190  	}
   191  
   192  	listenerBytes, err := proto.Marshal(listener)
   193  	if err != nil {
   194  		return ciliumv2.XDSResource{}, err
   195  	}
   196  	return ciliumv2.XDSResource{
   197  		Any: &anypb.Any{
   198  			TypeUrl: envoy.ListenerTypeURL,
   199  			Value:   listenerBytes,
   200  		},
   201  	}, nil
   202  }
   203  
   204  func (r *ciliumEnvoyConfigReconciler) getConnectionManager(svc *corev1.Service) (ciliumv2.XDSResource, error) {
   205  	connectionManager := &envoy_extensions_filters_network_http_connection_manager_v3.HttpConnectionManager{
   206  		StatPrefix: getName(svc),
   207  		RouteSpecifier: &envoy_extensions_filters_network_http_connection_manager_v3.HttpConnectionManager_Rds{
   208  			Rds: &envoy_extensions_filters_network_http_connection_manager_v3.Rds{
   209  				RouteConfigName: getName(svc),
   210  			},
   211  		},
   212  		UseRemoteAddress: &wrapperspb.BoolValue{Value: true},
   213  		SkipXffAppend:    false,
   214  		HttpFilters: []*envoy_extensions_filters_network_http_connection_manager_v3.HttpFilter{
   215  			{
   216  				Name: "envoy.filters.http.router",
   217  				ConfigType: &envoy_extensions_filters_network_http_connection_manager_v3.HttpFilter_TypedConfig{
   218  					TypedConfig: r.toAny(&envoy_extensions_filters_http_router_v3.Router{}),
   219  				},
   220  			},
   221  		},
   222  	}
   223  
   224  	mutatorFuncs := []httpConnectionManagerMutator{
   225  		grpcHttpConnectionManagerMutator(svc),
   226  	}
   227  	for _, fn := range mutatorFuncs {
   228  		connectionManager = fn(connectionManager)
   229  	}
   230  
   231  	connectionManagerBytes, err := proto.Marshal(connectionManager)
   232  	if err != nil {
   233  		return ciliumv2.XDSResource{}, err
   234  	}
   235  
   236  	return ciliumv2.XDSResource{
   237  		Any: &anypb.Any{
   238  			TypeUrl: envoy.HttpConnectionManagerTypeURL,
   239  			Value:   connectionManagerBytes,
   240  		},
   241  	}, nil
   242  }
   243  
   244  func (r *ciliumEnvoyConfigReconciler) getVirtualHost(svc *corev1.Service) *envoy_config_route_v3.VirtualHost {
   245  	route := &envoy_config_route_v3.Route{
   246  		Match: &envoy_config_route_v3.RouteMatch{
   247  			PathSpecifier: &envoy_config_route_v3.RouteMatch_Prefix{
   248  				Prefix: "/",
   249  			},
   250  		},
   251  		Action: &envoy_config_route_v3.Route_Route{
   252  			Route: &envoy_config_route_v3.RouteAction{
   253  				ClusterSpecifier: &envoy_config_route_v3.RouteAction_Cluster{
   254  					Cluster: getName(svc),
   255  				},
   256  				MaxStreamDuration: &envoy_config_route_v3.RouteAction_MaxStreamDuration{
   257  					MaxStreamDuration: &durationpb.Duration{
   258  						Seconds: 0,
   259  					},
   260  				},
   261  			},
   262  		},
   263  	}
   264  
   265  	routeMutatorFuncs := []routeMutator{}
   266  	for _, fn := range routeMutatorFuncs {
   267  		route = fn(route)
   268  	}
   269  
   270  	virtualHost := &envoy_config_route_v3.VirtualHost{
   271  		Name:    getName(svc),
   272  		Domains: []string{"*"},
   273  		Routes:  []*envoy_config_route_v3.Route{route},
   274  	}
   275  
   276  	mutatorFuncs := []virtualHostMutator{}
   277  	for _, fn := range mutatorFuncs {
   278  		virtualHost = fn(virtualHost)
   279  	}
   280  
   281  	return virtualHost
   282  }
   283  
   284  func getName(obj metav1.Object) string {
   285  	return fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName())
   286  }
   287  
   288  func (r *ciliumEnvoyConfigReconciler) toAny(message proto.Message) *anypb.Any {
   289  	a, err := anypb.New(message)
   290  	if err != nil {
   291  		r.logger.WithError(err).Errorf("invalid message %s", message)
   292  		return nil
   293  	}
   294  	return a
   295  }