Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 34 additions & 22 deletions internal/gatewayapi/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources *
destName := fmt.Sprintf("accesslog_als_%d_%d", i, j)
settingName := irDestinationSettingName(destName, -1)
// TODO: how to get authority from the backendRefs?
ds, traffic, err := t.processBackendRefs(settingName, sink.ALS.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
ds, traffic, err := t.processBackendRefsForTelemetry(settingName, sink.ALS.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -679,7 +679,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources *
// TODO: rename this, so that we can share backend with tracing?
destName := fmt.Sprintf("accesslog_otel_%d_%d", i, j)
settingName := irDestinationSettingName(destName, -1)
ds, traffic, err := t.processBackendRefs(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
ds, traffic, err := t.processBackendRefsForTelemetry(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -742,7 +742,7 @@ func (t *Translator) processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.Envo
// TODO: rename this, so that we can share backend with accesslog?
destName := "tracing"
settingName := irDestinationSettingName(destName, -1)
ds, traffic, err := t.processBackendRefs(settingName, tracing.Provider.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
ds, traffic, err := t.processBackendRefsForTelemetry(settingName, tracing.Provider.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -879,7 +879,7 @@ func (t *Translator) processMetrics(envoyproxy *egv1a1.EnvoyProxy, resources *re

destName := fmt.Sprintf("metrics_otel_%d", i)
settingName := irDestinationSettingName(destName, -1)
ds, _, err := t.processBackendRefs(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
ds, _, err := t.processBackendRefsForTelemetry(settingName, sink.OpenTelemetry.BackendCluster, envoyproxy.Namespace, resources, envoyproxy)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -927,54 +927,66 @@ func (t *Translator) processMetrics(envoyproxy *egv1a1.EnvoyProxy, resources *re
}, resolvedSinks, nil
}

func (t *Translator) processBackendRefs(name string, backendCluster egv1a1.BackendCluster, namespace string,
func (t *Translator) processBackendRefsForTelemetry(name string, backendCluster egv1a1.BackendCluster, namespace string,
resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy,
) ([]*ir.DestinationSetting, *ir.TrafficFeatures, error) {
traffic, err := translateTrafficFeatures(backendCluster.BackendSettings)
if err != nil {
return nil, nil, err
}

parent := gwapiv1.ParentReference{
Comment thread
codefromthecrypt marked this conversation as resolved.
Group: ptr.To(gwapiv1.Group(egv1a1.GroupName)),
Kind: ptr.To(gwapiv1.Kind(egv1a1.KindEnvoyProxy)),
Namespace: ptr.To(gwapiv1.Namespace(envoyProxy.Namespace)),
Name: gwapiv1.ObjectName(envoyProxy.Name),
}

result := make([]*ir.DestinationSetting, 0, len(backendCluster.BackendRefs))
for i := range backendCluster.BackendRefs {
ref := &backendCluster.BackendRefs[i]
ns := NamespaceDerefOr(ref.Namespace, namespace)
kind := KindDerefOr(ref.Kind, resource.KindService)

var ds *ir.DestinationSetting
switch kind {
case resource.KindService:
if err := t.validateBackendRefService(ref.BackendObjectReference, ns, corev1.ProtocolTCP); err != nil {
return nil, nil, err
}
ds, err := t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, envoyProxy, nil)
ds, err = t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, envoyProxy, nil)
if err != nil {
return nil, nil, err
}
result = append(result, ds)
case resource.KindBackend:
if err := t.validateBackendRefBackend(ref.BackendObjectReference, resources, ns, true); err != nil {
return nil, nil, err
}
ds := t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP)
ds = t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP)
// Dynamic resolver destinations are not supported for none-route destinations
if ds.IsDynamicResolver {
return nil, nil, errors.New("dynamic resolver destinations are not supported")
}
// Apply TLS config for backend (telemetry) clusters
backend := t.GetBackend(ns, string(ref.Name))
if backend.Spec.TLS != nil {
tlsConfig, err := t.processServerValidationTLSSettings(backend)
if err != nil {
return nil, nil, err
}
ds.TLS = tlsConfig
// Infer SNI from FQDN for telemetry backends (no Host header available)
if ds.TLS.SNI == nil && len(backend.Spec.Endpoints) == 1 && backend.Spec.Endpoints[0].FQDN != nil {
ds.TLS.SNI = &backend.Spec.Endpoints[0].FQDN.Hostname
}
}
result = append(result, ds)
default:
return nil, nil, fmt.Errorf("unsupported kind for backendRefs: %s", kind)
}

// Apply TLS from Backend resource, BackendTLSPolicy, and EnvoyProxy.
backendTLS, err := t.applyBackendTLSSetting(ref.BackendObjectReference, ns, parent, resources, envoyProxy)
if err != nil {
return nil, nil, err
}
ds.TLS = backendTLS

// Infer SNI from FQDN for telemetry backends (no Host header available).
if ds.TLS != nil && ds.TLS.SNI == nil && kind == resource.KindBackend {
backend := t.GetBackend(ns, string(ref.Name))
if len(backend.Spec.Endpoints) == 1 && backend.Spec.Endpoints[0].FQDN != nil {
ds.TLS.SNI = &backend.Spec.Endpoints[0].FQDN.Hostname
}
}

result = append(result, ds)
}
if len(result) == 0 {
return nil, traffic, nil
Expand Down
175 changes: 174 additions & 1 deletion internal/gatewayapi/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,7 @@ func TestProcessBackendRefsSNIInference(t *testing.T) {
}},
}

ds, _, err := translator.processBackendRefs("test", backendCluster, ns, resources, nil)
ds, _, err := translator.processBackendRefsForTelemetry("test", backendCluster, ns, resources, &egv1a1.EnvoyProxy{ObjectMeta: metav1.ObjectMeta{Namespace: "envoy-gateway-system", Name: "test-proxy"}})
require.NoError(t, err)
require.Len(t, ds, 1)

Expand All @@ -1315,3 +1315,176 @@ func TestProcessBackendRefsSNIInference(t *testing.T) {
})
}
}

func TestProcessBackendRefsBackendTLSPolicy(t *testing.T) {
ns := "test-ns"
backendName := "otel-collector"
serviceName := "otel-svc"
envoyProxy := &egv1a1.EnvoyProxy{ObjectMeta: metav1.ObjectMeta{Namespace: "envoy-gateway-system", Name: "test-proxy"}}

backendBackendCluster := egv1a1.BackendCluster{BackendRefs: []egv1a1.BackendRef{{
BackendObjectReference: gwapiv1.BackendObjectReference{
Group: ptr.To(gwapiv1.Group("gateway.envoyproxy.io")), Kind: ptr.To(gwapiv1.Kind("Backend")),
Name: gwapiv1.ObjectName(backendName), Namespace: ptr.To(gwapiv1.Namespace(ns)),
},
}}}
otelBackend := &egv1a1.Backend{
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: backendName},
Spec: egv1a1.BackendSpec{Endpoints: []egv1a1.BackendEndpoint{{FQDN: &egv1a1.FQDNEndpoint{Hostname: "otel.example.com", Port: 443}}}},
}
otelBackendWithTLS := &egv1a1.Backend{
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: backendName},
Spec: egv1a1.BackendSpec{
Endpoints: []egv1a1.BackendEndpoint{{FQDN: &egv1a1.FQDNEndpoint{Hostname: "otel.example.com", Port: 443}}},
TLS: &egv1a1.BackendTLSSettings{
WellKnownCACertificates: ptr.To(gwapiv1.WellKnownCACertificatesSystem),
SNI: ptr.To(gwapiv1.PreciseHostname("backend-sni.example.com")),
},
},
}
otelBackendPolicy := &gwapiv1.BackendTLSPolicy{
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: "otel-tls"},
Spec: gwapiv1.BackendTLSPolicySpec{
TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{{
LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
Group: "gateway.envoyproxy.io", Kind: "Backend", Name: gwapiv1.ObjectName(backendName),
},
}},
Validation: gwapiv1.BackendTLSPolicyValidation{
WellKnownCACertificates: ptr.To(gwapiv1.WellKnownCACertificatesSystem),
Hostname: "otel.example.com",
},
},
}
backendEndpoints := []*ir.DestinationEndpoint{{Host: "otel.example.com", Port: 443}}
backendMetadata := &ir.ResourceMetadata{Name: backendName, Namespace: ns}
backendPolicyTLS := &ir.TLSUpstreamConfig{
SNI: ptr.To("otel.example.com"), UseSystemTrustStore: true,
CACertificate: &ir.TLSCACertificate{Name: "otel-tls/test-ns-ca"}, SubjectAltNames: []ir.SubjectAltName{},
}

serviceBackendCluster := egv1a1.BackendCluster{BackendRefs: []egv1a1.BackendRef{{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: gwapiv1.ObjectName(serviceName), Namespace: ptr.To(gwapiv1.Namespace(ns)),
Port: ptr.To(gwapiv1.PortNumber(4317)),
},
}}}
otelService := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: serviceName},
Spec: corev1.ServiceSpec{ClusterIP: "7.7.7.7", Ports: []corev1.ServicePort{{
Name: "grpc", Port: 4317, TargetPort: intstr.IntOrString{IntVal: 4317}, Protocol: corev1.ProtocolTCP,
}}},
}
otelEndpointSlice := &discoveryv1.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: serviceName, Labels: map[string]string{"kubernetes.io/service-name": serviceName}},
AddressType: discoveryv1.AddressTypeIPv4,
Endpoints: []discoveryv1.Endpoint{{Addresses: []string{"7.7.7.7"}, Conditions: discoveryv1.EndpointConditions{Ready: ptr.To(true)}}},
Ports: []discoveryv1.EndpointPort{{Name: ptr.To("grpc"), Port: ptr.To(int32(4317)), Protocol: ptr.To(corev1.ProtocolTCP)}},
}
otelServicePolicy := &gwapiv1.BackendTLSPolicy{
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: "otel-svc-tls"},
Spec: gwapiv1.BackendTLSPolicySpec{
TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{{
LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{Kind: "Service", Name: gwapiv1.ObjectName(serviceName)},
SectionName: ptr.To(gwapiv1.SectionName("grpc")),
}},
Validation: gwapiv1.BackendTLSPolicyValidation{
WellKnownCACertificates: ptr.To(gwapiv1.WellKnownCACertificatesSystem),
Hostname: "otel-svc.example.com",
},
},
}
serviceEndpoints := []*ir.DestinationEndpoint{{Host: "7.7.7.7", Port: 4317}}
serviceMetadata := &ir.ResourceMetadata{Name: serviceName, Namespace: ns, SectionName: "4317"}
servicePolicyTLS := &ir.TLSUpstreamConfig{
SNI: ptr.To("otel-svc.example.com"), UseSystemTrustStore: true,
CACertificate: &ir.TLSCACertificate{Name: "otel-svc-tls/test-ns-ca"}, SubjectAltNames: []ir.SubjectAltName{},
}

tests := []struct {
name string
backendCluster egv1a1.BackendCluster
context *TranslatorContext
resources *resource.Resources
expected []*ir.DestinationSetting
expectedErr string
}{
{
name: "BackendTLSPolicy without Backend TLS",
backendCluster: backendBackendCluster,
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackend}},
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackend}, BackendTLSPolicies: []*gwapiv1.BackendTLSPolicy{otelBackendPolicy}},
expected: []*ir.DestinationSetting{{
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata, TLS: backendPolicyTLS,
}},
},
{
name: "no BackendTLSPolicy and no Backend TLS",
backendCluster: backendBackendCluster,
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackend}},
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackend}},
expected: []*ir.DestinationSetting{{
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata,
}},
},
{
name: "Backend ref without namespace, no TLS, no BackendTLSPolicy",
backendCluster: egv1a1.BackendCluster{BackendRefs: []egv1a1.BackendRef{{
BackendObjectReference: gwapiv1.BackendObjectReference{
Group: ptr.To(gwapiv1.Group("gateway.envoyproxy.io")), Kind: ptr.To(gwapiv1.Kind("Backend")),
Name: gwapiv1.ObjectName(backendName),
},
}}},
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackend}},
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackend}},
expected: []*ir.DestinationSetting{{
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata,
}},
},
{
name: "BackendTLSPolicy overrides Backend TLS SNI",
backendCluster: backendBackendCluster,
context: &TranslatorContext{BackendMap: map[types.NamespacedName]*egv1a1.Backend{{Namespace: ns, Name: backendName}: otelBackendWithTLS}},
resources: &resource.Resources{Backends: []*egv1a1.Backend{otelBackendWithTLS}, BackendTLSPolicies: []*gwapiv1.BackendTLSPolicy{otelBackendPolicy}},
expected: []*ir.DestinationSetting{{
Name: "test", Protocol: ir.TCP, Endpoints: backendEndpoints,
AddressType: ptr.To(ir.FQDN), Metadata: backendMetadata, TLS: backendPolicyTLS,
}},
},
{
name: "BackendTLSPolicy for Service",
backendCluster: serviceBackendCluster,
context: &TranslatorContext{
ServiceMap: map[types.NamespacedName]*corev1.Service{{Namespace: ns, Name: serviceName}: otelService},
EndpointSliceMap: map[backendServiceKey][]*discoveryv1.EndpointSlice{
{kind: resource.KindService, namespace: ns, name: serviceName}: {otelEndpointSlice},
},
},
resources: &resource.Resources{BackendTLSPolicies: []*gwapiv1.BackendTLSPolicy{otelServicePolicy}},
expected: []*ir.DestinationSetting{{
Name: "test", Protocol: ir.TCP, Endpoints: serviceEndpoints,
AddressType: ptr.To(ir.IP), Metadata: serviceMetadata, TLS: servicePolicyTLS,
}},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
translator := &Translator{
TranslatorContext: tc.context,
BackendEnabled: true,
GatewayControllerName: egv1a1.GatewayControllerName,
}
ds, _, err := translator.processBackendRefsForTelemetry("test", tc.backendCluster, ns, tc.resources, envoyProxy)
if tc.expectedErr != "" {
require.EqualError(t, err, tc.expectedErr)
} else {
require.NoError(t, err)
require.Equal(t, tc.expected, ds)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
envoyProxyForGatewayClass:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
namespace: envoy-gateway-system
name: telemetry-btls
spec:
telemetry:
accessLog:
settings:
- format:
type: Text
text: |
[%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE%
sinks:
- type: OpenTelemetry
openTelemetry:
backendRefs:
- name: otel-collector
namespace: envoy-gateway
kind: Backend
group: gateway.envoyproxy.io
resourceAttributes:
k8s.cluster.name: "cluster-1"
tracing:
provider:
type: OpenTelemetry
backendRefs:
- name: otel-collector
namespace: envoy-gateway
kind: Backend
group: gateway.envoyproxy.io
samplingRate: 100
metrics:
sinks:
- type: OpenTelemetry
openTelemetry:
backendRefs:
- name: otel-collector
namespace: envoy-gateway
kind: Backend
group: gateway.envoyproxy.io
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-1
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
backends:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: Backend
metadata:
name: otel-collector
namespace: envoy-gateway
spec:
endpoints:
- fqdn:
hostname: otel-collector.example.com
port: 443
backendTLSPolicies:
- apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
name: otel-tls
namespace: envoy-gateway
spec:
targetRefs:
- group: gateway.envoyproxy.io
kind: Backend
name: otel-collector
validation:
wellKnownCACertificates: System
hostname: otel-collector.example.com
Loading
Loading