aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alts/BUILD.bazel1
-rw-r--r--alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java6
-rw-r--r--alts/src/test/java/io/grpc/alts/internal/AltsProtocolNegotiatorTest.java4
-rw-r--r--core/src/main/java/io/grpc/internal/Channelz.java93
-rw-r--r--core/src/test/java/io/grpc/internal/ChannelzTest.java21
-rw-r--r--netty/src/main/java/io/grpc/netty/GrpcHttp2ConnectionHandler.java15
-rw-r--r--netty/src/main/java/io/grpc/netty/NettyClientHandler.java12
-rw-r--r--netty/src/main/java/io/grpc/netty/NettyClientTransport.java28
-rw-r--r--netty/src/main/java/io/grpc/netty/NettyServerHandler.java10
-rw-r--r--netty/src/main/java/io/grpc/netty/NettyServerTransport.java31
-rw-r--r--netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java24
-rw-r--r--netty/src/test/java/io/grpc/netty/NettyServerHandlerTest.java4
-rw-r--r--okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java41
-rw-r--r--services/src/main/java/io/grpc/services/ChannelzProtoUtil.java41
-rw-r--r--services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java62
-rw-r--r--services/src/test/java/io/grpc/services/ChannelzTestHelper.java3
16 files changed, 332 insertions, 64 deletions
diff --git a/alts/BUILD.bazel b/alts/BUILD.bazel
index aaa3e345f..6f0627b5e 100644
--- a/alts/BUILD.bazel
+++ b/alts/BUILD.bazel
@@ -9,6 +9,7 @@ java_library(
":handshaker_java_grpc",
":handshaker_java_proto",
"//core",
+ "//core:internal",
"//netty",
"//stub",
"@com_google_code_findbugs_jsr305//jar",
diff --git a/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java b/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java
index ef49835a5..4c6e5165c 100644
--- a/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java
+++ b/alts/src/main/java/io/grpc/alts/internal/AltsProtocolNegotiator.java
@@ -18,11 +18,14 @@ package io.grpc.alts.internal;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.protobuf.Any;
import io.grpc.Attributes;
import io.grpc.Grpc;
import io.grpc.Status;
import io.grpc.alts.internal.RpcProtocolVersionsUtil.RpcVersionsCheckResult;
import io.grpc.alts.internal.TsiHandshakeHandler.TsiHandshakeCompletionEvent;
+import io.grpc.internal.Channelz.OtherSecurity;
+import io.grpc.internal.Channelz.Security;
import io.grpc.netty.GrpcHttp2ConnectionHandler;
import io.grpc.netty.ProtocolNegotiator;
import io.grpc.netty.ProtocolNegotiators.AbstractBufferingHandler;
@@ -119,7 +122,8 @@ public abstract class AltsProtocolNegotiator implements ProtocolNegotiator {
.set(TSI_PEER_KEY, altsEvt.peer())
.set(ALTS_CONTEXT_KEY, altsContext)
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
- .build());
+ .build(),
+ new Security(new OtherSecurity("alts", Any.pack(altsContext.context))));
}
// Now write any buffered data and remove this handler.
diff --git a/alts/src/test/java/io/grpc/alts/internal/AltsProtocolNegotiatorTest.java b/alts/src/test/java/io/grpc/alts/internal/AltsProtocolNegotiatorTest.java
index f271c4c47..e7ce17fe5 100644
--- a/alts/src/test/java/io/grpc/alts/internal/AltsProtocolNegotiatorTest.java
+++ b/alts/src/test/java/io/grpc/alts/internal/AltsProtocolNegotiatorTest.java
@@ -28,6 +28,7 @@ import io.grpc.Grpc;
import io.grpc.alts.internal.Handshaker.HandshakerResult;
import io.grpc.alts.internal.TsiFrameProtector.Consumer;
import io.grpc.alts.internal.TsiPeer.Property;
+import io.grpc.internal.Channelz;
import io.grpc.netty.GrpcHttp2ConnectionHandler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
@@ -399,7 +400,8 @@ public class AltsProtocolNegotiatorTest {
}
@Override
- public void handleProtocolNegotiationCompleted(Attributes attrs) {
+ public void handleProtocolNegotiationCompleted(
+ Attributes attrs, Channelz.Security securityInfo) {
// If we are added to the pipeline, we need to remove ourselves. The HTTP2 handler
channel.pipeline().remove(this);
this.attrs = attrs;
diff --git a/core/src/main/java/io/grpc/internal/Channelz.java b/core/src/main/java/io/grpc/internal/Channelz.java
index 23414a83d..505130517 100644
--- a/core/src/main/java/io/grpc/internal/Channelz.java
+++ b/core/src/main/java/io/grpc/internal/Channelz.java
@@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import io.grpc.ConnectivityState;
import java.net.SocketAddress;
+import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -30,10 +31,15 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
public final class Channelz {
+ private static final Logger log = Logger.getLogger(Channelz.class.getName());
private static final Channelz INSTANCE = new Channelz();
private final ConcurrentNavigableMap<Long, Instrumented<ServerStats>> servers
@@ -450,21 +456,100 @@ public final class Channelz {
}
public static final class Security {
- // TODO(zpencer): fill this in
+ @Nullable
+ public final Tls tls;
+ @Nullable
+ public final OtherSecurity other;
+
+ public Security(Tls tls) {
+ this.tls = Preconditions.checkNotNull(tls);
+ this.other = null;
+ }
+
+ public Security(OtherSecurity other) {
+ this.tls = null;
+ this.other = Preconditions.checkNotNull(other);
+ }
+ }
+
+ public static final class OtherSecurity {
+ public final String name;
+ @Nullable
+ public final Object any;
+
+ /**
+ * Creates an instance.
+ * @param name the name.
+ * @param any a com.google.protobuf.Any object
+ */
+ public OtherSecurity(String name, @Nullable Object any) {
+ this.name = Preconditions.checkNotNull(name);
+ Preconditions.checkState(
+ any == null || any.getClass().getName().endsWith("com.google.protobuf.Any"),
+ "the 'any' object must be of type com.google.protobuf.Any");
+ this.any = any;
+ }
+ }
+
+ @Immutable
+ public static final class Tls {
+ public final String cipherSuiteStandardName;
+ @Nullable public final Certificate localCert;
+ @Nullable public final Certificate remoteCert;
+
+ /**
+ * A constructor only for testing.
+ */
+ public Tls(String cipherSuiteName, Certificate localCert, Certificate remoteCert) {
+ this.cipherSuiteStandardName = cipherSuiteName;
+ this.localCert = localCert;
+ this.remoteCert = remoteCert;
+ }
+
+ /**
+ * Creates an instance.
+ */
+ public Tls(SSLSession session) {
+ String cipherSuiteStandardName = session.getCipherSuite();
+ Certificate localCert = null;
+ Certificate remoteCert = null;
+ Certificate[] localCerts = session.getLocalCertificates();
+ if (localCerts != null) {
+ localCert = localCerts[0];
+ }
+ try {
+ Certificate[] peerCerts = session.getPeerCertificates();
+ if (peerCerts != null) {
+ // The javadoc of getPeerCertificate states that the peer's own certificate is the first
+ // element of the list.
+ remoteCert = peerCerts[0];
+ }
+ } catch (SSLPeerUnverifiedException e) {
+ // peer cert is not available
+ log.log(
+ Level.FINE,
+ String.format("Peer cert not available for peerHost=%s", session.getPeerHost()),
+ e);
+ }
+ this.cipherSuiteStandardName = cipherSuiteStandardName;
+ this.localCert = localCert;
+ this.remoteCert = remoteCert;
+ }
}
public static final class SocketStats {
@Nullable public final TransportStats data;
- public final SocketAddress local;
+ @Nullable public final SocketAddress local;
@Nullable public final SocketAddress remote;
public final SocketOptions socketOptions;
+ // Can be null if plaintext
@Nullable public final Security security;
/** Creates an instance. */
public SocketStats(
TransportStats data,
- SocketAddress local,
- SocketAddress remote,
+ @Nullable SocketAddress local,
+ @Nullable SocketAddress remote,
SocketOptions socketOptions,
Security security) {
this.data = data;
diff --git a/core/src/test/java/io/grpc/internal/ChannelzTest.java b/core/src/test/java/io/grpc/internal/ChannelzTest.java
index 23b862ec2..ad50c87ac 100644
--- a/core/src/test/java/io/grpc/internal/ChannelzTest.java
+++ b/core/src/test/java/io/grpc/internal/ChannelzTest.java
@@ -19,10 +19,13 @@ package io.grpc.internal;
import static com.google.common.truth.Truth.assertThat;
import static io.grpc.internal.Channelz.id;
import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.internal.Channelz.ChannelStats;
@@ -31,6 +34,9 @@ import io.grpc.internal.Channelz.ServerList;
import io.grpc.internal.Channelz.ServerSocketsList;
import io.grpc.internal.Channelz.ServerStats;
import io.grpc.internal.Channelz.SocketStats;
+import io.grpc.internal.Channelz.Tls;
+import java.security.cert.Certificate;
+import javax.net.ssl.SSLSession;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -272,6 +278,21 @@ public final class ChannelzTest {
assertThat(list2.sockets).containsExactly(socket2);
}
+ @Test
+ public void tlsSecurityInfo() throws Exception {
+ Certificate local = io.grpc.internal.testing.TestUtils.loadX509Cert("client.pem");
+ Certificate remote = io.grpc.internal.testing.TestUtils.loadX509Cert("server0.pem");
+ final SSLSession session = mock(SSLSession.class);
+ when(session.getCipherSuite()).thenReturn("TLS_NULL_WITH_NULL_NULL");
+ when(session.getLocalCertificates()).thenReturn(new Certificate[]{local});
+ when(session.getPeerCertificates()).thenReturn(new Certificate[]{remote});
+
+ Tls tls = new Tls(session);
+ assertEquals(local, tls.localCert);
+ assertEquals(remote, tls.remoteCert);
+ assertEquals("TLS_NULL_WITH_NULL_NULL", tls.cipherSuiteStandardName);
+ }
+
private void assertEmptyServerSocketsPage(long serverId, long socketId) {
ServerSocketsList emptyPage
= channelz.getServerSockets(serverId, socketId, /*maxPageSize=*/ 1);
diff --git a/netty/src/main/java/io/grpc/netty/GrpcHttp2ConnectionHandler.java b/netty/src/main/java/io/grpc/netty/GrpcHttp2ConnectionHandler.java
index 7fedf3327..5163e76e5 100644
--- a/netty/src/main/java/io/grpc/netty/GrpcHttp2ConnectionHandler.java
+++ b/netty/src/main/java/io/grpc/netty/GrpcHttp2ConnectionHandler.java
@@ -18,6 +18,7 @@ package io.grpc.netty;
import io.grpc.Attributes;
import io.grpc.Internal;
+import io.grpc.internal.Channelz;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
@@ -44,14 +45,26 @@ public abstract class GrpcHttp2ConnectionHandler extends Http2ConnectionHandler
}
/**
+ * Same as {@link #handleProtocolNegotiationCompleted(Attributes, Channelz.Security)}
+ * but with no {@link Channelz.Security}.
+ *
+ * @deprecated Use the two argument method instead.
+ */
+ @Deprecated
+ public void handleProtocolNegotiationCompleted(Attributes attrs) {
+ handleProtocolNegotiationCompleted(attrs, /*securityInfo=*/ null);
+ }
+
+ /**
* Triggered on protocol negotiation completion.
*
* <p>It must me called after negotiation is completed but before given handler is added to the
* channel.
*
* @param attrs arbitrary attributes passed after protocol negotiation (eg. SSLSession).
+ * @param securityInfo informs channelz about the security protocol.
*/
- public void handleProtocolNegotiationCompleted(Attributes attrs) {
+ public void handleProtocolNegotiationCompleted(Attributes attrs, Channelz.Security securityInfo) {
}
/**
diff --git a/netty/src/main/java/io/grpc/netty/NettyClientHandler.java b/netty/src/main/java/io/grpc/netty/NettyClientHandler.java
index fd16746b4..5f0d37354 100644
--- a/netty/src/main/java/io/grpc/netty/NettyClientHandler.java
+++ b/netty/src/main/java/io/grpc/netty/NettyClientHandler.java
@@ -27,6 +27,7 @@ import io.grpc.Attributes;
import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.StatusException;
+import io.grpc.internal.Channelz;
import io.grpc.internal.ClientStreamListener.RpcProgress;
import io.grpc.internal.ClientTransport.PingCallback;
import io.grpc.internal.GrpcUtil;
@@ -104,6 +105,7 @@ class NettyClientHandler extends AbstractNettyHandler {
private WriteQueue clientWriteQueue;
private Http2Ping ping;
private Attributes attributes = Attributes.EMPTY;
+ private Channelz.Security securityInfo;
static NettyClientHandler newHandler(
ClientTransportLifecycleManager lifecycleManager,
@@ -407,9 +409,15 @@ class NettyClientHandler extends AbstractNettyHandler {
}
@Override
- public void handleProtocolNegotiationCompleted(Attributes attributes) {
+ public void handleProtocolNegotiationCompleted(
+ Attributes attributes, Channelz.Security securityInfo) {
this.attributes = attributes;
- super.handleProtocolNegotiationCompleted(attributes);
+ this.securityInfo = securityInfo;
+ super.handleProtocolNegotiationCompleted(attributes, securityInfo);
+ }
+
+ Channelz.Security getSecurityInfo() {
+ return securityInfo;
}
diff --git a/netty/src/main/java/io/grpc/netty/NettyClientTransport.java b/netty/src/main/java/io/grpc/netty/NettyClientTransport.java
index 0020c54f5..7d963fc8e 100644
--- a/netty/src/main/java/io/grpc/netty/NettyClientTransport.java
+++ b/netty/src/main/java/io/grpc/netty/NettyClientTransport.java
@@ -28,7 +28,6 @@ import io.grpc.CallOptions;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.Status;
-import io.grpc.internal.Channelz.Security;
import io.grpc.internal.Channelz.SocketStats;
import io.grpc.internal.ClientStream;
import io.grpc.internal.ConnectionClientTransport;
@@ -80,7 +79,6 @@ class NettyClientTransport implements ConnectionClientTransport {
private final long keepAliveTimeoutNanos;
private final boolean keepAliveWithoutCalls;
private final Runnable tooManyPingsRunnable;
-
private ProtocolNegotiator.Handler negotiationHandler;
private NettyClientHandler handler;
// We should not send on the channel until negotiation completes. This is a hard requirement
@@ -320,26 +318,14 @@ class NettyClientTransport implements ConnectionClientTransport {
if (channel.eventLoop().inEventLoop()) {
// This is necessary, otherwise we will block forever if we get the future from inside
// the event loop.
- result.set(
- new SocketStats(
- transportTracer.getStats(),
- channel.localAddress(),
- channel.remoteAddress(),
- Utils.getSocketOptions(channel),
- new Security()));
+ result.set(getStatsHelper(channel));
return result;
}
channel.eventLoop().submit(
new Runnable() {
@Override
public void run() {
- result.set(
- new SocketStats(
- transportTracer.getStats(),
- channel.localAddress(),
- channel.remoteAddress(),
- Utils.getSocketOptions(channel),
- new Security()));
+ result.set(getStatsHelper(channel));
}
})
.addListener(
@@ -354,6 +340,16 @@ class NettyClientTransport implements ConnectionClientTransport {
return result;
}
+ private SocketStats getStatsHelper(Channel ch) {
+ assert ch.eventLoop().inEventLoop();
+ return new SocketStats(
+ transportTracer.getStats(),
+ channel.localAddress(),
+ channel.remoteAddress(),
+ Utils.getSocketOptions(ch),
+ handler == null ? null : handler.getSecurityInfo());
+ }
+
@VisibleForTesting
Channel channel() {
return channel;
diff --git a/netty/src/main/java/io/grpc/netty/NettyServerHandler.java b/netty/src/main/java/io/grpc/netty/NettyServerHandler.java
index 47c628e0c..0bacd5081 100644
--- a/netty/src/main/java/io/grpc/netty/NettyServerHandler.java
+++ b/netty/src/main/java/io/grpc/netty/NettyServerHandler.java
@@ -36,6 +36,7 @@ import io.grpc.InternalStatus;
import io.grpc.Metadata;
import io.grpc.ServerStreamTracer;
import io.grpc.Status;
+import io.grpc.internal.Channelz;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.LogExceptionRunnable;
@@ -112,6 +113,7 @@ class NettyServerHandler extends AbstractNettyHandler {
private final KeepAliveEnforcer keepAliveEnforcer;
/** Incomplete attributes produced by negotiator. */
private Attributes negotiationAttributes;
+ private Channelz.Security securityInfo;
/** Completed attributes produced by transportReady. */
private Attributes attributes;
private Throwable connectionError;
@@ -504,8 +506,14 @@ class NettyServerHandler extends AbstractNettyHandler {
}
@Override
- public void handleProtocolNegotiationCompleted(Attributes attrs) {
+ public void handleProtocolNegotiationCompleted(
+ Attributes attrs, Channelz.Security securityInfo) {
negotiationAttributes = attrs;
+ this.securityInfo = securityInfo;
+ }
+
+ Channelz.Security getSecurityInfo() {
+ return securityInfo;
}
@VisibleForTesting
diff --git a/netty/src/main/java/io/grpc/netty/NettyServerTransport.java b/netty/src/main/java/io/grpc/netty/NettyServerTransport.java
index c76ca6673..5c00e2090 100644
--- a/netty/src/main/java/io/grpc/netty/NettyServerTransport.java
+++ b/netty/src/main/java/io/grpc/netty/NettyServerTransport.java
@@ -59,6 +59,8 @@ class NettyServerTransport implements ServerTransport {
private final ChannelPromise channelUnused;
private final ProtocolNegotiator protocolNegotiator;
private final int maxStreams;
+ // only accessed from channel event loop
+ private NettyServerHandler grpcHandler;
private ServerTransportListener listener;
private boolean terminated;
private final int flowControlWindow;
@@ -115,7 +117,7 @@ class NettyServerTransport implements ServerTransport {
this.listener = listener;
// Create the Netty handler for the pipeline.
- final NettyServerHandler grpcHandler = createHandler(listener, channelUnused);
+ grpcHandler = createHandler(listener, channelUnused);
NettyHandlerSettings.setAutoWindow(grpcHandler);
// Notify when the channel closes.
@@ -199,30 +201,17 @@ class NettyServerTransport implements ServerTransport {
@Override
public ListenableFuture<SocketStats> getStats() {
final SettableFuture<SocketStats> result = SettableFuture.create();
- // TODO: fill in security
if (channel.eventLoop().inEventLoop()) {
// This is necessary, otherwise we will block forever if we get the future from inside
// the event loop.
- result.set(
- new SocketStats(
- transportTracer.getStats(),
- channel.localAddress(),
- channel.remoteAddress(),
- Utils.getSocketOptions(channel),
- /*security=*/ null));
+ result.set(getStatsHelper(channel));
return result;
}
channel.eventLoop().submit(
new Runnable() {
@Override
public void run() {
- result.set(
- new SocketStats(
- transportTracer.getStats(),
- channel.localAddress(),
- channel.remoteAddress(),
- Utils.getSocketOptions(channel),
- /*security=*/ null));
+ result.set(getStatsHelper(channel));
}
})
.addListener(
@@ -237,6 +226,16 @@ class NettyServerTransport implements ServerTransport {
return result;
}
+ private SocketStats getStatsHelper(Channel ch) {
+ Preconditions.checkState(ch.eventLoop().inEventLoop());
+ return new SocketStats(
+ transportTracer.getStats(),
+ channel.localAddress(),
+ channel.remoteAddress(),
+ Utils.getSocketOptions(ch),
+ grpcHandler == null ? null : grpcHandler.getSecurityInfo());
+ }
+
/**
* Creates the Netty handler to be used in the channel pipeline.
*/
diff --git a/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java b/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
index 398ef8838..b07814a98 100644
--- a/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
+++ b/netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
@@ -25,6 +25,7 @@ import io.grpc.Attributes;
import io.grpc.Grpc;
import io.grpc.Internal;
import io.grpc.Status;
+import io.grpc.internal.Channelz;
import io.grpc.internal.GrpcUtil;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture;
@@ -62,6 +63,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
/**
* Common {@link ProtocolNegotiator}s used by gRPC.
@@ -86,7 +88,8 @@ public final class ProtocolNegotiators {
// Set sttributes before replace to be sure we pass it before accepting any requests.
handler.handleProtocolNegotiationCompleted(Attributes.newBuilder()
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
- .build());
+ .build(),
+ /*securityInfo=*/ null);
// Just replace this handler with the gRPC handler.
ctx.pipeline().replace(this, null, handler);
}
@@ -145,14 +148,15 @@ public final class ProtocolNegotiators {
SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
if (handshakeEvent.isSuccess()) {
if (NEXT_PROTOCOL_VERSIONS.contains(sslHandler(ctx.pipeline()).applicationProtocol())) {
+ SSLSession session = sslHandler(ctx.pipeline()).engine().getSession();
// Successfully negotiated the protocol.
// Notify about completion and pass down SSLSession in attributes.
grpcHandler.handleProtocolNegotiationCompleted(
Attributes.newBuilder()
- .set(Grpc.TRANSPORT_ATTR_SSL_SESSION,
- sslHandler(ctx.pipeline()).engine().getSession())
+ .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
- .build());
+ .build(),
+ new Channelz.Security(new Channelz.Tls(session)));
// Replace this handler with the GRPC handler.
ctx.pipeline().replace(this, null, grpcHandler);
} else {
@@ -634,13 +638,15 @@ public final class ProtocolNegotiators {
// will fail before we see the userEvent, and the channel is closed down prematurely.
ctx.pipeline().addBefore(ctx.name(), null, grpcHandler);
+ SSLSession session = handler.engine().getSession();
// Successfully negotiated the protocol.
// Notify about completion and pass down SSLSession in attributes.
grpcHandler.handleProtocolNegotiationCompleted(
Attributes.newBuilder()
- .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, handler.engine().getSession())
+ .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
- .build());
+ .build(),
+ new Channelz.Security(new Channelz.Tls(session)));
writeBufferedAndRemove(ctx);
} else {
Exception ex = new Exception(
@@ -686,7 +692,8 @@ public final class ProtocolNegotiators {
Attributes
.newBuilder()
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
- .build());
+ .build(),
+ /*securityInfo=*/ null);
super.channelActive(ctx);
}
}
@@ -727,7 +734,8 @@ public final class ProtocolNegotiators {
Attributes
.newBuilder()
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
- .build());
+ .build(),
+ /*securityInfo=*/ null);
} else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
fail(ctx, unavailableException("HTTP/2 upgrade rejected"));
}
diff --git a/netty/src/test/java/io/grpc/netty/NettyServerHandlerTest.java b/netty/src/test/java/io/grpc/netty/NettyServerHandlerTest.java
index 8be0ba6ec..eb4aa3d5a 100644
--- a/netty/src/test/java/io/grpc/netty/NettyServerHandlerTest.java
+++ b/netty/src/test/java/io/grpc/netty/NettyServerHandlerTest.java
@@ -193,7 +193,7 @@ public class NettyServerHandlerTest extends NettyHandlerTestBase<NettyServerHand
handler().setKeepAliveManagerForTest(spyKeepAliveManager);
// Simulate receipt of the connection preface
- handler().handleProtocolNegotiationCompleted(Attributes.EMPTY);
+ handler().handleProtocolNegotiationCompleted(Attributes.EMPTY, /*securityInfo=*/ null);
channelRead(Http2CodecUtil.connectionPrefaceBuf());
// Simulate receipt of initial remote settings.
ByteBuf serializedSettings = serializeSettings(new Http2Settings());
@@ -204,7 +204,7 @@ public class NettyServerHandlerTest extends NettyHandlerTestBase<NettyServerHand
public void transportReadyDelayedUntilConnectionPreface() throws Exception {
initChannel(new GrpcHttp2ServerHeadersDecoder(GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE));
- handler().handleProtocolNegotiationCompleted(Attributes.EMPTY);
+ handler().handleProtocolNegotiationCompleted(Attributes.EMPTY, /*securityInfo=*/ null);
verify(transportListener, never()).transportReady(any(Attributes.class));
// Simulate receipt of the connection preface
diff --git a/okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java b/okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java
index feab8311f..6d1265a77 100644
--- a/okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java
+++ b/okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java
@@ -38,7 +38,7 @@ import io.grpc.MethodDescriptor.MethodType;
import io.grpc.Status;
import io.grpc.Status.Code;
import io.grpc.StatusException;
-import io.grpc.internal.Channelz.Security;
+import io.grpc.internal.Channelz;
import io.grpc.internal.Channelz.SocketStats;
import io.grpc.internal.ClientStreamListener.RpcProgress;
import io.grpc.internal.ConnectionClientTransport;
@@ -81,6 +81,8 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import okio.Buffer;
import okio.BufferedSink;
@@ -185,6 +187,8 @@ class OkHttpClientTransport implements ConnectionClientTransport {
private final Runnable tooManyPingsRunnable;
@GuardedBy("lock")
private final TransportTracer transportTracer;
+ @GuardedBy("lock")
+ private Channelz.Security securityInfo;
@VisibleForTesting
@Nullable
@@ -454,6 +458,7 @@ class OkHttpClientTransport implements ConnectionClientTransport {
Variant variant = new Http2();
BufferedSink sink;
Socket sock;
+ SSLSession sslSession = null;
try {
if (proxy == null) {
sock = new Socket(address.getAddress(), address.getPort());
@@ -463,9 +468,11 @@ class OkHttpClientTransport implements ConnectionClientTransport {
}
if (sslSocketFactory != null) {
- sock = OkHttpTlsUpgrader.upgrade(
+ SSLSocket sslSocket = OkHttpTlsUpgrader.upgrade(
sslSocketFactory, hostnameVerifier, sock, getOverridenHost(), getOverridenPort(),
connectionSpec);
+ sslSession = sslSocket.getSession();
+ sock = sslSocket;
}
sock.setTcpNoDelay(true);
source = Okio.buffer(Okio.source(sock));
@@ -475,6 +482,7 @@ class OkHttpClientTransport implements ConnectionClientTransport {
attributes = Attributes
.newBuilder()
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, sock.getRemoteSocketAddress())
+ .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, sslSession)
.build();
} catch (StatusException e) {
startGoAway(0, ErrorCode.INTERNAL_ERROR, e.getStatus());
@@ -489,10 +497,12 @@ class OkHttpClientTransport implements ConnectionClientTransport {
FrameWriter rawFrameWriter;
synchronized (lock) {
- socket = sock;
+ socket = Preconditions.checkNotNull(sock, "socket");
maxConcurrentStreams = Integer.MAX_VALUE;
-
startPendingStreams();
+ if (sslSession != null) {
+ securityInfo = new Channelz.Security(new Channelz.Tls(sslSession));
+ }
}
rawFrameWriter = variant.newWriter(sink, true);
@@ -905,14 +915,23 @@ class OkHttpClientTransport implements ConnectionClientTransport {
@Override
public ListenableFuture<SocketStats> getStats() {
+ SettableFuture<SocketStats> ret = SettableFuture.create();
synchronized (lock) {
- SettableFuture<SocketStats> ret = SettableFuture.create();
- ret.set(new SocketStats(
- transportTracer.getStats(),
- socket.getLocalSocketAddress(),
- socket.getRemoteSocketAddress(),
- Utils.getSocketOptions(socket),
- new Security()));
+ if (socket == null) {
+ ret.set(new SocketStats(
+ transportTracer.getStats(),
+ /*local=*/ null,
+ /*remote=*/ null,
+ new Channelz.SocketOptions.Builder().build(),
+ /*security=*/ null));
+ } else {
+ ret.set(new SocketStats(
+ transportTracer.getStats(),
+ socket.getLocalSocketAddress(),
+ socket.getRemoteSocketAddress(),
+ Utils.getSocketOptions(socket),
+ securityInfo));
+ }
return ret;
}
}
diff --git a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java b/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java
index 83708dd90..3ad4f70f4 100644
--- a/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java
+++ b/services/src/main/java/io/grpc/services/ChannelzProtoUtil.java
@@ -36,6 +36,9 @@ import io.grpc.channelz.v1.ChannelRef;
import io.grpc.channelz.v1.GetServerSocketsResponse;
import io.grpc.channelz.v1.GetServersResponse;
import io.grpc.channelz.v1.GetTopChannelsResponse;
+import io.grpc.channelz.v1.Security;
+import io.grpc.channelz.v1.Security.OtherSecurity;
+import io.grpc.channelz.v1.Security.Tls;
import io.grpc.channelz.v1.Server;
import io.grpc.channelz.v1.ServerData;
import io.grpc.channelz.v1.ServerRef;
@@ -61,15 +64,20 @@ import io.grpc.internal.Instrumented;
import io.grpc.internal.WithLogId;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* A static utility class for turning internal data structures into protos.
*/
final class ChannelzProtoUtil {
+ private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName());
+
private ChannelzProtoUtil() {
// do not instantiate.
}
@@ -128,11 +136,44 @@ final class ChannelzProtoUtil {
.build();
}
+ static Security toSecurity(Channelz.Security security) {
+ Preconditions.checkNotNull(security);
+ Preconditions.checkState(
+ security.tls != null ^ security.other != null,
+ "one of tls or othersecurity must be non null");
+ if (security.tls != null) {
+ Tls.Builder tlsBuilder
+ = Tls.newBuilder().setStandardName(security.tls.cipherSuiteStandardName);
+ try {
+ if (security.tls.localCert != null) {
+ tlsBuilder.setLocalCertificate(ByteString.copyFrom(
+ security.tls.localCert.getEncoded()));
+ }
+ if (security.tls.remoteCert != null) {
+ tlsBuilder.setRemoteCertificate(ByteString.copyFrom(
+ security.tls.remoteCert.getEncoded()));
+ }
+ } catch (CertificateEncodingException e) {
+ logger.log(Level.FINE, "Caught exception", e);
+ }
+ return Security.newBuilder().setTls(tlsBuilder).build();
+ } else {
+ OtherSecurity.Builder builder = OtherSecurity.newBuilder().setName(security.other.name);
+ if (security.other.any != null) {
+ builder.setValue((Any) security.other.any);
+ }
+ return Security.newBuilder().setOther(builder).build();
+ }
+ }
+
static Socket toSocket(Instrumented<SocketStats> obj) {
SocketStats socketStats = getFuture(obj.getStats());
Builder builder = Socket.newBuilder()
.setRef(toSocketRef(obj))
.setLocal(toAddress(socketStats.local));
+ if (socketStats.security != null) {
+ builder.setSecurity(toSecurity(socketStats.security));
+ }
// listen sockets do not have remote nor data
if (socketStats.remote != null) {
builder.setRemote(toAddress(socketStats.remote));
diff --git a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java
index bb1410120..861b9f05e 100644
--- a/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java
+++ b/services/src/test/java/io/grpc/services/ChannelzProtoUtilTest.java
@@ -19,12 +19,16 @@ package io.grpc.services;
import static com.google.common.truth.Truth.assertThat;
import static io.grpc.internal.Channelz.id;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.Int64Value;
+import com.google.protobuf.Message;
import com.google.protobuf.util.Durations;
import com.google.protobuf.util.Timestamps;
import io.grpc.ConnectivityState;
@@ -36,9 +40,13 @@ import io.grpc.channelz.v1.Channel;
import io.grpc.channelz.v1.ChannelData;
import io.grpc.channelz.v1.ChannelData.State;
import io.grpc.channelz.v1.ChannelRef;
+import io.grpc.channelz.v1.GetChannelRequest;
import io.grpc.channelz.v1.GetServerSocketsResponse;
import io.grpc.channelz.v1.GetServersResponse;
import io.grpc.channelz.v1.GetTopChannelsResponse;
+import io.grpc.channelz.v1.Security;
+import io.grpc.channelz.v1.Security.OtherSecurity;
+import io.grpc.channelz.v1.Security.Tls;
import io.grpc.channelz.v1.Server;
import io.grpc.channelz.v1.ServerData;
import io.grpc.channelz.v1.ServerRef;
@@ -69,6 +77,7 @@ import io.netty.channel.unix.DomainSocketAddress;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Map.Entry;
import org.junit.Test;
@@ -413,6 +422,59 @@ public final class ChannelzProtoUtilTest {
}
@Test
+ public void socketSecurityTls() throws Exception {
+ Certificate local = mock(Certificate.class);
+ Certificate remote = mock(Certificate.class);
+ when(local.getEncoded()).thenReturn("localcert".getBytes(Charsets.UTF_8));
+ when(remote.getEncoded()).thenReturn("remotecert".getBytes(Charsets.UTF_8));
+
+ socket.security = new Channelz.Security(
+ new Channelz.Tls("TLS_NULL_WITH_NULL_NULL", local, remote));
+ assertEquals(
+ Security.newBuilder().setTls(
+ Tls.newBuilder()
+ .setStandardName("TLS_NULL_WITH_NULL_NULL")
+ .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8))
+ .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8)))
+ .build(),
+ ChannelzProtoUtil.toSocket(socket).getSecurity());
+
+ socket.security = new Channelz.Security(
+ new Channelz.Tls("TLS_NULL_WITH_NULL_NULL", /*localcert=*/ null, remote));
+ assertEquals(
+ Security.newBuilder().setTls(
+ Tls.newBuilder()
+ .setStandardName("TLS_NULL_WITH_NULL_NULL")
+ .setRemoteCertificate(ByteString.copyFrom("remotecert", Charsets.UTF_8)))
+ .build(),
+ ChannelzProtoUtil.toSocket(socket).getSecurity());
+
+ socket.security = new Channelz.Security(
+ new Channelz.Tls("TLS_NULL_WITH_NULL_NULL", local, /*remotecert=*/ null));
+ assertEquals(
+ Security.newBuilder().setTls(
+ Tls.newBuilder()
+ .setStandardName("TLS_NULL_WITH_NULL_NULL")
+ .setLocalCertificate(ByteString.copyFrom("localcert", Charsets.UTF_8)))
+ .build(),
+ ChannelzProtoUtil.toSocket(socket).getSecurity());
+ }
+
+ @Test
+ public void socketSecurityOther() throws Exception {
+ // what is packed here is not important, just pick some proto message
+ Message contents = GetChannelRequest.newBuilder().setChannelId(1).build();
+ Any packed = Any.pack(contents);
+ socket.security
+ = new Channelz.Security(new Channelz.OtherSecurity("other_security", packed));
+ assertEquals(
+ Security.newBuilder().setOther(
+ OtherSecurity.newBuilder().setName("other_security").setValue(packed))
+ .build(),
+ ChannelzProtoUtil.toSocket(socket).getSecurity());
+ }
+
+ @Test
public void toAddress_inet() throws Exception {
InetSocketAddress inet4 = new InetSocketAddress(Inet4Address.getByName("10.0.0.1"), 1000);
assertEquals(
diff --git a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java b/services/src/test/java/io/grpc/services/ChannelzTestHelper.java
index 81f63ae81..6f9c24250 100644
--- a/services/src/test/java/io/grpc/services/ChannelzTestHelper.java
+++ b/services/src/test/java/io/grpc/services/ChannelzTestHelper.java
@@ -57,6 +57,7 @@ final class ChannelzTestHelper {
SocketAddress local = new InetSocketAddress("10.0.0.1", 1000);
SocketAddress remote = new InetSocketAddress("10.0.0.2", 1000);
Channelz.SocketOptions socketOptions = new Channelz.SocketOptions.Builder().build();
+ Security security = null;
@Override
public ListenableFuture<SocketStats> getStats() {
@@ -67,7 +68,7 @@ final class ChannelzTestHelper {
local,
remote,
socketOptions,
- new Security()));
+ security));
return ret;
}