Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bb153a8

Browse files
xds: Trust Manager fix for when SAN validation against SNI sent doesn't apply (#12775)
Fixes a bug in propagation of `autoSniSanValidationDoesNotApply` (from PR #12422). It added an argument `autoSniSanValidationDoesNotApply` to `SslContextProviderSupplier.updateSslContext` that sets it on the `DynamicSslContextProvider` but because `UpstreamTlsContext` equals wasn't implemented, it was getting replaced by a new instance and the flag getting lost. This issue was identified when fixing an incorrect merge caused error in `CertProviderClientSslContextProvider` that recreated the trust manager without consideration to `autoSniSanValidationDoesNotApply`. It ought to have caused failure in the test `XdsSecurityClientServerTest.tlsClientServer_autoSniValidation_noSniApplicable_usesMatcherFromCmnVdnCtx` but it wasn't, because even though `autoSniSanValidationDoesNotApply` was false due to not getting the propagated true value, SAN matcher fallback was still happening because there was no server SNI sent. With the new changes, in addition to fixing the equals method, by moving the decision about autoSniSanValidationDoesNotApply to TlsContextManagerImpl.findOrCreateClientSslContextProvider I have eliminated the need to have a deferred setting of this decision via DynamicSslContextProvider.setAutoSniSanValidationDoesNotApply called from SslContextProviderSupplier.updateSslContext. Summary of Changes: 1. Enhanced `UpstreamTlsContext` (EnvoyServerProtoData.java): * Modified Caching Behavior: Implemented full equals() and hashCode() overrides for `UpstreamTlsContext`. Previously, it relied on the base class which only compared the commonTlsContext, causing different SNI or auto-validation settings to incorrectly share the same cache entry. * Normalization: Updated constructors to normalize the sni field to an empty string ("") if null. This prevents equality mismatches between context objects created from different sources (e.g., test helpers vs. Envoy protos). 2. Centralized Validation Logic in `TlsContextManagerImpl` * API Update: Modified `findOrCreateClientSslContextProvider` to accept the `autoSniSanValidationDoesNotApply` flag. * Effective Key Generation: If the flag is true, the manager now creates a specialized UpstreamTlsContext for the cache lookup where `autoSniSanValidation` is forced to false. This ensures the cache key reflects the effective validation behavior, allowing subchannels with different hostname/IP types to correctly share or separate their `SslContextProvider` instances. 3. Propagated Flag through `SslContextProviderSupplier`: * Updated the supplier to pass the validation flag from the `ClientSecurityHandler` down to the manager and provider. * Simplified the lifecycle by removing the need for a separate setter on the provider. 4. Refined `DynamicSslContextProvider` and `CertProviderClientSslContextProvider`: * Removed the redundant `setAutoSniSanValidationDoesNotApply` method and state. * The provider now retrieves the final validation state directly from its immutable `UpstreamTlsContext` during context computation. 5. Strengthened Integration Tests (XdsSecurityClientServerTest.java) * Modified the test `tlsClientServer_autoSniValidation_noSniApplicable_usesMatcherFromCmnVdnCtx` to enable `CertificateUtils.useChannelAuthorityIfNoSniApplicable.` * This forces the client to use the channel's authority as a non-empty SNI that mismatches the server certificate. This ensures the test rigorously asserts that SNI validation is truly disabled when it "does not apply" (e.g., when no specific SNI was requested by the user). 6. Updated Test Suite & API Alignment * SecurityProtocolNegotiatorsTest: Updated test cases to pass true for the validation flag. This was required because the new strict cache key equality exposed that the test was previously using a state that didn't match the ClientSecurityHandler it was testing.
1 parent 01c0a90 commit bb153a8

6 files changed

Lines changed: 67 additions & 46 deletions

File tree

xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,17 @@ public static final class UpstreamTlsContext extends BaseTlsContext {
7979

8080
@VisibleForTesting
8181
public UpstreamTlsContext(CommonTlsContext commonTlsContext) {
82+
this(commonTlsContext, "", false, false);
83+
}
84+
85+
@VisibleForTesting
86+
public UpstreamTlsContext(
87+
CommonTlsContext commonTlsContext, String sni, boolean autoHostSni,
88+
boolean autoSniSanValidation) {
8289
super(commonTlsContext);
83-
this.sni = null;
84-
this.autoHostSni = false;
85-
this.autoSniSanValidation = false;
90+
this.sni = sni == null ? "" : sni;
91+
this.autoHostSni = autoHostSni;
92+
this.autoSniSanValidation = autoSniSanValidation;
8693
}
8794

8895
@VisibleForTesting
@@ -122,6 +129,26 @@ public String toString() {
122129
+ "\nauto_sni_san_validation=" + autoSniSanValidation
123130
+ "}";
124131
}
132+
133+
@Override
134+
public boolean equals(Object o) {
135+
if (this == o) {
136+
return true;
137+
}
138+
if (o == null || getClass() != o.getClass()) {
139+
return false;
140+
}
141+
UpstreamTlsContext that = (UpstreamTlsContext) o;
142+
return autoHostSni == that.autoHostSni
143+
&& autoSniSanValidation == that.autoSniSanValidation
144+
&& Objects.equals(commonTlsContext, that.commonTlsContext)
145+
&& Objects.equals(sni, that.sni);
146+
}
147+
148+
@Override
149+
public int hashCode() {
150+
return Objects.hash(commonTlsContext, sni, autoHostSni, autoSniSanValidation);
151+
}
125152
}
126153

127154
public static final class DownstreamTlsContext extends BaseTlsContext {

xds/src/main/java/io/grpc/xds/internal/security/DynamicSslContextProvider.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public abstract class DynamicSslContextProvider extends SslContextProvider {
4444
@Nullable protected final CertificateValidationContext staticCertificateValidationContext;
4545
@Nullable protected AbstractMap.SimpleImmutableEntry<SslContext, X509TrustManager>
4646
sslContextAndTrustManager;
47-
protected boolean autoSniSanValidationDoesNotApply;
4847

4948
protected DynamicSslContextProvider(
5049
BaseTlsContext tlsContext, CertificateValidationContext staticCertValidationContext) {
@@ -60,10 +59,6 @@ protected DynamicSslContextProvider(
6059

6160
protected abstract CertificateValidationContext generateCertificateValidationContext();
6261

63-
public void setAutoSniSanValidationDoesNotApply() {
64-
autoSniSanValidationDoesNotApply = true;
65-
}
66-
6762
/** Gets a server or client side SslContextBuilder. */
6863
protected abstract AbstractMap.SimpleImmutableEntry<SslContextBuilder, X509TrustManager>
6964
getSslContextBuilderAndTrustManager(

xds/src/main/java/io/grpc/xds/internal/security/SslContextProviderSupplier.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,11 @@ public synchronized void updateSslContext(
6060
try {
6161
if (!shutdown) {
6262
if (sslContextProvider == null) {
63-
sslContextProvider = getSslContextProvider();
64-
if (tlsContext instanceof UpstreamTlsContext && autoSniSanValidationDoesNotApply) {
65-
((DynamicSslContextProvider) sslContextProvider).setAutoSniSanValidationDoesNotApply();
66-
}
63+
sslContextProvider = getSslContextProvider(autoSniSanValidationDoesNotApply);
6764
}
6865
}
6966
// we want to increment the ref-count so call findOrCreate again...
70-
final SslContextProvider toRelease = getSslContextProvider();
67+
final SslContextProvider toRelease = getSslContextProvider(autoSniSanValidationDoesNotApply);
7168
toRelease.addCallback(
7269
new SslContextProvider.Callback(callback.getExecutor()) {
7370

@@ -102,11 +99,20 @@ private void releaseSslContextProvider(SslContextProvider toRelease) {
10299
}
103100
}
104101

105-
private SslContextProvider getSslContextProvider() {
106-
return tlsContext instanceof UpstreamTlsContext
107-
? tlsContextManager.findOrCreateClientSslContextProvider((UpstreamTlsContext) tlsContext)
108-
: tlsContextManager.findOrCreateServerSslContextProvider(
109-
(DownstreamTlsContext) tlsContext);
102+
private SslContextProvider getSslContextProvider(boolean autoSniSanValidationDoesNotApply) {
103+
if (tlsContext instanceof UpstreamTlsContext) {
104+
UpstreamTlsContext upstreamTlsContext = (UpstreamTlsContext) tlsContext;
105+
if (autoSniSanValidationDoesNotApply && upstreamTlsContext.getAutoSniSanValidation()) {
106+
upstreamTlsContext = new UpstreamTlsContext(
107+
upstreamTlsContext.getCommonTlsContext(),
108+
upstreamTlsContext.getSni(),
109+
upstreamTlsContext.getAutoHostSni(),
110+
false);
111+
}
112+
return tlsContextManager.findOrCreateClientSslContextProvider(upstreamTlsContext);
113+
}
114+
return tlsContextManager.findOrCreateServerSslContextProvider(
115+
(DownstreamTlsContext) tlsContext);
110116
}
111117

112118
@VisibleForTesting public boolean isShutdown() {

xds/src/main/java/io/grpc/xds/internal/security/certprovider/CertProviderClientSslContextProvider.java

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,39 +58,25 @@ final class CertProviderClientSslContextProvider extends CertProviderSslContextP
5858
getSslContextBuilderAndTrustManager(
5959
CertificateValidationContext certificateValidationContext)
6060
throws CertStoreException {
61-
SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
61+
UpstreamTlsContext upstreamTlsContext = (UpstreamTlsContext) tlsContext;
62+
XdsTrustManagerFactory trustManagerFactory;
6263
if (savedSpiffeTrustMap != null) {
63-
sslContextBuilder = sslContextBuilder.trustManager(
64-
new XdsTrustManagerFactory(
65-
savedSpiffeTrustMap,
66-
certificateValidationContext,
67-
autoSniSanValidationDoesNotApply
68-
? false : ((UpstreamTlsContext) tlsContext).getAutoSniSanValidation()));
64+
trustManagerFactory = new XdsTrustManagerFactory(
65+
savedSpiffeTrustMap,
66+
certificateValidationContext,
67+
upstreamTlsContext.getAutoSniSanValidation());
6968
} else if (savedTrustedRoots != null) {
70-
sslContextBuilder = sslContextBuilder.trustManager(
71-
new XdsTrustManagerFactory(
69+
trustManagerFactory = new XdsTrustManagerFactory(
7270
savedTrustedRoots.toArray(new X509Certificate[0]),
7371
certificateValidationContext,
74-
autoSniSanValidationDoesNotApply
75-
? false : ((UpstreamTlsContext) tlsContext).getAutoSniSanValidation()));
72+
upstreamTlsContext.getAutoSniSanValidation());
7673
} else {
7774
// Should be impossible because of the check in CertProviderClientSslContextProviderFactory
7875
throw new IllegalStateException("There must be trusted roots or a SPIFFE trust map");
7976
}
80-
XdsTrustManagerFactory trustManagerFactory;
81-
if (savedSpiffeTrustMap != null) {
82-
trustManagerFactory = new XdsTrustManagerFactory(
83-
savedSpiffeTrustMap,
84-
certificateValidationContext,
85-
((UpstreamTlsContext) tlsContext).getAutoSniSanValidation());
86-
sslContextBuilder = sslContextBuilder.trustManager(trustManagerFactory);
87-
} else {
88-
trustManagerFactory = new XdsTrustManagerFactory(
89-
savedTrustedRoots.toArray(new X509Certificate[0]),
90-
certificateValidationContext,
91-
((UpstreamTlsContext) tlsContext).getAutoSniSanValidation());
92-
sslContextBuilder = sslContextBuilder.trustManager(trustManagerFactory);
93-
}
77+
78+
SslContextBuilder sslContextBuilder =
79+
GrpcSslContexts.forClient().trustManager(trustManagerFactory);
9480
if (isMtls()) {
9581
sslContextBuilder.keyManager(savedKey, savedCertChain);
9682
}

xds/src/test/java/io/grpc/xds/XdsSecurityClientServerTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import io.grpc.xds.internal.security.SslContextProviderSupplier;
7878
import io.grpc.xds.internal.security.TlsContextManagerImpl;
7979
import io.grpc.xds.internal.security.certprovider.FileWatcherCertificateProviderProvider;
80+
import io.grpc.xds.internal.security.trust.CertificateUtils;
8081
import io.netty.handler.ssl.NotSslRecordException;
8182
import java.io.File;
8283
import java.io.FileOutputStream;
@@ -378,7 +379,11 @@ public void tlsClientServer_autoSniValidation_sniFromHostname()
378379
public void tlsClientServer_autoSniValidation_noSniApplicable_usesMatcherFromCmnVdnCtx()
379380
throws Exception {
380381
Path trustStoreFilePath = getCacertFilePathForTestCa();
382+
boolean originalUseChannelAuthorityIfNoSniApplicable =
383+
CertificateUtils.useChannelAuthorityIfNoSniApplicable;
381384
try {
385+
CertificateUtils.useChannelAuthorityIfNoSniApplicable =
386+
true;
382387
setTrustStoreSystemProperties(trustStoreFilePath.toAbsolutePath().toString());
383388
DownstreamTlsContext downstreamTlsContext =
384389
setBootstrapInfoAndBuildDownstreamTlsContext(SERVER_1_PEM_FILE, null, null, null, null,
@@ -398,6 +403,8 @@ public void tlsClientServer_autoSniValidation_noSniApplicable_usesMatcherFromCmn
398403
getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ OVERRIDE_AUTHORITY);
399404
unaryRpc(/* requestMessage= */ "buddy", blockingStub);
400405
} finally {
406+
CertificateUtils.useChannelAuthorityIfNoSniApplicable =
407+
originalUseChannelAuthorityIfNoSniApplicable;
401408
Files.deleteIfExists(trustStoreFilePath);
402409
clearTrustStoreSystemProperties();
403410
}

xds/src/test/java/io/grpc/xds/internal/security/SecurityProtocolNegotiatorsTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public void updateSslContextAndExtendedX509TrustManager(
209209
protected void onException(Throwable throwable) {
210210
future.set(throwable);
211211
}
212-
}, false);
212+
}, true);
213213
assertThat(executor.runDueTasks()).isEqualTo(1);
214214
channel.runPendingTasks();
215215
Object fromFuture = future.get(2, TimeUnit.SECONDS);
@@ -356,7 +356,7 @@ public void updateSslContextAndExtendedX509TrustManager(
356356
protected void onException(Throwable throwable) {
357357
future.set(throwable);
358358
}
359-
}, false);
359+
}, true);
360360
channel.runPendingTasks(); // need this for tasks to execute on eventLoop
361361
assertThat(executor.runDueTasks()).isEqualTo(1);
362362
Object fromFuture = future.get(2, TimeUnit.SECONDS);
@@ -493,7 +493,7 @@ public void updateSslContextAndExtendedX509TrustManager(
493493
protected void onException(Throwable throwable) {
494494
future.set(throwable);
495495
}
496-
}, false);
496+
}, true);
497497
executor.runDueTasks();
498498
channel.runPendingTasks(); // need this for tasks to execute on eventLoop
499499
Object fromFuture = future.get(5, TimeUnit.SECONDS);

0 commit comments

Comments
 (0)