Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable DirectPath bound token in InstantiatingGrpcChannelProvider #3572

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions gax-java/dependencies.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ version.io_grpc=1.69.0
# 2) Replace all characters which are neither alphabetic nor digits with the underscore ('_') character
maven.com_google_api_grpc_proto_google_common_protos=com.google.api.grpc:proto-google-common-protos:2.50.0
maven.com_google_api_grpc_grpc_google_common_protos=com.google.api.grpc:grpc-google-common-protos:2.50.0
maven.com_google_auth_google_auth_library_oauth2_http=com.google.auth:google-auth-library-oauth2-http:1.30.1
maven.com_google_auth_google_auth_library_credentials=com.google.auth:google-auth-library-credentials:1.30.1
maven.com_google_auth_google_auth_library_oauth2_http=com.google.auth:google-auth-library-oauth2-http:1.30.2-SNAPSHOT
maven.com_google_auth_google_auth_library_credentials=com.google.auth:google-auth-library-credentials:1.30.2-SNAPSHOT
maven.io_opentelemetry_opentelemetry_api=io.opentelemetry:opentelemetry-api:1.45.0
maven.io_opencensus_opencensus_api=io.opencensus:opencensus-api:0.31.1
maven.io_opencensus_opencensus_contrib_grpc_metrics=io.opencensus:opencensus-contrib-grpc-metrics:0.31.1
Expand Down
2 changes: 2 additions & 0 deletions gax-java/gax-grpc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-credentials</artifactId>
<version>1.30.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
Expand All @@ -50,6 +51,7 @@
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.30.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,32 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP
@Nullable private final Boolean allowNonDefaultServiceAccount;
@VisibleForTesting final ImmutableMap<String, ?> directPathServiceConfig;
@Nullable private final MtlsProvider mtlsProvider;
@Nullable private final List<HardBoundTokenTypes> allowedHardBoundTokenTypes;
@VisibleForTesting final Map<String, String> headersWithDuplicatesRemoved = new HashMap<>();

@Nullable
private final ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> channelConfigurator;

/*
* Experimental feature
*
* <p>{@link HardBoundTokenTypes} specifies if hard bound tokens should be used if DirectPath
* or S2A is used to estabilsh a connection to Google APIs.
*
*/
public enum HardBoundTokenTypes {
// Use ALTS bound tokens when using DirectPath
ALTS,
// Use MTLS bound tokens when using S2A
MTLS_S2A
}

private InstantiatingGrpcChannelProvider(Builder builder) {
this.processorCount = builder.processorCount;
this.executor = builder.executor;
this.headerProvider = builder.headerProvider;
this.endpoint = builder.endpoint;
this.allowedHardBoundTokenTypes = builder.allowedHardBoundTokenTypes;
this.mtlsProvider = builder.mtlsProvider;
this.envProvider = builder.envProvider;
this.interceptorProvider = builder.interceptorProvider;
Expand Down Expand Up @@ -319,16 +335,19 @@ private void logDirectPathMisconfig() {
Level.WARNING,
"Env var "
+ DIRECT_PATH_ENV_ENABLE_XDS
+ " was found and set to TRUE, but DirectPath was not enabled for this client. If this is intended for "
+ "this client, please note that this is a misconfiguration and set the attemptDirectPath option as well.");
+ " was found and set to TRUE, but DirectPath was not enabled for this client. If"
+ " this is intended for this client, please note that this is a misconfiguration"
+ " and set the attemptDirectPath option as well.");
}
// Case 2: Direct Path xDS was enabled via Builder. Direct Path Traffic Director must be set
// (enabled with `setAttemptDirectPath(true)`) along with xDS.
// Here we warn the user about this.
else if (isDirectPathXdsEnabledViaBuilderOption()) {
LOG.log(
Level.WARNING,
"DirectPath is misconfigured. The DirectPath XDS option was set, but the attemptDirectPath option was not. Please set both the attemptDirectPath and attemptDirectPathXds options.");
"DirectPath is misconfigured. The DirectPath XDS option was set, but the"
+ " attemptDirectPath option was not. Please set both the attemptDirectPath and"
+ " attemptDirectPathXds options.");
}
} else {
// Case 3: credential is not correctly set
Expand Down Expand Up @@ -361,6 +380,18 @@ boolean isCredentialDirectPathCompatible() {
return credentials instanceof ComputeEngineCredentials;
}

@VisibleForTesting
boolean isDirectPathBoundTokenEnabled() {
if (allowedHardBoundTokenTypes == null || !(credentials instanceof ComputeEngineCredentials))
return false;
for (HardBoundTokenTypes boundTokenTypes : allowedHardBoundTokenTypes) {
if (boundTokenTypes == HardBoundTokenTypes.ALTS) {
return true;
}
}
return false;
}

// DirectPath should only be used on Compute Engine.
// Notice Windows is supported for now.
@VisibleForTesting
Expand Down Expand Up @@ -429,9 +460,26 @@ private ManagedChannel createSingleChannel() throws IOException {
// Check DirectPath traffic.
boolean useDirectPathXds = false;
if (canUseDirectPath()) {
CallCredentials altsCallCreds = null;
if (isDirectPathBoundTokenEnabled()) {
ComputeEngineCredentials.Builder credsBuilder =
((ComputeEngineCredentials) credentials).toBuilder();
// We only set scopes and HTTP transport factory from the original credentials because
// only those are used in gRPC CallCredentials to fetch request metadata.
altsCallCreds =
MoreCallCredentials.from(
ComputeEngineCredentials.newBuilder()
.setScopes(credsBuilder.getScopes())
.setHttpTransportFactory(credsBuilder.getHttpTransportFactory())
.setGoogleAuthTransport(ComputeEngineCredentials.GoogleAuthTransport.ALTS)
.build());
}
CallCredentials callCreds = MoreCallCredentials.from(credentials);
ChannelCredentials channelCreds =
GoogleDefaultChannelCredentials.newBuilder().callCredentials(callCreds).build();
GoogleDefaultChannelCredentials.newBuilder()
.callCredentials(callCreds)
.altsCallCredentials(altsCallCreds)
.build();
useDirectPathXds = isDirectPathXdsEnabled();
if (useDirectPathXds) {
// google-c2p: CloudToProd(C2P) Directpath. This scheme is defined in
Expand Down Expand Up @@ -620,6 +668,7 @@ public static final class Builder {
@Nullable private Boolean attemptDirectPathXds;
@Nullable private Boolean allowNonDefaultServiceAccount;
@Nullable private ImmutableMap<String, ?> directPathServiceConfig;
@Nullable private List<HardBoundTokenTypes> allowedHardBoundTokenTypes;

private Builder() {
processorCount = Runtime.getRuntime().availableProcessors();
Expand Down Expand Up @@ -700,6 +749,30 @@ public Builder setEndpoint(String endpoint) {
return this;
}

/*
* Sets the allowed hard bound token types for this TransportChannelProvider.
*
* <p>This is optional; if it is not provided, bearer tokens will be used.
*
* <p>Examples:
*
* <p>allowedValues is {HardBoundTokenTypes.ALTS}: If DirectPath is used to create the channel,
* use hard ALTS-bound tokens for requests sent on that channel.
*
* <p>allowedValues is {HardBoundTokenTypes.MTLS_S2A}: If MTLS via S2A is used to create the
* channel, use hard MTLS-bound tokens for requests sent on that channel.
*
* <p>allowedValues is {HardBoundTokenTypes.ALTS, HardBoundTokenTypes.MTLS_S2A}: if DirectPath
* is used to create the channel, use hard ALTS-bound tokens for requests sent on that channel.
* If MTLS via S2A is used to create the channel, use hard MTLS-bound tokens for requests sent
* on that channel.
*/
@InternalApi
public Builder setAllowHardBoundTokenTypes(List<HardBoundTokenTypes> allowedValues) {
this.allowedHardBoundTokenTypes = allowedValues;
return this;
}

@VisibleForTesting
Builder setMtlsProvider(MtlsProvider mtlsProvider) {
this.mtlsProvider = mtlsProvider;
Expand Down Expand Up @@ -755,6 +828,7 @@ public Integer getMaxInboundMetadataSize() {
public Builder setKeepAliveTime(org.threeten.bp.Duration duration) {
return setKeepAliveTimeDuration(toJavaTimeDuration(duration));
}

/** The time without read activity before sending a keepalive ping. */
public Builder setKeepAliveTimeDuration(java.time.Duration duration) {
this.keepAliveTime = duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

import com.google.api.core.ApiFunction;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.Builder;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.HardBoundTokenTypes;
import com.google.api.gax.rpc.FixedHeaderProvider;
import com.google.api.gax.rpc.HeaderProvider;
import com.google.api.gax.rpc.TransportChannel;
Expand Down Expand Up @@ -626,7 +627,9 @@ private void createAndCloseTransportChannel(InstantiatingGrpcChannelProvider pro
createAndCloseTransportChannel(provider);
assertThat(logHandler.getAllMessages())
.contains(
"DirectPath is misconfigured. The DirectPath XDS option was set, but the attemptDirectPath option was not. Please set both the attemptDirectPath and attemptDirectPathXds options.");
"DirectPath is misconfigured. The DirectPath XDS option was set, but the"
+ " attemptDirectPath option was not. Please set both the attemptDirectPath and"
+ " attemptDirectPathXds options.");
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
}

Expand All @@ -641,8 +644,10 @@ void testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSe
createAndCloseTransportChannel(provider);
assertThat(logHandler.getAllMessages())
.contains(
"Env var GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS was found and set to TRUE, but DirectPath was not enabled for this client. If this is intended for "
+ "this client, please note that this is a misconfiguration and set the attemptDirectPath option as well.");
"Env var GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS was found and set to TRUE, but DirectPath"
+ " was not enabled for this client. If this is intended for this client, please"
+ " note that this is a misconfiguration and set the attemptDirectPath option as"
+ " well.");
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
}

Expand Down Expand Up @@ -729,6 +734,37 @@ public void canUseDirectPath_happyPath() throws IOException {
InstantiatingGrpcChannelProvider provider =
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
Truth.assertThat(provider.canUseDirectPath()).isTrue();
Truth.assertThat(provider.isDirectPathBoundTokenEnabled()).isFalse();

// verify this info is passed correctly to transport channel
TransportChannel transportChannel = provider.getTransportChannel();
Truth.assertThat(((GrpcTransportChannel) transportChannel).isDirectPath()).isTrue();
transportChannel.shutdownNow();
}

@Test
public void canUseDirectPath_happyPathWithBoundToken() throws IOException {
System.setProperty("os.name", "Linux");
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
Mockito.when(
envProvider.getenv(
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
.thenReturn("false");
// verifies the credentials gets called and returns a non-null builder.
Mockito.when(computeEngineCredentials.toBuilder())
.thenReturn(ComputeEngineCredentials.newBuilder());
InstantiatingGrpcChannelProvider.Builder builder =
InstantiatingGrpcChannelProvider.newBuilder()
.setAttemptDirectPath(true)
.setCredentials(computeEngineCredentials)
.setAllowHardBoundTokenTypes(Collections.singletonList(HardBoundTokenTypes.ALTS))
.setEndpoint(DEFAULT_ENDPOINT)
.setEnvProvider(envProvider)
.setHeaderProvider(Mockito.mock(HeaderProvider.class));
InstantiatingGrpcChannelProvider provider =
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
Truth.assertThat(provider.canUseDirectPath()).isTrue();
Truth.assertThat(provider.isDirectPathBoundTokenEnabled()).isTrue();

// verify this info is passed correctly to transport channel
TransportChannel transportChannel = provider.getTransportChannel();
Expand Down