HADOOP-15676. Cleanup TestSSLHttpServer. Contributed by Szilard Nemeth.

This commit is contained in:
Xiao Chen 2018-10-11 15:08:22 -07:00
parent c05b260069
commit 64f2b32d57

View File

@ -24,8 +24,9 @@
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException; import java.security.GeneralSecurityException;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
@ -34,6 +35,7 @@
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.http.TestHttpServer.EchoServlet;
import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.net.NetUtils; import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.ssl.KeyStoreTestUtil; import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
@ -60,23 +62,34 @@ public class TestSSLHttpServer extends HttpServerFunctionalTest {
LoggerFactory.getLogger(TestSSLHttpServer.class); LoggerFactory.getLogger(TestSSLHttpServer.class);
private static final String HTTPS_CIPHER_SUITES_KEY = "https.cipherSuites"; private static final String HTTPS_CIPHER_SUITES_KEY = "https.cipherSuites";
private static final String JAVAX_NET_DEBUG_KEY = "javax.net.debug"; private static final String JAVAX_NET_DEBUG_KEY = "javax.net.debug";
private static Configuration conf; private static final String SSL_SERVER_KEYSTORE_PROP_PREFIX = "ssl.server" +
".keystore";
private static final String SSL_SERVER_TRUSTSTORE_PROP_PREFIX = "ssl.server" +
".truststore";
private static final String SERVLET_NAME_LONGHEADER = "longheader";
private static final String SERVLET_PATH_LONGHEADER =
"/" + SERVLET_NAME_LONGHEADER;
private static final String SERVLET_NAME_ECHO = "echo";
private static final String SERVLET_PATH_ECHO = "/" + SERVLET_NAME_ECHO;
private static HttpServer2 server; private static HttpServer2 server;
private static String keystoresDir; private static String keystoreDir;
private static String sslConfDir; private static String sslConfDir;
private static SSLFactory clientSslFactory; private static SSLFactory clientSslFactory;
private static String cipherSuitesPropertyValue; private static String cipherSuitesPropertyValue;
private static String sslDebugPropertyValue; private static String sslDebugPropertyValue;
private static final String excludeCiphers = "TLS_ECDHE_RSA_WITH_RC4_128_SHA," private static final String EXCLUDED_CIPHERS =
"TLS_ECDHE_RSA_WITH_RC4_128_SHA,"
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, \n" + "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, \n"
+ "SSL_RSA_WITH_DES_CBC_SHA," + "SSL_RSA_WITH_DES_CBC_SHA,"
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA, " + "SSL_DHE_RSA_WITH_DES_CBC_SHA, "
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5,\t \n" + "SSL_RSA_EXPORT_WITH_RC4_40_MD5,\t \n"
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA," + "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,"
+ "SSL_RSA_WITH_RC4_128_MD5 \t"; + "SSL_RSA_WITH_RC4_128_MD5 \t";
private static final String oneEnabledCiphers = excludeCiphers private static final String ONE_ENABLED_CIPHERS = EXCLUDED_CIPHERS
+ ",TLS_RSA_WITH_AES_128_CBC_SHA"; + ",TLS_RSA_WITH_AES_128_CBC_SHA";
private static final String exclusiveEnabledCiphers private static final String EXCLUSIVE_ENABLED_CIPHERS
= "\tTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, \n" = "\tTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, \n"
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA," + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,"
+ "TLS_RSA_WITH_AES_128_CBC_SHA," + "TLS_RSA_WITH_AES_128_CBC_SHA,"
@ -90,49 +103,54 @@ public static void setup() throws Exception {
turnOnSSLDebugLogging(); turnOnSSLDebugLogging();
storeHttpsCipherSuites(); storeHttpsCipherSuites();
conf = new Configuration(); Configuration conf = new Configuration();
conf.setInt(HttpServer2.HTTP_MAX_THREADS_KEY, 10); conf.setInt(HttpServer2.HTTP_MAX_THREADS_KEY, 10);
File base = new File(BASEDIR); File base = new File(BASEDIR);
FileUtil.fullyDelete(base); FileUtil.fullyDelete(base);
base.mkdirs(); base.mkdirs();
keystoresDir = new File(BASEDIR).getAbsolutePath(); keystoreDir = new File(BASEDIR).getAbsolutePath();
sslConfDir = KeyStoreTestUtil.getClasspathDir(TestSSLHttpServer.class); sslConfDir = KeyStoreTestUtil.getClasspathDir(TestSSLHttpServer.class);
KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, conf, false, true, KeyStoreTestUtil.setupSSLConfig(keystoreDir, sslConfDir, conf, false, true,
excludeCiphers); EXCLUDED_CIPHERS);
Configuration sslConf = KeyStoreTestUtil.getSslConfig(); Configuration sslConf = KeyStoreTestUtil.getSslConfig();
clientSslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, sslConf); clientSslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, sslConf);
clientSslFactory.init(); clientSslFactory.init();
server = new HttpServer2.Builder() setupServer(conf, sslConf);
.setName("test")
.addEndpoint(new URI("https://localhost"))
.setConf(conf)
.keyPassword(sslConf.get("ssl.server.keystore.keypassword"))
.keyStore(sslConf.get("ssl.server.keystore.location"),
sslConf.get("ssl.server.keystore.password"),
sslConf.get("ssl.server.keystore.type", "jks"))
.trustStore(sslConf.get("ssl.server.truststore.location"),
sslConf.get("ssl.server.truststore.password"),
sslConf.get("ssl.server.truststore.type", "jks"))
.excludeCiphers(
sslConf.get("ssl.server.exclude.cipher.list")).build();
server.addServlet("echo", "/echo", TestHttpServer.EchoServlet.class);
server.addServlet("longheader", "/longheader", LongHeaderServlet.class);
server.start();
baseUrl = new URL("https://" baseUrl = new URL("https://"
+ NetUtils.getHostPortString(server.getConnectorAddress(0))); + NetUtils.getHostPortString(server.getConnectorAddress(0)));
LOG.info("HTTP server started: " + baseUrl); LOG.info("HTTP server started: " + baseUrl);
} }
private static void setupServer(Configuration conf, Configuration sslConf)
throws IOException, URISyntaxException {
server = new HttpServer2.Builder().setName("test")
.addEndpoint(new URI("https://localhost")).setConf(conf)
.keyPassword(
sslConf.get(SSL_SERVER_KEYSTORE_PROP_PREFIX + ".keypassword"))
.keyStore(sslConf.get(SSL_SERVER_KEYSTORE_PROP_PREFIX + ".location"),
sslConf.get(SSL_SERVER_KEYSTORE_PROP_PREFIX + ".password"),
sslConf.get(SSL_SERVER_KEYSTORE_PROP_PREFIX + ".type", "jks"))
.trustStore(
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".location"),
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".password"),
sslConf.get(SSL_SERVER_TRUSTSTORE_PROP_PREFIX + ".type", "jks"))
.excludeCiphers(sslConf.get("ssl.server.exclude.cipher.list")).build();
server.addServlet(SERVLET_NAME_ECHO, SERVLET_PATH_ECHO, EchoServlet.class);
server.addServlet(SERVLET_NAME_LONGHEADER, SERVLET_PATH_LONGHEADER,
LongHeaderServlet.class);
server.start();
}
@AfterClass @AfterClass
public static void cleanup() throws Exception { public static void cleanup() throws Exception {
server.stop(); server.stop();
FileUtil.fullyDelete(new File(BASEDIR)); FileUtil.fullyDelete(new File(BASEDIR));
KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir); KeyStoreTestUtil.cleanupSSLConfig(keystoreDir, sslConfDir);
clientSslFactory.destroy(); clientSslFactory.destroy();
restoreHttpsCipherSuites(); restoreHttpsCipherSuites();
restoreSSLDebugLogging(); restoreSSLDebugLogging();
@ -181,11 +199,27 @@ private static void restoreSSLDebugLogging() {
} }
} }
private HttpsURLConnection getConnectionWithSSLSocketFactory(URL url,
String ciphers) throws IOException, GeneralSecurityException {
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
SSLSocketFactory sslSocketFactory = clientSslFactory
.createSSLSocketFactory();
LOG.info("Creating " +
PreferredCipherSSLSocketFactory.class.getCanonicalName() +
" with ciphers: " + ciphers);
PreferredCipherSSLSocketFactory cipherSSLSocketFactory
= new PreferredCipherSSLSocketFactory(sslSocketFactory,
StringUtils.getTrimmedStrings(ciphers));
conn.setSSLSocketFactory(cipherSSLSocketFactory);
return conn;
}
@Test @Test
public void testEcho() throws Exception { public void testEcho() throws Exception {
assertEquals("a:b\nc:d\n", readOut(new URL(baseUrl, "/echo?a=b&c=d"))); assertEquals("a:b\nc:d\n",
assertEquals("a:b\nc<:d\ne:>\n", readOut(new URL(baseUrl, readFromURL(new URL(baseUrl, SERVLET_PATH_ECHO + "?a=b&c=d")));
"/echo?a=b&c<=d&e=>"))); assertEquals("a:b\nc&lt;:d\ne:&gt;\n",
readFromURL(new URL(baseUrl, SERVLET_PATH_ECHO + "?a=b&c<=d&e=>")));
} }
/** /**
@ -195,15 +229,20 @@ public void testEcho() throws Exception {
*/ */
@Test @Test
public void testLongHeader() throws Exception { public void testLongHeader() throws Exception {
URL url = new URL(baseUrl, "/longheader"); URL url = new URL(baseUrl, SERVLET_PATH_LONGHEADER);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory()); conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
testLongHeader(conn); testLongHeader(conn);
} }
private static String readOut(URL url) throws Exception { private static String readFromURL(URL url) throws Exception {
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory()); conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
return readFromConnection(conn);
}
private static String readFromConnection(HttpsURLConnection conn)
throws IOException {
InputStream in = conn.getInputStream(); InputStream in = conn.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copyBytes(in, out, 1024); IOUtils.copyBytes(in, out, 1024);
@ -218,21 +257,15 @@ private static String readOut(URL url) throws Exception {
*/ */
@Test @Test
public void testExcludedCiphers() throws Exception { public void testExcludedCiphers() throws Exception {
URL url = new URL(baseUrl, "/echo?a=b&c=d"); URL url = new URL(baseUrl, SERVLET_PATH_ECHO + "?a=b&c=d");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); HttpsURLConnection conn = getConnectionWithSSLSocketFactory(url,
SSLSocketFactory sslSocketF = clientSslFactory.createSSLSocketFactory(); EXCLUDED_CIPHERS);
PrefferedCipherSSLSocketFactory testPreferredCipherSSLSocketF assertFalse("excludedCipher list is empty", EXCLUDED_CIPHERS.isEmpty());
= new PrefferedCipherSSLSocketFactory(sslSocketF,
StringUtils.getTrimmedStrings(excludeCiphers));
conn.setSSLSocketFactory(testPreferredCipherSSLSocketF);
assertFalse("excludedCipher list is empty", excludeCiphers.isEmpty());
try { try {
InputStream in = conn.getInputStream(); readFromConnection(conn);
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copyBytes(in, out, 1024);
fail("No Ciphers in common, SSLHandshake must fail."); fail("No Ciphers in common, SSLHandshake must fail.");
} catch (SSLHandshakeException ex) { } catch (SSLHandshakeException ex) {
LOG.info("No Ciphers in common, expected succesful test result.", ex); LOG.info("No Ciphers in common, expected successful test result.", ex);
} }
} }
@ -242,25 +275,7 @@ public void testExcludedCiphers() throws Exception {
*/ */
@Test @Test
public void testOneEnabledCiphers() throws Exception { public void testOneEnabledCiphers() throws Exception {
URL url = new URL(baseUrl, "/echo?a=b&c=d"); testEnabledCiphers(ONE_ENABLED_CIPHERS);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
SSLSocketFactory sslSocketF = clientSslFactory.createSSLSocketFactory();
PrefferedCipherSSLSocketFactory testPreferredCipherSSLSocketF
= new PrefferedCipherSSLSocketFactory(sslSocketF,
StringUtils.getTrimmedStrings(oneEnabledCiphers));
conn.setSSLSocketFactory(testPreferredCipherSSLSocketF);
assertFalse("excludedCipher list is empty", oneEnabledCiphers.isEmpty());
try {
InputStream in = conn.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copyBytes(in, out, 1024);
assertEquals(out.toString(), "a:b\nc:d\n");
LOG.info("Atleast one additional enabled cipher than excluded ciphers,"
+ " expected successful test result.");
} catch (SSLHandshakeException ex) {
fail("Atleast one additional cipher available for successful handshake."
+ " Unexpected test failure: " + ex);
}
} }
/** Test verifies that mutually exclusive server's disabled cipher suites and /** Test verifies that mutually exclusive server's disabled cipher suites and
@ -268,33 +283,25 @@ public void testOneEnabledCiphers() throws Exception {
*/ */
@Test @Test
public void testExclusiveEnabledCiphers() throws Exception { public void testExclusiveEnabledCiphers() throws Exception {
URL url = new URL(baseUrl, "/echo?a=b&c=d"); testEnabledCiphers(EXCLUSIVE_ENABLED_CIPHERS);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
SSLSocketFactory sslSocketF = clientSslFactory.createSSLSocketFactory();
PrefferedCipherSSLSocketFactory testPreferredCipherSSLSocketF
= new PrefferedCipherSSLSocketFactory(sslSocketF,
StringUtils.getTrimmedStrings(exclusiveEnabledCiphers));
conn.setSSLSocketFactory(testPreferredCipherSSLSocketF);
assertFalse("excludedCipher list is empty",
exclusiveEnabledCiphers.isEmpty());
try {
InputStream in = conn.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copyBytes(in, out, 1024);
assertEquals(out.toString(), "a:b\nc:d\n");
LOG.info("Atleast one additional enabled cipher than excluded ciphers,"
+ " expected successful test result.");
} catch (SSLHandshakeException ex) {
fail("Atleast one additional cipher available for successful handshake."
+ " Unexpected test failure: " + ex);
}
} }
private class PrefferedCipherSSLSocketFactory extends SSLSocketFactory { private void testEnabledCiphers(String ciphers) throws
IOException, GeneralSecurityException {
URL url = new URL(baseUrl, SERVLET_PATH_ECHO + "?a=b&c=d");
HttpsURLConnection conn = getConnectionWithSSLSocketFactory(url, ciphers);
assertFalse("excludedCipher list is empty", ciphers.isEmpty());
String out = readFromConnection(conn);
assertEquals(out, "a:b\nc:d\n");
LOG.info("At least one additional enabled cipher than excluded ciphers,"
+ " expected successful test result.");
}
private class PreferredCipherSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegateSocketFactory; private final SSLSocketFactory delegateSocketFactory;
private final String[] enabledCipherSuites; private final String[] enabledCipherSuites;
public PrefferedCipherSSLSocketFactory(SSLSocketFactory sslSocketFactory, PreferredCipherSSLSocketFactory(SSLSocketFactory sslSocketFactory,
String[] pEnabledCipherSuites) { String[] pEnabledCipherSuites) {
delegateSocketFactory = sslSocketFactory; delegateSocketFactory = sslSocketFactory;
if (null != pEnabledCipherSuites && pEnabledCipherSuites.length > 0) { if (null != pEnabledCipherSuites && pEnabledCipherSuites.length > 0) {
@ -319,31 +326,24 @@ public Socket createSocket(Socket socket, String string, int i, boolean bln)
throws IOException { throws IOException {
SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket( SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(
socket, string, i, bln); socket, string, i, bln);
if (null != enabledCipherSuites) { setEnabledCipherSuites(sslSocket);
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
}
return sslSocket; return sslSocket;
} }
@Override @Override
public Socket createSocket(String string, int i) throws IOException, public Socket createSocket(String string, int i) throws IOException {
UnknownHostException {
SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket( SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(
string, i); string, i);
if (null != enabledCipherSuites) { setEnabledCipherSuites(sslSocket);
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
}
return sslSocket; return sslSocket;
} }
@Override @Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) public Socket createSocket(String string, int i, InetAddress ia, int i1)
throws IOException, UnknownHostException { throws IOException {
SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket( SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(
string, i, ia, i1); string, i, ia, i1);
if (null != enabledCipherSuites) { setEnabledCipherSuites(sslSocket);
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
}
return sslSocket; return sslSocket;
} }
@ -351,9 +351,7 @@ public Socket createSocket(String string, int i, InetAddress ia, int i1)
public Socket createSocket(InetAddress ia, int i) throws IOException { public Socket createSocket(InetAddress ia, int i) throws IOException {
SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(ia, SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(ia,
i); i);
if (null != enabledCipherSuites) { setEnabledCipherSuites(sslSocket);
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
}
return sslSocket; return sslSocket;
} }
@ -362,10 +360,14 @@ public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1)
throws IOException { throws IOException {
SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(ia, SSLSocket sslSocket = (SSLSocket) delegateSocketFactory.createSocket(ia,
i, ia1, i1); i, ia1, i1);
setEnabledCipherSuites(sslSocket);
return sslSocket;
}
private void setEnabledCipherSuites(SSLSocket sslSocket) {
if (null != enabledCipherSuites) { if (null != enabledCipherSuites) {
sslSocket.setEnabledCipherSuites(enabledCipherSuites); sslSocket.setEnabledCipherSuites(enabledCipherSuites);
} }
return sslSocket;
} }
} }
} }