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

Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@
import com.google.api.gax.rpc.TransportChannel;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.api.gax.rpc.internal.EnvironmentProvider;
import com.google.api.gax.rpc.mtls.MtlsProvider;
import com.google.api.gax.rpc.mtls.CertificateBasedAccess;
import com.google.auth.ApiKeyCredentials;
import com.google.auth.Credentials;
import com.google.auth.mtls.CertificateSourceUnavailableException;
import com.google.auth.mtls.DefaultMtlsProviderFactory;
import com.google.auth.mtls.MtlsProvider;
import com.google.auth.oauth2.ComputeEngineCredentials;
import com.google.auth.oauth2.SecureSessionAgent;
import com.google.auth.oauth2.SecureSessionAgentConfig;
Expand Down Expand Up @@ -150,6 +153,7 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP
@Nullable private final Boolean allowNonDefaultServiceAccount;
@VisibleForTesting final ImmutableMap<String, ?> directPathServiceConfig;
@Nullable private final MtlsProvider mtlsProvider;
private final CertificateBasedAccess certificateBasedAccess;
@Nullable private final SecureSessionAgent s2aConfigProvider;
private final List<HardBoundTokenTypes> allowedHardBoundTokenTypes;
@VisibleForTesting final Map<String, String> headersWithDuplicatesRemoved = new HashMap<>();
Expand Down Expand Up @@ -183,6 +187,7 @@ private InstantiatingGrpcChannelProvider(Builder builder) {
this.mtlsEndpoint = builder.mtlsEndpoint;
this.allowedHardBoundTokenTypes = builder.allowedHardBoundTokenTypes;
this.mtlsProvider = builder.mtlsProvider;
this.certificateBasedAccess = builder.certificateBasedAccess;
this.s2aConfigProvider = builder.s2aConfigProvider;
this.envProvider = builder.envProvider;
this.interceptorProvider = builder.interceptorProvider;
Expand Down Expand Up @@ -484,7 +489,10 @@ boolean canUseDirectPathWithUniverseDomain() {

@VisibleForTesting
ChannelCredentials createMtlsChannelCredentials() throws IOException, GeneralSecurityException {
if (mtlsProvider.useMtlsClientCertificate()) {
if (mtlsProvider == null) {
return null;
}
if (certificateBasedAccess.useMtlsClientCertificate()) {
KeyStore mtlsKeyStore = mtlsProvider.getKeyStore();
if (mtlsKeyStore != null) {
KeyManagerFactory factory =
Expand Down Expand Up @@ -853,7 +861,8 @@ public static final class Builder {
private boolean useS2A;
private EnvironmentProvider envProvider;
private SecureSessionAgent s2aConfigProvider = SecureSessionAgent.create();
private MtlsProvider mtlsProvider = new MtlsProvider();
@Nullable private MtlsProvider mtlsProvider;
private CertificateBasedAccess certificateBasedAccess;
@Nullable private GrpcInterceptorProvider interceptorProvider;
@Nullable private Integer maxInboundMessageSize;
@Nullable private Integer maxInboundMetadataSize;
Expand Down Expand Up @@ -904,6 +913,7 @@ private Builder(InstantiatingGrpcChannelProvider provider) {
this.allowedHardBoundTokenTypes = provider.allowedHardBoundTokenTypes;
this.directPathServiceConfig = provider.directPathServiceConfig;
this.mtlsProvider = provider.mtlsProvider;
this.certificateBasedAccess = provider.certificateBasedAccess;
this.s2aConfigProvider = provider.s2aConfigProvider;
}

Expand Down Expand Up @@ -994,6 +1004,12 @@ Builder setMtlsProvider(MtlsProvider mtlsProvider) {
return this;
}

@VisibleForTesting
Builder setCertificateBasedAccess(CertificateBasedAccess certificateBasedAccess) {
this.certificateBasedAccess = certificateBasedAccess;
return this;
}

@VisibleForTesting
Builder setS2AConfigProvider(SecureSessionAgent s2aConfigProvider) {
this.s2aConfigProvider = s2aConfigProvider;
Expand Down Expand Up @@ -1269,6 +1285,25 @@ CallCredentials createHardBoundTokensCallCredentials(
}

public InstantiatingGrpcChannelProvider build() {
if (certificateBasedAccess == null) {
certificateBasedAccess = CertificateBasedAccess.createWithSystemEnv();
}
if (certificateBasedAccess.useMtlsClientCertificate()) {
if (mtlsProvider == null) {
// Attempt to create default MtlsProvider from environment.
try {
mtlsProvider = DefaultMtlsProviderFactory.create();
} catch (CertificateSourceUnavailableException e) {
// This is okay. Leave mtlsProvider as null so that we will not auto-upgrade
// to mTLS endpoints. See https://google.aip.dev/auth/4114.
} catch (IOException e) {
LOG.log(
Level.WARNING,
"DefaultMtlsProviderFactory encountered unexpected IOException: " + e.getMessage());
}
}
}
Comment on lines +1288 to +1305
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qq, I noticed that this rough logic also exists here and in the EndpointContext. What's the reason that it needs to exist in the gRPC Channel Provider as well as EndpointContext?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good question. The issue is that EndpointContext is responsible for "endpoint resolution", which takes a dependency on the availability of mTLS (i.e. use mTLS endpoint only if mTLS support is available) - see the complicated "determineEndpoint" function. In the other 2 locations (gRPC/HTTP channel provider), they are used for configuring the TLS settings of the Channel themselves.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the other 2 locations (gRPC/HTTP channel provider), they are used for configuring the TLS settings of the Channel themselves.

Sorry, can you elaborate on this point? I wasn't aware there was settings that MtlsProvider itself was configuring anything. Can you link me to how it's configure TLS settings on the channel?

My assumption was that it was only using the MtlsProvider logic (i.e. checking for env var) to see if Mtls was to be enabled and not touching anything on the channel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me clarify, for gRPC/HTTP channel provider, the "mtlsKeyStore" is used for creating the mTLS-enabled transport as seen in the code snippet below:

HttpTransport createHttpTransport() throws IOException, GeneralSecurityException {
if (certificateBasedAccess == null || mtlsProvider == null) {
return null;
}
if (certificateBasedAccess.useMtlsClientCertificate()) {
KeyStore mtlsKeyStore = mtlsProvider.getKeyStore();
if (mtlsKeyStore != null) {
return new NetHttpTransport.Builder().trustCertificates(null, mtlsKeyStore, "").build();
}
}
return null;
}

The channel providers have no reference to the EndpointContext, so need to independently calculate and bootstrap the mTLS provider.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for the explanation.

In this case, I think what we have previously configured is probably a mistake (i.e. configuring multiple separate MtlsProviders). I think realistically the flow should be

  1. EndpointContext creates the MtlsProvider and CertificateBasedAccess class that is used to compute the endpoint
  2. gRPC and HttpJson channel providers get the MtlsProvider and CertificateBasedAccess classes that were created in the endpointcontext

However, I think in order for that to be done, we'll need to create public methods inside TransportChannelProvider to allow access to them. It is possible with adding InternalApi and we have done previously to access mtlsEndpoint.

Let me think about the options a bit more. I think what you have is based on the existing code and should work, but I think it previously wasn't configured the best/ correctly and would love to try and fix it if possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @andyrzhao, taking a look at this and I don't think there is a clear best way to do this.

Since it looks like these implementations (EndpointContext, GrpcChannelProvider, HttpJsonChannelProvider) are private implementations and not accessible to customers, I think I'm fine with keep it as-is. Adding public methods to clean this up can be done in a future time and perhaps a better implementation can also be found. The new implementation follows the old one and I don't think this would be a regression.

Would you mind creating an issue in our repo to track that future enhancement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Lawerence! Created #3872


if (isMtlsS2AHardBoundTokensEnabled()) {
// Set a {@code ComputeEngineCredentials} instance to be per-RPC call credentials,
// which will be used to fetch MTLS_S2A hard bound tokens from the metdata server.
Expand Down
Loading
Loading