mirror of https://github.com/grpc/grpc-java.git
alts: Expose ChannelCredentials for the various negotiators
AltsChannelBuilder could be improved a bit more by removing the call to InternalNettyChannelBuilder.setProtocolNegotiatorFactory. However, to do that cleanest would require reworking how port is plumbed in NettyChannelBuilder and potentially AbstractManagedChannelImplBuilder to move getDefaultPort() to ProtocolNegotiator from ClientFactory. Saving that for another day.
This commit is contained in:
parent
e595779047
commit
5a687e3da8
|
@ -17,26 +17,14 @@
|
|||
package io.grpc.alts;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.ExperimentalApi;
|
||||
import io.grpc.ForwardingChannelBuilder;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.alts.internal.AltsProtocolNegotiator.ClientAltsProtocolNegotiatorFactory;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
import io.grpc.internal.ObjectPool;
|
||||
import io.grpc.internal.SharedResourcePool;
|
||||
import io.grpc.netty.InternalNettyChannelBuilder;
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import io.grpc.netty.NettyChannelBuilder;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -45,14 +33,9 @@ import javax.annotation.Nullable;
|
|||
*/
|
||||
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4151")
|
||||
public final class AltsChannelBuilder extends ForwardingChannelBuilder<AltsChannelBuilder> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AltsChannelBuilder.class.getName());
|
||||
private final NettyChannelBuilder delegate;
|
||||
private final ImmutableList.Builder<String> targetServiceAccountsBuilder =
|
||||
ImmutableList.builder();
|
||||
private ObjectPool<Channel> handshakerChannelPool =
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL);
|
||||
private boolean enableUntrustedAlts;
|
||||
private final AltsChannelCredentials.Builder credentialsBuilder =
|
||||
new AltsChannelCredentials.Builder();
|
||||
|
||||
/** "Overrides" the static method in {@link ManagedChannelBuilder}. */
|
||||
public static final AltsChannelBuilder forTarget(String target) {
|
||||
|
@ -73,7 +56,7 @@ public final class AltsChannelBuilder extends ForwardingChannelBuilder<AltsChann
|
|||
* service account in the handshaker result. Otherwise, the handshake fails.
|
||||
*/
|
||||
public AltsChannelBuilder addTargetServiceAccount(String targetServiceAccount) {
|
||||
targetServiceAccountsBuilder.add(targetServiceAccount);
|
||||
credentialsBuilder.addTargetServiceAccount(targetServiceAccount);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -82,17 +65,13 @@ public final class AltsChannelBuilder extends ForwardingChannelBuilder<AltsChann
|
|||
* is running on Google Cloud Platform.
|
||||
*/
|
||||
public AltsChannelBuilder enableUntrustedAltsForTesting() {
|
||||
enableUntrustedAlts = true;
|
||||
credentialsBuilder.enableUntrustedAltsForTesting();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets a new handshaker service address for testing. */
|
||||
public AltsChannelBuilder setHandshakerAddressForTesting(String handshakerAddress) {
|
||||
// Instead of using the default shared channel to the handshaker service, create a separate
|
||||
// resource to the test address.
|
||||
handshakerChannelPool =
|
||||
SharedResourcePool.forResource(
|
||||
HandshakerServiceChannel.getHandshakerChannelForTesting(handshakerAddress));
|
||||
credentialsBuilder.setHandshakerAddressForTesting(handshakerAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -103,22 +82,9 @@ public final class AltsChannelBuilder extends ForwardingChannelBuilder<AltsChann
|
|||
|
||||
@Override
|
||||
public ManagedChannel build() {
|
||||
if (!CheckGcpEnvironment.isOnGcp()) {
|
||||
if (enableUntrustedAlts) {
|
||||
logger.log(
|
||||
Level.WARNING,
|
||||
"Untrusted ALTS mode is enabled and we cannot guarantee the trustworthiness of the "
|
||||
+ "ALTS handshaker service");
|
||||
} else {
|
||||
Status status =
|
||||
Status.INTERNAL.withDescription("ALTS is only allowed to run on Google Cloud Platform");
|
||||
delegate().intercept(new FailingClientInterceptor(status));
|
||||
}
|
||||
}
|
||||
InternalNettyChannelBuilder.setProtocolNegotiatorFactory(
|
||||
delegate(),
|
||||
new ClientAltsProtocolNegotiatorFactory(
|
||||
targetServiceAccountsBuilder.build(), handshakerChannelPool));
|
||||
credentialsBuilder.buildProtocolNegotiatorFactory());
|
||||
|
||||
return delegate().build();
|
||||
}
|
||||
|
@ -126,24 +92,6 @@ public final class AltsChannelBuilder extends ForwardingChannelBuilder<AltsChann
|
|||
@VisibleForTesting
|
||||
@Nullable
|
||||
ProtocolNegotiator getProtocolNegotiatorForTest() {
|
||||
return new ClientAltsProtocolNegotiatorFactory(
|
||||
targetServiceAccountsBuilder.build(), handshakerChannelPool)
|
||||
.buildProtocolNegotiator();
|
||||
}
|
||||
|
||||
/** An implementation of {@link ClientInterceptor} that fails each call. */
|
||||
static final class FailingClientInterceptor implements ClientInterceptor {
|
||||
|
||||
private final Status status;
|
||||
|
||||
public FailingClientInterceptor(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
|
||||
return new FailingClientCall<>(status);
|
||||
}
|
||||
return credentialsBuilder.buildProtocolNegotiatorFactory().newNegotiator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.ExperimentalApi;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.alts.internal.AltsProtocolNegotiator.ClientAltsProtocolNegotiatorFactory;
|
||||
import io.grpc.internal.ObjectPool;
|
||||
import io.grpc.internal.SharedResourcePool;
|
||||
import io.grpc.netty.GrpcHttp2ConnectionHandler;
|
||||
import io.grpc.netty.InternalNettyChannelCredentials;
|
||||
import io.grpc.netty.InternalProtocolNegotiator;
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerAdapter;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.AsciiString;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Provides secure and authenticated commmunication between two cloud VMs using ALTS.
|
||||
*/
|
||||
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4151")
|
||||
public final class AltsChannelCredentials {
|
||||
private static final Logger logger = Logger.getLogger(AltsChannelCredentials.class.getName());
|
||||
|
||||
private AltsChannelCredentials() {}
|
||||
|
||||
public static ChannelCredentials create() {
|
||||
return newBuilder().build();
|
||||
}
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final ImmutableList.Builder<String> targetServiceAccountsBuilder =
|
||||
ImmutableList.builder();
|
||||
private ObjectPool<Channel> handshakerChannelPool =
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL);
|
||||
private boolean enableUntrustedAlts;
|
||||
|
||||
/**
|
||||
* Adds an expected target service accounts. One of the added service accounts should match peer
|
||||
* service account in the handshaker result. Otherwise, the handshake fails.
|
||||
*/
|
||||
public Builder addTargetServiceAccount(String targetServiceAccount) {
|
||||
targetServiceAccountsBuilder.add(targetServiceAccount);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables untrusted ALTS for testing. If this function is called, we will not check whether
|
||||
* ALTS is running on Google Cloud Platform.
|
||||
*/
|
||||
public Builder enableUntrustedAltsForTesting() {
|
||||
enableUntrustedAlts = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets a new handshaker service address for testing. */
|
||||
public Builder setHandshakerAddressForTesting(String handshakerAddress) {
|
||||
// Instead of using the default shared channel to the handshaker service, create a separate
|
||||
// resource to the test address.
|
||||
handshakerChannelPool =
|
||||
SharedResourcePool.forResource(
|
||||
HandshakerServiceChannel.getHandshakerChannelForTesting(handshakerAddress));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChannelCredentials build() {
|
||||
return InternalNettyChannelCredentials.create(buildProtocolNegotiatorFactory());
|
||||
}
|
||||
|
||||
InternalProtocolNegotiator.ClientFactory buildProtocolNegotiatorFactory() {
|
||||
if (!CheckGcpEnvironment.isOnGcp()) {
|
||||
if (enableUntrustedAlts) {
|
||||
logger.log(
|
||||
Level.WARNING,
|
||||
"Untrusted ALTS mode is enabled and we cannot guarantee the trustworthiness of the "
|
||||
+ "ALTS handshaker service");
|
||||
} else {
|
||||
Status status = Status.INTERNAL.withDescription(
|
||||
"ALTS is only allowed to run on Google Cloud Platform");
|
||||
return new FailingProtocolNegotiatorFactory(status);
|
||||
}
|
||||
}
|
||||
|
||||
return new ClientAltsProtocolNegotiatorFactory(
|
||||
targetServiceAccountsBuilder.build(), handshakerChannelPool);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FailingProtocolNegotiatorFactory
|
||||
implements InternalProtocolNegotiator.ClientFactory {
|
||||
private final Status status;
|
||||
|
||||
public FailingProtocolNegotiatorFactory(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolNegotiator newNegotiator() {
|
||||
return new FailingProtocolNegotiator(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return 443;
|
||||
}
|
||||
}
|
||||
|
||||
private static final AsciiString SCHEME = AsciiString.of("https");
|
||||
|
||||
private static final class FailingProtocolNegotiator implements ProtocolNegotiator {
|
||||
private final Status status;
|
||||
|
||||
public FailingProtocolNegotiator(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsciiString scheme() {
|
||||
return SCHEME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
|
||||
return new ChannelHandlerAdapter() {
|
||||
@Override public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
ctx.fireExceptionCaught(status.asRuntimeException());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.CallOptions;
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.ClientCall;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.MethodDescriptor;
|
||||
import io.grpc.Status;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** An implementation of {@link ClientInterceptor} that adds call credentials on each call. */
|
||||
final class CallCredentialsInterceptor implements ClientInterceptor {
|
||||
|
||||
@Nullable private final CallCredentials credentials;
|
||||
private final Status status;
|
||||
|
||||
public CallCredentialsInterceptor(@Nullable CallCredentials credentials, Status status) {
|
||||
this.credentials = credentials;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
|
||||
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
|
||||
if (!status.isOk()) {
|
||||
return new FailingClientCall<>(status);
|
||||
}
|
||||
return next.newCall(method, callOptions.withCallCredentials(credentials));
|
||||
}
|
||||
}
|
|
@ -16,23 +16,10 @@
|
|||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.auth.oauth2.ComputeEngineCredentials;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ForwardingChannelBuilder;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
|
||||
import io.grpc.auth.MoreCallCredentials;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
import io.grpc.internal.SharedResourcePool;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.InternalNettyChannelBuilder;
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import io.grpc.netty.NettyChannelBuilder;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* {@code ManagedChannelBuilder} for Google Compute Engine. This class sets up a secure channel
|
||||
|
@ -44,27 +31,7 @@ public final class ComputeEngineChannelBuilder
|
|||
private final NettyChannelBuilder delegate;
|
||||
|
||||
private ComputeEngineChannelBuilder(String target) {
|
||||
delegate = NettyChannelBuilder.forTarget(target);
|
||||
SslContext sslContext;
|
||||
try {
|
||||
sslContext = GrpcSslContexts.forClient().build();
|
||||
} catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
InternalNettyChannelBuilder.setProtocolNegotiatorFactory(
|
||||
delegate(),
|
||||
new GoogleDefaultProtocolNegotiatorFactory(
|
||||
/* targetServiceAccounts= */ ImmutableList.<String>of(),
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
|
||||
sslContext));
|
||||
CallCredentials credentials = MoreCallCredentials.from(ComputeEngineCredentials.create());
|
||||
Status status = Status.OK;
|
||||
if (!CheckGcpEnvironment.isOnGcp()) {
|
||||
status =
|
||||
Status.INTERNAL.withDescription(
|
||||
"Compute Engine Credentials can only be used on Google Cloud Platform");
|
||||
}
|
||||
delegate().intercept(new CallCredentialsInterceptor(credentials, status));
|
||||
delegate = NettyChannelBuilder.forTarget(target, ComputeEngineChannelCredentials.create());
|
||||
}
|
||||
|
||||
/** "Overrides" the static method in {@link ManagedChannelBuilder}. */
|
||||
|
@ -81,19 +48,4 @@ public final class ComputeEngineChannelBuilder
|
|||
protected NettyChannelBuilder delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ProtocolNegotiator getProtocolNegotiatorForTest() {
|
||||
SslContext sslContext;
|
||||
try {
|
||||
sslContext = GrpcSslContexts.forClient().build();
|
||||
} catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new GoogleDefaultProtocolNegotiatorFactory(
|
||||
/* targetServiceAccounts= */ ImmutableList.<String>of(),
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
|
||||
sslContext)
|
||||
.buildProtocolNegotiator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.auth.oauth2.ComputeEngineCredentials;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.CompositeChannelCredentials;
|
||||
import io.grpc.ExperimentalApi;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
|
||||
import io.grpc.auth.MoreCallCredentials;
|
||||
import io.grpc.internal.SharedResourcePool;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.InternalNettyChannelCredentials;
|
||||
import io.grpc.netty.InternalProtocolNegotiator;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* Credentials appropriate to contact Google services when running on Google Compute Engine. This
|
||||
* class sets up a secure channel using ALTS if applicable and using TLS as fallback. It is a subset
|
||||
* of the functionality provided by {@link GoogleDefaultChannelCredentials}.
|
||||
*/
|
||||
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||
public final class ComputeEngineChannelCredentials {
|
||||
private ComputeEngineChannelCredentials() {}
|
||||
|
||||
/**
|
||||
* Creates credentials for Google Compute Engine. This class sets up a secure channel using ALTS
|
||||
* if applicable and using TLS as fallback.
|
||||
*/
|
||||
public static ChannelCredentials create() {
|
||||
ChannelCredentials nettyCredentials =
|
||||
InternalNettyChannelCredentials.create(createClientFactory());
|
||||
CallCredentials callCredentials;
|
||||
if (CheckGcpEnvironment.isOnGcp()) {
|
||||
callCredentials = MoreCallCredentials.from(ComputeEngineCredentials.create());
|
||||
} else {
|
||||
callCredentials = new FailingCallCredentials(
|
||||
Status.INTERNAL.withDescription(
|
||||
"Compute Engine Credentials can only be used on Google Cloud Platform"));
|
||||
}
|
||||
return CompositeChannelCredentials.create(nettyCredentials, callCredentials);
|
||||
}
|
||||
|
||||
private static InternalProtocolNegotiator.ClientFactory createClientFactory() {
|
||||
SslContext sslContext;
|
||||
try {
|
||||
sslContext = GrpcSslContexts.forClient().build();
|
||||
} catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new GoogleDefaultProtocolNegotiatorFactory(
|
||||
/* targetServiceAccounts= */ ImmutableList.<String>of(),
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
|
||||
sslContext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.Status;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* {@code CallCredentials} that always fail the RPC.
|
||||
*/
|
||||
final class FailingCallCredentials extends CallCredentials {
|
||||
private final Status status;
|
||||
|
||||
public FailingCallCredentials(Status status) {
|
||||
this.status = Preconditions.checkNotNull(status, "status");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyRequestMetadata(
|
||||
CallCredentials.RequestInfo requestInfo,
|
||||
Executor appExecutor,
|
||||
CallCredentials.MetadataApplier applier) {
|
||||
applier.fail(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thisUsesUnstableApi() {}
|
||||
}
|
|
@ -16,25 +16,10 @@
|
|||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ForwardingChannelBuilder;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
|
||||
import io.grpc.auth.MoreCallCredentials;
|
||||
import io.grpc.internal.GrpcUtil;
|
||||
import io.grpc.internal.SharedResourcePool;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.InternalNettyChannelBuilder;
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import io.grpc.netty.NettyChannelBuilder;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import java.io.IOException;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* Google default version of {@code ManagedChannelBuilder}. This class sets up a secure channel
|
||||
|
@ -46,30 +31,7 @@ public final class GoogleDefaultChannelBuilder
|
|||
private final NettyChannelBuilder delegate;
|
||||
|
||||
private GoogleDefaultChannelBuilder(String target) {
|
||||
delegate = NettyChannelBuilder.forTarget(target);
|
||||
SslContext sslContext;
|
||||
try {
|
||||
sslContext = GrpcSslContexts.forClient().build();
|
||||
} catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
InternalNettyChannelBuilder.setProtocolNegotiatorFactory(
|
||||
delegate(),
|
||||
new GoogleDefaultProtocolNegotiatorFactory(
|
||||
/* targetServiceAccounts= */ ImmutableList.<String>of(),
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
|
||||
sslContext));
|
||||
@Nullable CallCredentials credentials = null;
|
||||
Status status = Status.OK;
|
||||
try {
|
||||
credentials = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault());
|
||||
} catch (IOException e) {
|
||||
status =
|
||||
Status.UNAUTHENTICATED
|
||||
.withDescription("Failed to get Google default credentials")
|
||||
.withCause(e);
|
||||
}
|
||||
delegate().intercept(new CallCredentialsInterceptor(credentials, status));
|
||||
delegate = NettyChannelBuilder.forTarget(target, GoogleDefaultChannelCredentials.create());
|
||||
}
|
||||
|
||||
/** "Overrides" the static method in {@link ManagedChannelBuilder}. */
|
||||
|
@ -86,19 +48,4 @@ public final class GoogleDefaultChannelBuilder
|
|||
protected NettyChannelBuilder delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ProtocolNegotiator getProtocolNegotiatorForTest() {
|
||||
SslContext sslContext;
|
||||
try {
|
||||
sslContext = GrpcSslContexts.forClient().build();
|
||||
} catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new GoogleDefaultProtocolNegotiatorFactory(
|
||||
/* targetServiceAccounts= */ ImmutableList.<String>of(),
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
|
||||
sslContext)
|
||||
.buildProtocolNegotiator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2020 The gRPC Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.grpc.alts;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ChannelCredentials;
|
||||
import io.grpc.CompositeChannelCredentials;
|
||||
import io.grpc.ExperimentalApi;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.alts.internal.AltsProtocolNegotiator.GoogleDefaultProtocolNegotiatorFactory;
|
||||
import io.grpc.auth.MoreCallCredentials;
|
||||
import io.grpc.internal.SharedResourcePool;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.InternalNettyChannelCredentials;
|
||||
import io.grpc.netty.InternalProtocolNegotiator;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import java.io.IOException;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* Credentials appropriate to contact Google services. This class sets up a secure channel using
|
||||
* ALTS if applicable and uses TLS as fallback.
|
||||
*/
|
||||
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||
public final class GoogleDefaultChannelCredentials {
|
||||
private GoogleDefaultChannelCredentials() {}
|
||||
|
||||
/**
|
||||
* Creates Google default credentials uses a secure channel with ALTS if applicable and uses TLS
|
||||
* as fallback.
|
||||
*/
|
||||
public static ChannelCredentials create() {
|
||||
ChannelCredentials nettyCredentials =
|
||||
InternalNettyChannelCredentials.create(createClientFactory());
|
||||
CallCredentials callCredentials;
|
||||
try {
|
||||
callCredentials = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault());
|
||||
} catch (IOException e) {
|
||||
// TODO(ejona): Should this just throw?
|
||||
callCredentials = new FailingCallCredentials(
|
||||
Status.UNAUTHENTICATED
|
||||
.withDescription("Failed to get Google default credentials")
|
||||
.withCause(e));
|
||||
}
|
||||
return CompositeChannelCredentials.create(nettyCredentials, callCredentials);
|
||||
}
|
||||
|
||||
private static InternalProtocolNegotiator.ClientFactory createClientFactory() {
|
||||
SslContext sslContext;
|
||||
try {
|
||||
sslContext = GrpcSslContexts.forClient().build();
|
||||
} catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new GoogleDefaultProtocolNegotiatorFactory(
|
||||
/* targetServiceAccounts= */ ImmutableList.<String>of(),
|
||||
SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL),
|
||||
sslContext);
|
||||
}
|
||||
}
|
|
@ -32,8 +32,7 @@ import io.grpc.alts.internal.RpcProtocolVersionsUtil.RpcVersionsCheckResult;
|
|||
import io.grpc.grpclb.GrpclbConstants;
|
||||
import io.grpc.internal.ObjectPool;
|
||||
import io.grpc.netty.GrpcHttp2ConnectionHandler;
|
||||
import io.grpc.netty.InternalNettyChannelBuilder;
|
||||
import io.grpc.netty.InternalNettyChannelBuilder.ProtocolNegotiatorFactory;
|
||||
import io.grpc.netty.InternalProtocolNegotiator;
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import io.grpc.netty.InternalProtocolNegotiators;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
@ -65,23 +64,28 @@ public final class AltsProtocolNegotiator {
|
|||
* channel.
|
||||
*/
|
||||
public static final class ClientAltsProtocolNegotiatorFactory
|
||||
implements InternalNettyChannelBuilder.ProtocolNegotiatorFactory {
|
||||
implements InternalProtocolNegotiator.ClientFactory {
|
||||
|
||||
private final ImmutableList<String> targetServiceAccounts;
|
||||
private final LazyChannel lazyHandshakerChannel;
|
||||
private final ObjectPool<Channel> handshakerChannelPool;
|
||||
|
||||
public ClientAltsProtocolNegotiatorFactory(
|
||||
List<String> targetServiceAccounts,
|
||||
ObjectPool<Channel> handshakerChannelPool) {
|
||||
this.targetServiceAccounts = ImmutableList.copyOf(targetServiceAccounts);
|
||||
this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
|
||||
this.handshakerChannelPool = checkNotNull(handshakerChannelPool, "handshakerChannelPool");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolNegotiator buildProtocolNegotiator() {
|
||||
public ProtocolNegotiator newNegotiator() {
|
||||
return new ClientAltsProtocolNegotiator(
|
||||
new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel),
|
||||
lazyHandshakerChannel);
|
||||
targetServiceAccounts,
|
||||
handshakerChannelPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return 443;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,9 +94,10 @@ public final class AltsProtocolNegotiator {
|
|||
private final LazyChannel lazyHandshakerChannel;
|
||||
|
||||
ClientAltsProtocolNegotiator(
|
||||
TsiHandshakerFactory handshakerFactory, LazyChannel lazyHandshakerChannel) {
|
||||
this.handshakerFactory = checkNotNull(handshakerFactory, "handshakerFactory");
|
||||
this.lazyHandshakerChannel = checkNotNull(lazyHandshakerChannel, "lazyHandshakerChannel");
|
||||
ImmutableList<String> targetServiceAccounts, ObjectPool<Channel> handshakerChannelPool) {
|
||||
this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
|
||||
this.handshakerFactory =
|
||||
new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -177,9 +182,9 @@ public final class AltsProtocolNegotiator {
|
|||
* A Protocol Negotiator factory which can switch between ALTS and TLS based on EAG Attrs.
|
||||
*/
|
||||
public static final class GoogleDefaultProtocolNegotiatorFactory
|
||||
implements ProtocolNegotiatorFactory {
|
||||
implements InternalProtocolNegotiator.ClientFactory {
|
||||
private final ImmutableList<String> targetServiceAccounts;
|
||||
private final LazyChannel lazyHandshakerChannel;
|
||||
private final ObjectPool<Channel> handshakerChannelPool;
|
||||
private final SslContext sslContext;
|
||||
|
||||
/**
|
||||
|
@ -191,17 +196,22 @@ public final class AltsProtocolNegotiator {
|
|||
ObjectPool<Channel> handshakerChannelPool,
|
||||
SslContext sslContext) {
|
||||
this.targetServiceAccounts = ImmutableList.copyOf(targetServiceAccounts);
|
||||
this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
|
||||
this.handshakerChannelPool = checkNotNull(handshakerChannelPool, "handshakerChannelPool");
|
||||
this.sslContext = checkNotNull(sslContext, "sslContext");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolNegotiator buildProtocolNegotiator() {
|
||||
public ProtocolNegotiator newNegotiator() {
|
||||
return new GoogleDefaultProtocolNegotiator(
|
||||
new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel),
|
||||
lazyHandshakerChannel,
|
||||
targetServiceAccounts,
|
||||
handshakerChannelPool,
|
||||
sslContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return 443;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GoogleDefaultProtocolNegotiator implements ProtocolNegotiator {
|
||||
|
@ -210,11 +220,12 @@ public final class AltsProtocolNegotiator {
|
|||
private final SslContext sslContext;
|
||||
|
||||
GoogleDefaultProtocolNegotiator(
|
||||
TsiHandshakerFactory handshakerFactory,
|
||||
LazyChannel lazyHandshakerChannel,
|
||||
ImmutableList<String> targetServiceAccounts,
|
||||
ObjectPool<Channel> handshakerChannelPool,
|
||||
SslContext sslContext) {
|
||||
this.handshakerFactory = checkNotNull(handshakerFactory, "handshakerFactory");
|
||||
this.lazyHandshakerChannel = checkNotNull(lazyHandshakerChannel, "lazyHandshakerChannel");
|
||||
this.lazyHandshakerChannel = new LazyChannel(handshakerChannelPool);
|
||||
this.handshakerFactory =
|
||||
new ClientTsiHandshakerFactory(targetServiceAccounts, lazyHandshakerChannel);
|
||||
this.sslContext = checkNotNull(sslContext, "checkNotNull");
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package io.grpc.alts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
@ -30,9 +27,5 @@ public final class ComputeEngineChannelBuilderTest {
|
|||
public void buildsNettyChannel() throws Exception {
|
||||
ComputeEngineChannelBuilder builder = ComputeEngineChannelBuilder.forTarget("localhost:8080");
|
||||
builder.build();
|
||||
|
||||
ProtocolNegotiator protocolNegotiator = builder.getProtocolNegotiatorForTest();
|
||||
assertThat(protocolNegotiator.getClass().getSimpleName())
|
||||
.isEqualTo("GoogleDefaultProtocolNegotiator");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package io.grpc.alts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
@ -30,9 +27,5 @@ public final class GoogleDefaultChannelBuilderTest {
|
|||
public void buildsNettyChannel() throws Exception {
|
||||
GoogleDefaultChannelBuilder builder = GoogleDefaultChannelBuilder.forTarget("localhost:8080");
|
||||
builder.build();
|
||||
|
||||
ProtocolNegotiator protocolNegotiator = builder.getProtocolNegotiatorForTest();
|
||||
assertThat(protocolNegotiator.getClass().getSimpleName())
|
||||
.isEqualTo("GoogleDefaultProtocolNegotiator");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public final class GoogleDefaultProtocolNegotiatorTest {
|
|||
ImmutableList.<String>of(),
|
||||
handshakerChannelPool,
|
||||
sslContext)
|
||||
.buildProtocolNegotiator();
|
||||
.newNegotiator();
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
Loading…
Reference in New Issue