From 371e0cf9c6cf4bbed7d8a67853392c3e8bcd820a Mon Sep 17 00:00:00 2001 From: Kuromesi Date: Sun, 20 Sep 2026 09:59:14 +0800 Subject: [PATCH] fix: disable cross-SNI TLS session reuse in egress Disable upstream session resumption on all four MITM origin clusters because the pinned Envoy version shares cached sessions across server names. This prevents SAN validation failures when another origin accepts a cached ticket while retaining certificate verification and HTTP connection reuse. --- .../internal/router/egressmanifest_test.go | 44 +++++++++++++++++++ .../atenet-egress-with-sdsmint.yaml | 9 ++++ 2 files changed, 53 insertions(+) diff --git a/cmd/atenet/internal/router/egressmanifest_test.go b/cmd/atenet/internal/router/egressmanifest_test.go index 398de60df8..5f0c004257 100644 --- a/cmd/atenet/internal/router/egressmanifest_test.go +++ b/cmd/atenet/internal/router/egressmanifest_test.go @@ -16,6 +16,7 @@ package router import ( "os" + "slices" "strings" "testing" "time" @@ -31,6 +32,49 @@ var egressManifests = []string{ "../../../../manifests/ate-install/atenet-egress-with-sdsmint.yaml", } +// Missing max_session_keys defaults to 1, so it must be explicitly zero. +// Each MITM origin cluster serves multiple SNIs through one TLS context. +func TestEgressOriginDisablesSessionResumption(t *testing.T) { + var bootstrap struct { + StaticResources struct { + Clusters []struct { + Name string `json:"name"` + TransportSocket struct { + TypedConfig struct { + Type string `json:"@type"` + MaxSessionKeys *uint32 `json:"max_session_keys"` + } `json:"typed_config"` + } `json:"transport_socket"` + } `json:"clusters"` + } `json:"static_resources"` + } + raw := envoyConfig(t, "../../../../manifests/ate-install/atenet-egress-with-sdsmint.yaml") + if err := yaml.Unmarshal([]byte(raw), &bootstrap); err != nil { + t.Fatal(err) + } + var checked []string + for _, cluster := range bootstrap.StaticResources.Clusters { + ctx := cluster.TransportSocket.TypedConfig + if ctx.Type != "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext" { + continue + } + checked = append(checked, cluster.Name) + t.Run(cluster.Name, func(t *testing.T) { + if ctx.MaxSessionKeys == nil { + t.Fatal("max_session_keys is missing; Envoy enables session resumption by default") + } + if *ctx.MaxSessionKeys != 0 { + t.Errorf("max_session_keys = %d, want 0 to prevent cross-SNI session reuse", *ctx.MaxSessionKeys) + } + }) + } + slices.Sort(checked) + want := []string{"egress_forward_proxy", "egress_forward_proxy_grpc", "egress_original_dst_tls", "egress_original_dst_tls_grpc"} + if !slices.Equal(checked, want) { + t.Errorf("checked TLS clusters = %v, want %v", checked, want) + } +} + // TestEgressManifestsDisableTheConnectTimeout is the static-config half of // TestBuildConnectRoutes_DisablesTimeout: Envoy applies a route's timeout to a // CONNECT tunnel's whole lifetime rather than to its headers, so a route left diff --git a/manifests/ate-install/atenet-egress-with-sdsmint.yaml b/manifests/ate-install/atenet-egress-with-sdsmint.yaml index 0f67d1be30..d5eb21008c 100644 --- a/manifests/ate-install/atenet-egress-with-sdsmint.yaml +++ b/manifests/ate-install/atenet-egress-with-sdsmint.yaml @@ -779,6 +779,11 @@ data: # The only cluster that dials the internet. Reached from the MITM leg, so # the host it resolves comes from the decrypted request's own Host header # rather than from the CONNECT authority. + # Disable session resumption on all four origin TLS clusters: Envoy 1.39 + # shares sessions across SNIs within a client context, which can fail SAN + # validation if another origin accepts the ticket. Revisit after upgrading + # to a build with https://github.com/envoyproxy/envoy/pull/45982 enabled. + # New TLS connections use full handshakes; HTTP connection reuse is unchanged. - name: egress_forward_proxy lb_policy: CLUSTER_PROVIDED connect_timeout: 5s @@ -809,6 +814,7 @@ data: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + max_session_keys: 0 common_tls_context: validation_context: trusted_ca: @@ -837,6 +843,7 @@ data: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + max_session_keys: 0 common_tls_context: validation_context: trusted_ca: @@ -896,6 +903,7 @@ data: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + max_session_keys: 0 common_tls_context: validation_context: trusted_ca: @@ -918,6 +926,7 @@ data: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + max_session_keys: 0 common_tls_context: validation_context: trusted_ca: