Skip to content

Commit

Permalink
alts: Expose ChannelCredentials for the various negotiators
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ejona86 committed Oct 7, 2020
1 parent e595779 commit 5a687e3
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 245 deletions.
66 changes: 7 additions & 59 deletions alts/src/main/java/io/grpc/alts/AltsChannelBuilder.java
Expand Up @@ -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;

/**
Expand All @@ -45,14 +33,9 @@
*/
@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) {
Expand All @@ -73,7 +56,7 @@ private AltsChannelBuilder(String target) {
* service account in the handshaker result. Otherwise, the handshake fails.
*/
public AltsChannelBuilder addTargetServiceAccount(String targetServiceAccount) {
targetServiceAccountsBuilder.add(targetServiceAccount);
credentialsBuilder.addTargetServiceAccount(targetServiceAccount);
return this;
}

Expand All @@ -82,17 +65,13 @@ public AltsChannelBuilder addTargetServiceAccount(String targetServiceAccount) {
* 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;
}

Expand All @@ -103,47 +82,16 @@ protected NettyChannelBuilder delegate() {

@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();
}

@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();
}
}
158 changes: 158 additions & 0 deletions alts/src/main/java/io/grpc/alts/AltsChannelCredentials.java
@@ -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() {}
}
}
47 changes: 0 additions & 47 deletions alts/src/main/java/io/grpc/alts/CallCredentialsInterceptor.java

This file was deleted.

0 comments on commit 5a687e3

Please sign in to comment.