mirror of https://github.com/grpc/grpc-java.git
core,netty,okhttp,services,testing: expose security info to channelz (#4300)
Pull the TLS info from the SSLSession object for TLS, and AltsContext for ALTS.
This commit is contained in:
parent
218e944e16
commit
1a2d076aed
|
|
@ -9,6 +9,7 @@ java_library(
|
||||||
":handshaker_java_grpc",
|
":handshaker_java_grpc",
|
||||||
":handshaker_java_proto",
|
":handshaker_java_proto",
|
||||||
"//core",
|
"//core",
|
||||||
|
"//core:internal",
|
||||||
"//netty",
|
"//netty",
|
||||||
"//stub",
|
"//stub",
|
||||||
"@com_google_code_findbugs_jsr305//jar",
|
"@com_google_code_findbugs_jsr305//jar",
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,14 @@ package io.grpc.alts.internal;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.protobuf.Any;
|
||||||
import io.grpc.Attributes;
|
import io.grpc.Attributes;
|
||||||
import io.grpc.Grpc;
|
import io.grpc.Grpc;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.alts.internal.RpcProtocolVersionsUtil.RpcVersionsCheckResult;
|
import io.grpc.alts.internal.RpcProtocolVersionsUtil.RpcVersionsCheckResult;
|
||||||
import io.grpc.alts.internal.TsiHandshakeHandler.TsiHandshakeCompletionEvent;
|
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.GrpcHttp2ConnectionHandler;
|
||||||
import io.grpc.netty.ProtocolNegotiator;
|
import io.grpc.netty.ProtocolNegotiator;
|
||||||
import io.grpc.netty.ProtocolNegotiators.AbstractBufferingHandler;
|
import io.grpc.netty.ProtocolNegotiators.AbstractBufferingHandler;
|
||||||
|
|
@ -119,7 +122,8 @@ public abstract class AltsProtocolNegotiator implements ProtocolNegotiator {
|
||||||
.set(TSI_PEER_KEY, altsEvt.peer())
|
.set(TSI_PEER_KEY, altsEvt.peer())
|
||||||
.set(ALTS_CONTEXT_KEY, altsContext)
|
.set(ALTS_CONTEXT_KEY, altsContext)
|
||||||
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
.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.
|
// Now write any buffered data and remove this handler.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import io.grpc.Grpc;
|
||||||
import io.grpc.alts.internal.Handshaker.HandshakerResult;
|
import io.grpc.alts.internal.Handshaker.HandshakerResult;
|
||||||
import io.grpc.alts.internal.TsiFrameProtector.Consumer;
|
import io.grpc.alts.internal.TsiFrameProtector.Consumer;
|
||||||
import io.grpc.alts.internal.TsiPeer.Property;
|
import io.grpc.alts.internal.TsiPeer.Property;
|
||||||
|
import io.grpc.internal.Channelz;
|
||||||
import io.grpc.netty.GrpcHttp2ConnectionHandler;
|
import io.grpc.netty.GrpcHttp2ConnectionHandler;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
|
@ -399,7 +400,8 @@ public class AltsProtocolNegotiatorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
// If we are added to the pipeline, we need to remove ourselves. The HTTP2 handler
|
||||||
channel.pipeline().remove(this);
|
channel.pipeline().remove(this);
|
||||||
this.attrs = attrs;
|
this.attrs = attrs;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import io.grpc.ConnectivityState;
|
import io.grpc.ConnectivityState;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
@ -30,10 +31,15 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.ConcurrentNavigableMap;
|
import java.util.concurrent.ConcurrentNavigableMap;
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
public final class Channelz {
|
public final class Channelz {
|
||||||
|
private static final Logger log = Logger.getLogger(Channelz.class.getName());
|
||||||
private static final Channelz INSTANCE = new Channelz();
|
private static final Channelz INSTANCE = new Channelz();
|
||||||
|
|
||||||
private final ConcurrentNavigableMap<Long, Instrumented<ServerStats>> servers
|
private final ConcurrentNavigableMap<Long, Instrumented<ServerStats>> servers
|
||||||
|
|
@ -450,21 +456,100 @@ public final class Channelz {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Security {
|
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 {
|
public static final class SocketStats {
|
||||||
@Nullable public final TransportStats data;
|
@Nullable public final TransportStats data;
|
||||||
public final SocketAddress local;
|
@Nullable public final SocketAddress local;
|
||||||
@Nullable public final SocketAddress remote;
|
@Nullable public final SocketAddress remote;
|
||||||
public final SocketOptions socketOptions;
|
public final SocketOptions socketOptions;
|
||||||
|
// Can be null if plaintext
|
||||||
@Nullable public final Security security;
|
@Nullable public final Security security;
|
||||||
|
|
||||||
/** Creates an instance. */
|
/** Creates an instance. */
|
||||||
public SocketStats(
|
public SocketStats(
|
||||||
TransportStats data,
|
TransportStats data,
|
||||||
SocketAddress local,
|
@Nullable SocketAddress local,
|
||||||
SocketAddress remote,
|
@Nullable SocketAddress remote,
|
||||||
SocketOptions socketOptions,
|
SocketOptions socketOptions,
|
||||||
Security security) {
|
Security security) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,13 @@ package io.grpc.internal;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static io.grpc.internal.Channelz.id;
|
import static io.grpc.internal.Channelz.id;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
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 com.google.common.util.concurrent.ListenableFuture;
|
||||||
import io.grpc.internal.Channelz.ChannelStats;
|
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.ServerSocketsList;
|
||||||
import io.grpc.internal.Channelz.ServerStats;
|
import io.grpc.internal.Channelz.ServerStats;
|
||||||
import io.grpc.internal.Channelz.SocketStats;
|
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.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
@ -272,6 +278,21 @@ public final class ChannelzTest {
|
||||||
assertThat(list2.sockets).containsExactly(socket2);
|
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) {
|
private void assertEmptyServerSocketsPage(long serverId, long socketId) {
|
||||||
ServerSocketsList emptyPage
|
ServerSocketsList emptyPage
|
||||||
= channelz.getServerSockets(serverId, socketId, /*maxPageSize=*/ 1);
|
= channelz.getServerSockets(serverId, socketId, /*maxPageSize=*/ 1);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.grpc.netty;
|
||||||
|
|
||||||
import io.grpc.Attributes;
|
import io.grpc.Attributes;
|
||||||
import io.grpc.Internal;
|
import io.grpc.Internal;
|
||||||
|
import io.grpc.internal.Channelz;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
|
||||||
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
||||||
|
|
@ -43,6 +44,17 @@ public abstract class GrpcHttp2ConnectionHandler extends Http2ConnectionHandler
|
||||||
this.channelUnused = channelUnused;
|
this.channelUnused = channelUnused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Triggered on protocol negotiation completion.
|
||||||
*
|
*
|
||||||
|
|
@ -50,8 +62,9 @@ public abstract class GrpcHttp2ConnectionHandler extends Http2ConnectionHandler
|
||||||
* channel.
|
* channel.
|
||||||
*
|
*
|
||||||
* @param attrs arbitrary attributes passed after protocol negotiation (eg. SSLSession).
|
* @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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import io.grpc.Attributes;
|
||||||
import io.grpc.Metadata;
|
import io.grpc.Metadata;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.StatusException;
|
import io.grpc.StatusException;
|
||||||
|
import io.grpc.internal.Channelz;
|
||||||
import io.grpc.internal.ClientStreamListener.RpcProgress;
|
import io.grpc.internal.ClientStreamListener.RpcProgress;
|
||||||
import io.grpc.internal.ClientTransport.PingCallback;
|
import io.grpc.internal.ClientTransport.PingCallback;
|
||||||
import io.grpc.internal.GrpcUtil;
|
import io.grpc.internal.GrpcUtil;
|
||||||
|
|
@ -104,6 +105,7 @@ class NettyClientHandler extends AbstractNettyHandler {
|
||||||
private WriteQueue clientWriteQueue;
|
private WriteQueue clientWriteQueue;
|
||||||
private Http2Ping ping;
|
private Http2Ping ping;
|
||||||
private Attributes attributes = Attributes.EMPTY;
|
private Attributes attributes = Attributes.EMPTY;
|
||||||
|
private Channelz.Security securityInfo;
|
||||||
|
|
||||||
static NettyClientHandler newHandler(
|
static NettyClientHandler newHandler(
|
||||||
ClientTransportLifecycleManager lifecycleManager,
|
ClientTransportLifecycleManager lifecycleManager,
|
||||||
|
|
@ -407,9 +409,15 @@ class NettyClientHandler extends AbstractNettyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleProtocolNegotiationCompleted(Attributes attributes) {
|
public void handleProtocolNegotiationCompleted(
|
||||||
|
Attributes attributes, Channelz.Security securityInfo) {
|
||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
super.handleProtocolNegotiationCompleted(attributes);
|
this.securityInfo = securityInfo;
|
||||||
|
super.handleProtocolNegotiationCompleted(attributes, securityInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Channelz.Security getSecurityInfo() {
|
||||||
|
return securityInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import io.grpc.CallOptions;
|
||||||
import io.grpc.Metadata;
|
import io.grpc.Metadata;
|
||||||
import io.grpc.MethodDescriptor;
|
import io.grpc.MethodDescriptor;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.internal.Channelz.Security;
|
|
||||||
import io.grpc.internal.Channelz.SocketStats;
|
import io.grpc.internal.Channelz.SocketStats;
|
||||||
import io.grpc.internal.ClientStream;
|
import io.grpc.internal.ClientStream;
|
||||||
import io.grpc.internal.ConnectionClientTransport;
|
import io.grpc.internal.ConnectionClientTransport;
|
||||||
|
|
@ -80,7 +79,6 @@ class NettyClientTransport implements ConnectionClientTransport {
|
||||||
private final long keepAliveTimeoutNanos;
|
private final long keepAliveTimeoutNanos;
|
||||||
private final boolean keepAliveWithoutCalls;
|
private final boolean keepAliveWithoutCalls;
|
||||||
private final Runnable tooManyPingsRunnable;
|
private final Runnable tooManyPingsRunnable;
|
||||||
|
|
||||||
private ProtocolNegotiator.Handler negotiationHandler;
|
private ProtocolNegotiator.Handler negotiationHandler;
|
||||||
private NettyClientHandler handler;
|
private NettyClientHandler handler;
|
||||||
// We should not send on the channel until negotiation completes. This is a hard requirement
|
// 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()) {
|
if (channel.eventLoop().inEventLoop()) {
|
||||||
// This is necessary, otherwise we will block forever if we get the future from inside
|
// This is necessary, otherwise we will block forever if we get the future from inside
|
||||||
// the event loop.
|
// the event loop.
|
||||||
result.set(
|
result.set(getStatsHelper(channel));
|
||||||
new SocketStats(
|
|
||||||
transportTracer.getStats(),
|
|
||||||
channel.localAddress(),
|
|
||||||
channel.remoteAddress(),
|
|
||||||
Utils.getSocketOptions(channel),
|
|
||||||
new Security()));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
channel.eventLoop().submit(
|
channel.eventLoop().submit(
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
result.set(
|
result.set(getStatsHelper(channel));
|
||||||
new SocketStats(
|
|
||||||
transportTracer.getStats(),
|
|
||||||
channel.localAddress(),
|
|
||||||
channel.remoteAddress(),
|
|
||||||
Utils.getSocketOptions(channel),
|
|
||||||
new Security()));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addListener(
|
.addListener(
|
||||||
|
|
@ -354,6 +340,16 @@ class NettyClientTransport implements ConnectionClientTransport {
|
||||||
return result;
|
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
|
@VisibleForTesting
|
||||||
Channel channel() {
|
Channel channel() {
|
||||||
return channel;
|
return channel;
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import io.grpc.InternalStatus;
|
||||||
import io.grpc.Metadata;
|
import io.grpc.Metadata;
|
||||||
import io.grpc.ServerStreamTracer;
|
import io.grpc.ServerStreamTracer;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
|
import io.grpc.internal.Channelz;
|
||||||
import io.grpc.internal.GrpcUtil;
|
import io.grpc.internal.GrpcUtil;
|
||||||
import io.grpc.internal.KeepAliveManager;
|
import io.grpc.internal.KeepAliveManager;
|
||||||
import io.grpc.internal.LogExceptionRunnable;
|
import io.grpc.internal.LogExceptionRunnable;
|
||||||
|
|
@ -112,6 +113,7 @@ class NettyServerHandler extends AbstractNettyHandler {
|
||||||
private final KeepAliveEnforcer keepAliveEnforcer;
|
private final KeepAliveEnforcer keepAliveEnforcer;
|
||||||
/** Incomplete attributes produced by negotiator. */
|
/** Incomplete attributes produced by negotiator. */
|
||||||
private Attributes negotiationAttributes;
|
private Attributes negotiationAttributes;
|
||||||
|
private Channelz.Security securityInfo;
|
||||||
/** Completed attributes produced by transportReady. */
|
/** Completed attributes produced by transportReady. */
|
||||||
private Attributes attributes;
|
private Attributes attributes;
|
||||||
private Throwable connectionError;
|
private Throwable connectionError;
|
||||||
|
|
@ -504,8 +506,14 @@ class NettyServerHandler extends AbstractNettyHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleProtocolNegotiationCompleted(Attributes attrs) {
|
public void handleProtocolNegotiationCompleted(
|
||||||
|
Attributes attrs, Channelz.Security securityInfo) {
|
||||||
negotiationAttributes = attrs;
|
negotiationAttributes = attrs;
|
||||||
|
this.securityInfo = securityInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
Channelz.Security getSecurityInfo() {
|
||||||
|
return securityInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ class NettyServerTransport implements ServerTransport {
|
||||||
private final ChannelPromise channelUnused;
|
private final ChannelPromise channelUnused;
|
||||||
private final ProtocolNegotiator protocolNegotiator;
|
private final ProtocolNegotiator protocolNegotiator;
|
||||||
private final int maxStreams;
|
private final int maxStreams;
|
||||||
|
// only accessed from channel event loop
|
||||||
|
private NettyServerHandler grpcHandler;
|
||||||
private ServerTransportListener listener;
|
private ServerTransportListener listener;
|
||||||
private boolean terminated;
|
private boolean terminated;
|
||||||
private final int flowControlWindow;
|
private final int flowControlWindow;
|
||||||
|
|
@ -115,7 +117,7 @@ class NettyServerTransport implements ServerTransport {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
||||||
// Create the Netty handler for the pipeline.
|
// Create the Netty handler for the pipeline.
|
||||||
final NettyServerHandler grpcHandler = createHandler(listener, channelUnused);
|
grpcHandler = createHandler(listener, channelUnused);
|
||||||
NettyHandlerSettings.setAutoWindow(grpcHandler);
|
NettyHandlerSettings.setAutoWindow(grpcHandler);
|
||||||
|
|
||||||
// Notify when the channel closes.
|
// Notify when the channel closes.
|
||||||
|
|
@ -199,30 +201,17 @@ class NettyServerTransport implements ServerTransport {
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<SocketStats> getStats() {
|
public ListenableFuture<SocketStats> getStats() {
|
||||||
final SettableFuture<SocketStats> result = SettableFuture.create();
|
final SettableFuture<SocketStats> result = SettableFuture.create();
|
||||||
// TODO: fill in security
|
|
||||||
if (channel.eventLoop().inEventLoop()) {
|
if (channel.eventLoop().inEventLoop()) {
|
||||||
// This is necessary, otherwise we will block forever if we get the future from inside
|
// This is necessary, otherwise we will block forever if we get the future from inside
|
||||||
// the event loop.
|
// the event loop.
|
||||||
result.set(
|
result.set(getStatsHelper(channel));
|
||||||
new SocketStats(
|
|
||||||
transportTracer.getStats(),
|
|
||||||
channel.localAddress(),
|
|
||||||
channel.remoteAddress(),
|
|
||||||
Utils.getSocketOptions(channel),
|
|
||||||
/*security=*/ null));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
channel.eventLoop().submit(
|
channel.eventLoop().submit(
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
result.set(
|
result.set(getStatsHelper(channel));
|
||||||
new SocketStats(
|
|
||||||
transportTracer.getStats(),
|
|
||||||
channel.localAddress(),
|
|
||||||
channel.remoteAddress(),
|
|
||||||
Utils.getSocketOptions(channel),
|
|
||||||
/*security=*/ null));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addListener(
|
.addListener(
|
||||||
|
|
@ -237,6 +226,16 @@ class NettyServerTransport implements ServerTransport {
|
||||||
return result;
|
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.
|
* Creates the Netty handler to be used in the channel pipeline.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import io.grpc.Attributes;
|
||||||
import io.grpc.Grpc;
|
import io.grpc.Grpc;
|
||||||
import io.grpc.Internal;
|
import io.grpc.Internal;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
|
import io.grpc.internal.Channelz;
|
||||||
import io.grpc.internal.GrpcUtil;
|
import io.grpc.internal.GrpcUtil;
|
||||||
import io.netty.channel.ChannelDuplexHandler;
|
import io.netty.channel.ChannelDuplexHandler;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
|
|
@ -62,6 +63,7 @@ import java.util.logging.Logger;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLParameters;
|
import javax.net.ssl.SSLParameters;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common {@link ProtocolNegotiator}s used by gRPC.
|
* 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.
|
// Set sttributes before replace to be sure we pass it before accepting any requests.
|
||||||
handler.handleProtocolNegotiationCompleted(Attributes.newBuilder()
|
handler.handleProtocolNegotiationCompleted(Attributes.newBuilder()
|
||||||
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
||||||
.build());
|
.build(),
|
||||||
|
/*securityInfo=*/ null);
|
||||||
// Just replace this handler with the gRPC handler.
|
// Just replace this handler with the gRPC handler.
|
||||||
ctx.pipeline().replace(this, null, handler);
|
ctx.pipeline().replace(this, null, handler);
|
||||||
}
|
}
|
||||||
|
|
@ -145,14 +148,15 @@ public final class ProtocolNegotiators {
|
||||||
SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
|
SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
|
||||||
if (handshakeEvent.isSuccess()) {
|
if (handshakeEvent.isSuccess()) {
|
||||||
if (NEXT_PROTOCOL_VERSIONS.contains(sslHandler(ctx.pipeline()).applicationProtocol())) {
|
if (NEXT_PROTOCOL_VERSIONS.contains(sslHandler(ctx.pipeline()).applicationProtocol())) {
|
||||||
|
SSLSession session = sslHandler(ctx.pipeline()).engine().getSession();
|
||||||
// Successfully negotiated the protocol.
|
// Successfully negotiated the protocol.
|
||||||
// Notify about completion and pass down SSLSession in attributes.
|
// Notify about completion and pass down SSLSession in attributes.
|
||||||
grpcHandler.handleProtocolNegotiationCompleted(
|
grpcHandler.handleProtocolNegotiationCompleted(
|
||||||
Attributes.newBuilder()
|
Attributes.newBuilder()
|
||||||
.set(Grpc.TRANSPORT_ATTR_SSL_SESSION,
|
.set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
|
||||||
sslHandler(ctx.pipeline()).engine().getSession())
|
|
||||||
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
.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.
|
// Replace this handler with the GRPC handler.
|
||||||
ctx.pipeline().replace(this, null, grpcHandler);
|
ctx.pipeline().replace(this, null, grpcHandler);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -634,13 +638,15 @@ public final class ProtocolNegotiators {
|
||||||
// will fail before we see the userEvent, and the channel is closed down prematurely.
|
// will fail before we see the userEvent, and the channel is closed down prematurely.
|
||||||
ctx.pipeline().addBefore(ctx.name(), null, grpcHandler);
|
ctx.pipeline().addBefore(ctx.name(), null, grpcHandler);
|
||||||
|
|
||||||
|
SSLSession session = handler.engine().getSession();
|
||||||
// Successfully negotiated the protocol.
|
// Successfully negotiated the protocol.
|
||||||
// Notify about completion and pass down SSLSession in attributes.
|
// Notify about completion and pass down SSLSession in attributes.
|
||||||
grpcHandler.handleProtocolNegotiationCompleted(
|
grpcHandler.handleProtocolNegotiationCompleted(
|
||||||
Attributes.newBuilder()
|
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())
|
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
||||||
.build());
|
.build(),
|
||||||
|
new Channelz.Security(new Channelz.Tls(session)));
|
||||||
writeBufferedAndRemove(ctx);
|
writeBufferedAndRemove(ctx);
|
||||||
} else {
|
} else {
|
||||||
Exception ex = new Exception(
|
Exception ex = new Exception(
|
||||||
|
|
@ -686,7 +692,8 @@ public final class ProtocolNegotiators {
|
||||||
Attributes
|
Attributes
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
||||||
.build());
|
.build(),
|
||||||
|
/*securityInfo=*/ null);
|
||||||
super.channelActive(ctx);
|
super.channelActive(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -727,7 +734,8 @@ public final class ProtocolNegotiators {
|
||||||
Attributes
|
Attributes
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
|
||||||
.build());
|
.build(),
|
||||||
|
/*securityInfo=*/ null);
|
||||||
} else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
|
} else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
|
||||||
fail(ctx, unavailableException("HTTP/2 upgrade rejected"));
|
fail(ctx, unavailableException("HTTP/2 upgrade rejected"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ public class NettyServerHandlerTest extends NettyHandlerTestBase<NettyServerHand
|
||||||
handler().setKeepAliveManagerForTest(spyKeepAliveManager);
|
handler().setKeepAliveManagerForTest(spyKeepAliveManager);
|
||||||
|
|
||||||
// Simulate receipt of the connection preface
|
// Simulate receipt of the connection preface
|
||||||
handler().handleProtocolNegotiationCompleted(Attributes.EMPTY);
|
handler().handleProtocolNegotiationCompleted(Attributes.EMPTY, /*securityInfo=*/ null);
|
||||||
channelRead(Http2CodecUtil.connectionPrefaceBuf());
|
channelRead(Http2CodecUtil.connectionPrefaceBuf());
|
||||||
// Simulate receipt of initial remote settings.
|
// Simulate receipt of initial remote settings.
|
||||||
ByteBuf serializedSettings = serializeSettings(new Http2Settings());
|
ByteBuf serializedSettings = serializeSettings(new Http2Settings());
|
||||||
|
|
@ -204,7 +204,7 @@ public class NettyServerHandlerTest extends NettyHandlerTestBase<NettyServerHand
|
||||||
public void transportReadyDelayedUntilConnectionPreface() throws Exception {
|
public void transportReadyDelayedUntilConnectionPreface() throws Exception {
|
||||||
initChannel(new GrpcHttp2ServerHeadersDecoder(GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE));
|
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));
|
verify(transportListener, never()).transportReady(any(Attributes.class));
|
||||||
|
|
||||||
// Simulate receipt of the connection preface
|
// Simulate receipt of the connection preface
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import io.grpc.MethodDescriptor.MethodType;
|
||||||
import io.grpc.Status;
|
import io.grpc.Status;
|
||||||
import io.grpc.Status.Code;
|
import io.grpc.Status.Code;
|
||||||
import io.grpc.StatusException;
|
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.Channelz.SocketStats;
|
||||||
import io.grpc.internal.ClientStreamListener.RpcProgress;
|
import io.grpc.internal.ClientStreamListener.RpcProgress;
|
||||||
import io.grpc.internal.ConnectionClientTransport;
|
import io.grpc.internal.ConnectionClientTransport;
|
||||||
|
|
@ -81,6 +81,8 @@ import java.util.logging.Logger;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import okio.Buffer;
|
import okio.Buffer;
|
||||||
import okio.BufferedSink;
|
import okio.BufferedSink;
|
||||||
|
|
@ -185,6 +187,8 @@ class OkHttpClientTransport implements ConnectionClientTransport {
|
||||||
private final Runnable tooManyPingsRunnable;
|
private final Runnable tooManyPingsRunnable;
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private final TransportTracer transportTracer;
|
private final TransportTracer transportTracer;
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private Channelz.Security securityInfo;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
@ -454,6 +458,7 @@ class OkHttpClientTransport implements ConnectionClientTransport {
|
||||||
Variant variant = new Http2();
|
Variant variant = new Http2();
|
||||||
BufferedSink sink;
|
BufferedSink sink;
|
||||||
Socket sock;
|
Socket sock;
|
||||||
|
SSLSession sslSession = null;
|
||||||
try {
|
try {
|
||||||
if (proxy == null) {
|
if (proxy == null) {
|
||||||
sock = new Socket(address.getAddress(), address.getPort());
|
sock = new Socket(address.getAddress(), address.getPort());
|
||||||
|
|
@ -463,9 +468,11 @@ class OkHttpClientTransport implements ConnectionClientTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sslSocketFactory != null) {
|
if (sslSocketFactory != null) {
|
||||||
sock = OkHttpTlsUpgrader.upgrade(
|
SSLSocket sslSocket = OkHttpTlsUpgrader.upgrade(
|
||||||
sslSocketFactory, hostnameVerifier, sock, getOverridenHost(), getOverridenPort(),
|
sslSocketFactory, hostnameVerifier, sock, getOverridenHost(), getOverridenPort(),
|
||||||
connectionSpec);
|
connectionSpec);
|
||||||
|
sslSession = sslSocket.getSession();
|
||||||
|
sock = sslSocket;
|
||||||
}
|
}
|
||||||
sock.setTcpNoDelay(true);
|
sock.setTcpNoDelay(true);
|
||||||
source = Okio.buffer(Okio.source(sock));
|
source = Okio.buffer(Okio.source(sock));
|
||||||
|
|
@ -475,6 +482,7 @@ class OkHttpClientTransport implements ConnectionClientTransport {
|
||||||
attributes = Attributes
|
attributes = Attributes
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, sock.getRemoteSocketAddress())
|
.set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, sock.getRemoteSocketAddress())
|
||||||
|
.set(Grpc.TRANSPORT_ATTR_SSL_SESSION, sslSession)
|
||||||
.build();
|
.build();
|
||||||
} catch (StatusException e) {
|
} catch (StatusException e) {
|
||||||
startGoAway(0, ErrorCode.INTERNAL_ERROR, e.getStatus());
|
startGoAway(0, ErrorCode.INTERNAL_ERROR, e.getStatus());
|
||||||
|
|
@ -489,10 +497,12 @@ class OkHttpClientTransport implements ConnectionClientTransport {
|
||||||
|
|
||||||
FrameWriter rawFrameWriter;
|
FrameWriter rawFrameWriter;
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
socket = sock;
|
socket = Preconditions.checkNotNull(sock, "socket");
|
||||||
maxConcurrentStreams = Integer.MAX_VALUE;
|
maxConcurrentStreams = Integer.MAX_VALUE;
|
||||||
|
|
||||||
startPendingStreams();
|
startPendingStreams();
|
||||||
|
if (sslSession != null) {
|
||||||
|
securityInfo = new Channelz.Security(new Channelz.Tls(sslSession));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rawFrameWriter = variant.newWriter(sink, true);
|
rawFrameWriter = variant.newWriter(sink, true);
|
||||||
|
|
@ -905,14 +915,23 @@ class OkHttpClientTransport implements ConnectionClientTransport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<SocketStats> getStats() {
|
public ListenableFuture<SocketStats> getStats() {
|
||||||
synchronized (lock) {
|
|
||||||
SettableFuture<SocketStats> ret = SettableFuture.create();
|
SettableFuture<SocketStats> ret = SettableFuture.create();
|
||||||
|
synchronized (lock) {
|
||||||
|
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(
|
ret.set(new SocketStats(
|
||||||
transportTracer.getStats(),
|
transportTracer.getStats(),
|
||||||
socket.getLocalSocketAddress(),
|
socket.getLocalSocketAddress(),
|
||||||
socket.getRemoteSocketAddress(),
|
socket.getRemoteSocketAddress(),
|
||||||
Utils.getSocketOptions(socket),
|
Utils.getSocketOptions(socket),
|
||||||
new Security()));
|
securityInfo));
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ import io.grpc.channelz.v1.ChannelRef;
|
||||||
import io.grpc.channelz.v1.GetServerSocketsResponse;
|
import io.grpc.channelz.v1.GetServerSocketsResponse;
|
||||||
import io.grpc.channelz.v1.GetServersResponse;
|
import io.grpc.channelz.v1.GetServersResponse;
|
||||||
import io.grpc.channelz.v1.GetTopChannelsResponse;
|
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.Server;
|
||||||
import io.grpc.channelz.v1.ServerData;
|
import io.grpc.channelz.v1.ServerData;
|
||||||
import io.grpc.channelz.v1.ServerRef;
|
import io.grpc.channelz.v1.ServerRef;
|
||||||
|
|
@ -61,15 +64,20 @@ import io.grpc.internal.Instrumented;
|
||||||
import io.grpc.internal.WithLogId;
|
import io.grpc.internal.WithLogId;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.ExecutionException;
|
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.
|
* A static utility class for turning internal data structures into protos.
|
||||||
*/
|
*/
|
||||||
final class ChannelzProtoUtil {
|
final class ChannelzProtoUtil {
|
||||||
|
private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName());
|
||||||
|
|
||||||
private ChannelzProtoUtil() {
|
private ChannelzProtoUtil() {
|
||||||
// do not instantiate.
|
// do not instantiate.
|
||||||
}
|
}
|
||||||
|
|
@ -128,11 +136,44 @@ final class ChannelzProtoUtil {
|
||||||
.build();
|
.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) {
|
static Socket toSocket(Instrumented<SocketStats> obj) {
|
||||||
SocketStats socketStats = getFuture(obj.getStats());
|
SocketStats socketStats = getFuture(obj.getStats());
|
||||||
Builder builder = Socket.newBuilder()
|
Builder builder = Socket.newBuilder()
|
||||||
.setRef(toSocketRef(obj))
|
.setRef(toSocketRef(obj))
|
||||||
.setLocal(toAddress(socketStats.local));
|
.setLocal(toAddress(socketStats.local));
|
||||||
|
if (socketStats.security != null) {
|
||||||
|
builder.setSecurity(toSecurity(socketStats.security));
|
||||||
|
}
|
||||||
// listen sockets do not have remote nor data
|
// listen sockets do not have remote nor data
|
||||||
if (socketStats.remote != null) {
|
if (socketStats.remote != null) {
|
||||||
builder.setRemote(toAddress(socketStats.remote));
|
builder.setRemote(toAddress(socketStats.remote));
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,16 @@ package io.grpc.services;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static io.grpc.internal.Channelz.id;
|
import static io.grpc.internal.Channelz.id;
|
||||||
import static org.junit.Assert.assertEquals;
|
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.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.protobuf.Any;
|
import com.google.protobuf.Any;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Int64Value;
|
import com.google.protobuf.Int64Value;
|
||||||
|
import com.google.protobuf.Message;
|
||||||
import com.google.protobuf.util.Durations;
|
import com.google.protobuf.util.Durations;
|
||||||
import com.google.protobuf.util.Timestamps;
|
import com.google.protobuf.util.Timestamps;
|
||||||
import io.grpc.ConnectivityState;
|
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;
|
||||||
import io.grpc.channelz.v1.ChannelData.State;
|
import io.grpc.channelz.v1.ChannelData.State;
|
||||||
import io.grpc.channelz.v1.ChannelRef;
|
import io.grpc.channelz.v1.ChannelRef;
|
||||||
|
import io.grpc.channelz.v1.GetChannelRequest;
|
||||||
import io.grpc.channelz.v1.GetServerSocketsResponse;
|
import io.grpc.channelz.v1.GetServerSocketsResponse;
|
||||||
import io.grpc.channelz.v1.GetServersResponse;
|
import io.grpc.channelz.v1.GetServersResponse;
|
||||||
import io.grpc.channelz.v1.GetTopChannelsResponse;
|
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.Server;
|
||||||
import io.grpc.channelz.v1.ServerData;
|
import io.grpc.channelz.v1.ServerData;
|
||||||
import io.grpc.channelz.v1.ServerRef;
|
import io.grpc.channelz.v1.ServerRef;
|
||||||
|
|
@ -69,6 +77,7 @@ import io.netty.channel.unix.DomainSocketAddress;
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
@ -412,6 +421,59 @@ public final class ChannelzProtoUtilTest {
|
||||||
ChannelzProtoUtil.extractSocketData(socket.getStats().get()));
|
ChannelzProtoUtil.extractSocketData(socket.getStats().get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void toAddress_inet() throws Exception {
|
public void toAddress_inet() throws Exception {
|
||||||
InetSocketAddress inet4 = new InetSocketAddress(Inet4Address.getByName("10.0.0.1"), 1000);
|
InetSocketAddress inet4 = new InetSocketAddress(Inet4Address.getByName("10.0.0.1"), 1000);
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ final class ChannelzTestHelper {
|
||||||
SocketAddress local = new InetSocketAddress("10.0.0.1", 1000);
|
SocketAddress local = new InetSocketAddress("10.0.0.1", 1000);
|
||||||
SocketAddress remote = new InetSocketAddress("10.0.0.2", 1000);
|
SocketAddress remote = new InetSocketAddress("10.0.0.2", 1000);
|
||||||
Channelz.SocketOptions socketOptions = new Channelz.SocketOptions.Builder().build();
|
Channelz.SocketOptions socketOptions = new Channelz.SocketOptions.Builder().build();
|
||||||
|
Security security = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<SocketStats> getStats() {
|
public ListenableFuture<SocketStats> getStats() {
|
||||||
|
|
@ -67,7 +68,7 @@ final class ChannelzTestHelper {
|
||||||
local,
|
local,
|
||||||
remote,
|
remote,
|
||||||
socketOptions,
|
socketOptions,
|
||||||
new Security()));
|
security));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue