HADOOP-12765. HttpServer2 should switch to using the non-blocking SslSelectChannelConnector to prevent performance degradation when handling SSL connections. Contributed by Min Shen. Branch-2 patch contributed by Wei-Chiu Chuang.

(cherry picked from commit dfcbc12026)
(cherry picked from commit 8bc33bf343)
This commit is contained in:
Zhe Zhang 2016-08-23 14:46:08 -07:00
parent 4f43607311
commit 1d01704060
6 changed files with 79 additions and 60 deletions

View File

@ -104,6 +104,11 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-sslengine</artifactId>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>javax.servlet.jsp</groupId> <groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId> <artifactId>jsp-api</artifactId>

View File

@ -59,7 +59,7 @@ import org.apache.hadoop.security.authentication.util.FileSignerSecretProvider;
import org.apache.hadoop.security.authentication.util.RandomSignerSecretProvider; import org.apache.hadoop.security.authentication.util.RandomSignerSecretProvider;
import org.apache.hadoop.security.authentication.util.SignerSecretProvider; import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
import org.apache.hadoop.security.authentication.util.ZKSignerSecretProvider; import org.apache.hadoop.security.authentication.util.ZKSignerSecretProvider;
import org.apache.hadoop.security.ssl.SslSocketConnectorSecure; import org.apache.hadoop.security.ssl.SslSelectChannelConnectorSecure;
import org.apache.hadoop.jmx.JMXJsonServlet; import org.apache.hadoop.jmx.JMXJsonServlet;
import org.apache.hadoop.log.LogLevel; import org.apache.hadoop.log.LogLevel;
import org.apache.hadoop.metrics.MetricsServlet; import org.apache.hadoop.metrics.MetricsServlet;
@ -81,7 +81,7 @@ import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.HandlerCollection; import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.jetty.handler.RequestLogHandler; import org.mortbay.jetty.handler.RequestLogHandler;
import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.security.SslSocketConnector; import org.mortbay.jetty.security.SslSelectChannelConnector;
import org.mortbay.jetty.servlet.AbstractSessionManager; import org.mortbay.jetty.servlet.AbstractSessionManager;
import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.DefaultServlet; import org.mortbay.jetty.servlet.DefaultServlet;
@ -305,29 +305,7 @@ public final class HttpServer2 implements FilterContainer {
if ("http".equals(scheme)) { if ("http".equals(scheme)) {
listener = HttpServer2.createDefaultChannelConnector(); listener = HttpServer2.createDefaultChannelConnector();
} else if ("https".equals(scheme)) { } else if ("https".equals(scheme)) {
SslSocketConnector c = new SslSocketConnectorSecure(); listener = createHttpsChannelConnector();
c.setHeaderBufferSize(1024*64);
c.setNeedClientAuth(needsClientAuth);
c.setKeyPassword(keyPassword);
if (keyStore != null) {
c.setKeystore(keyStore);
c.setKeystoreType(keyStoreType);
c.setPassword(keyStorePassword);
}
if (trustStore != null) {
c.setTruststore(trustStore);
c.setTruststoreType(trustStoreType);
c.setTrustPassword(trustStorePassword);
}
if(null != excludeCiphers && !excludeCiphers.isEmpty()) {
c.setExcludeCipherSuites(excludeCiphers.split(","));
LOG.info("Excluded Cipher List:" + excludeCiphers);
}
listener = c;
} else { } else {
throw new HadoopIllegalArgumentException( throw new HadoopIllegalArgumentException(
@ -340,6 +318,32 @@ public final class HttpServer2 implements FilterContainer {
server.loadListeners(); server.loadListeners();
return server; return server;
} }
private Connector createHttpsChannelConnector() {
SslSelectChannelConnector c = new SslSelectChannelConnectorSecure();
configureChannelConnector(c);
c.setNeedClientAuth(needsClientAuth);
c.setKeyPassword(keyPassword);
if (keyStore != null) {
c.setKeystore(keyStore);
c.setKeystoreType(keyStoreType);
c.setPassword(keyStorePassword);
}
if (trustStore != null) {
c.setTruststore(trustStore);
c.setTruststoreType(trustStoreType);
c.setTrustPassword(trustStorePassword);
}
if(null != excludeCiphers && !excludeCiphers.isEmpty()) {
c.setExcludeCipherSuites(excludeCiphers.split(","));
LOG.info("Excluded Cipher List:" + excludeCiphers);
}
return c;
}
} }
private HttpServer2(final Builder b) throws IOException { private HttpServer2(final Builder b) throws IOException {
@ -506,21 +510,25 @@ public final class HttpServer2 implements FilterContainer {
} }
} }
@InterfaceAudience.Private private static void configureChannelConnector(SelectChannelConnector c) {
public static Connector createDefaultChannelConnector() { c.setLowResourceMaxIdleTime(10000);
SelectChannelConnector ret = new SelectChannelConnectorWithSafeStartup(); c.setAcceptQueueSize(128);
ret.setLowResourceMaxIdleTime(10000); c.setResolveNames(false);
ret.setAcceptQueueSize(128); c.setUseDirectBuffers(false);
ret.setResolveNames(false);
ret.setUseDirectBuffers(false);
if(Shell.WINDOWS) { if(Shell.WINDOWS) {
// result of setting the SO_REUSEADDR flag is different on Windows // result of setting the SO_REUSEADDR flag is different on Windows
// http://msdn.microsoft.com/en-us/library/ms740621(v=vs.85).aspx // http://msdn.microsoft.com/en-us/library/ms740621(v=vs.85).aspx
// without this 2 NN's can start on the same machine and listen on // without this 2 NN's can start on the same machine and listen on
// the same port with indeterminate routing of incoming requests to them // the same port with indeterminate routing of incoming requests to them
ret.setReuseAddress(false); c.setReuseAddress(false);
} }
ret.setHeaderBufferSize(1024*64); c.setHeaderBufferSize(1024*64);
}
@InterfaceAudience.Private
public static Connector createDefaultChannelConnector() {
SelectChannelConnector ret = new SelectChannelConnectorWithSafeStartup();
configureChannelConnector(ret);
return ret; return ret;
} }

View File

@ -18,41 +18,41 @@
package org.apache.hadoop.security.ssl; package org.apache.hadoop.security.ssl;
import org.mortbay.jetty.security.SslSocketConnector;
import javax.net.ssl.SSLServerSocket;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList; import java.util.ArrayList;
import javax.net.ssl.SSLEngine;
import org.apache.hadoop.classification.InterfaceAudience;
import org.mortbay.jetty.security.SslSelectChannelConnector;
/** /**
* This subclass of the Jetty SslSocketConnector exists solely to control * This subclass of the Jetty SslSelectChannelConnector exists solely to
* the TLS protocol versions allowed. This is fallout from the POODLE * control the TLS protocol versions allowed. This is fallout from the
* vulnerability (CVE-2014-3566), which requires that SSLv3 be disabled. * POODLE vulnerability (CVE-2014-3566), which requires that SSLv3 be disabled.
* Only TLS 1.0 and later protocols are allowed. * Only TLS 1.0 and later protocols are allowed.
*/ */
public class SslSocketConnectorSecure extends SslSocketConnector { @InterfaceAudience.Private
public class SslSelectChannelConnectorSecure extends SslSelectChannelConnector {
public SslSocketConnectorSecure() { public SslSelectChannelConnectorSecure() {
super(); super();
} }
/** /**
* Create a new ServerSocket that will not accept SSLv3 connections, * Disable SSLv3 protocol.
* but will accept TLSv1.x connections.
*/ */
protected ServerSocket newServerSocket(String host, int port,int backlog) @Override
throws IOException { protected SSLEngine createSSLEngine() throws IOException {
SSLServerSocket socket = (SSLServerSocket) SSLEngine engine = super.createSSLEngine();
super.newServerSocket(host, port, backlog);
ArrayList<String> nonSSLProtocols = new ArrayList<String>(); ArrayList<String> nonSSLProtocols = new ArrayList<String>();
for (String p : socket.getEnabledProtocols()) { for (String p : engine.getEnabledProtocols()) {
if (!p.contains("SSLv3")) { if (!p.contains("SSLv3")) {
nonSSLProtocols.add(p); nonSSLProtocols.add(p);
} }
} }
socket.setEnabledProtocols(nonSSLProtocols.toArray( engine.setEnabledProtocols(nonSSLProtocols.toArray(
new String[nonSSLProtocols.size()])); new String[nonSSLProtocols.size()]));
return socket; return engine;
} }
} }

View File

@ -18,14 +18,15 @@
package org.apache.hadoop.crypto.key.kms.server; package org.apache.hadoop.crypto.key.kms.server;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.kms.KMSRESTConstants; import org.apache.hadoop.crypto.key.kms.KMSRESTConstants;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.ssl.SslSocketConnectorSecure; import org.apache.hadoop.security.ssl.SslSelectChannelConnectorSecure;
import org.mortbay.jetty.Connector; import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server; import org.mortbay.jetty.Server;
import org.mortbay.jetty.security.SslSocketConnector; import org.mortbay.jetty.security.SslSelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext; import org.mortbay.jetty.webapp.WebAppContext;
import java.io.File; import java.io.File;
@ -57,7 +58,7 @@ public class MiniKMS {
server.getConnectors()[0].setHost(host); server.getConnectors()[0].setHost(host);
server.getConnectors()[0].setPort(port); server.getConnectors()[0].setPort(port);
} else { } else {
SslSocketConnector c = new SslSocketConnectorSecure(); SslSelectChannelConnector c = new SslSelectChannelConnectorSecure();
c.setHost(host); c.setHost(host);
c.setPort(port); c.setPort(port);
c.setNeedClientAuth(false); c.setNeedClientAuth(false);
@ -75,7 +76,7 @@ public class MiniKMS {
private static URL getJettyURL(Server server) { private static URL getJettyURL(Server server) {
boolean ssl = server.getConnectors()[0].getClass() boolean ssl = server.getConnectors()[0].getClass()
== SslSocketConnectorSecure.class; == SslSelectChannelConnectorSecure.class;
try { try {
String scheme = (ssl) ? "https" : "http"; String scheme = (ssl) ? "https" : "http";
return new URL(scheme + "://" + return new URL(scheme + "://" +

View File

@ -24,14 +24,14 @@ import java.net.ServerSocket;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.apache.hadoop.security.ssl.SslSocketConnectorSecure; import org.apache.hadoop.security.ssl.SslSelectChannelConnectorSecure;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.MethodRule; import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement; import org.junit.runners.model.Statement;
import org.mortbay.jetty.Connector; import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server; import org.mortbay.jetty.Server;
import org.mortbay.jetty.security.SslSocketConnector; import org.mortbay.jetty.security.SslSelectChannelConnector;
public class TestJettyHelper implements MethodRule { public class TestJettyHelper implements MethodRule {
private boolean ssl; private boolean ssl;
@ -93,7 +93,7 @@ public class TestJettyHelper implements MethodRule {
server.getConnectors()[0].setHost(host); server.getConnectors()[0].setHost(host);
server.getConnectors()[0].setPort(port); server.getConnectors()[0].setPort(port);
} else { } else {
SslSocketConnector c = new SslSocketConnectorSecure(); SslSelectChannelConnector c = new SslSelectChannelConnectorSecure();
c.setHost(host); c.setHost(host);
c.setPort(port); c.setPort(port);
c.setNeedClientAuth(false); c.setNeedClientAuth(false);

View File

@ -473,6 +473,11 @@
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
<version>6.1.26</version> <version>6.1.26</version>
</dependency> </dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-sslengine</artifactId>
<version>6.1.26</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.tomcat.embed</groupId> <groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId> <artifactId>tomcat-embed-core</artifactId>