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 }