From 823bb5dda88b2a9a1374c2a52af8d0e9b6bb801a Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 23 Oct 2018 15:27:37 -0700 Subject: [PATCH] MAPREDUCE-4669. MRAM web UI does not work with HTTPS. (Contributed by Robert Kanter) --- .../hadoop/security/ssl/KeyStoreTestUtil.java | 105 ++++++++++++ .../hadoop-mapreduce-client-app/pom.xml | 5 + .../v2/app/client/MRClientService.java | 15 +- .../mapreduce/v2/app/webapp/TestAMWebApp.java | 158 +++++++++++++++++- .../mapreduce/v2/util/MRWebAppUtil.java | 6 +- .../apache/hadoop/mapreduce/MRJobConfig.java | 22 +++ .../src/main/resources/mapred-default.xml | 21 +++ hadoop-project/pom.xml | 5 + .../apache/hadoop/yarn/webapp/WebApps.java | 25 ++- 9 files changed, 352 insertions(+), 10 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java index 1870b22809..0d30e6e410 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java @@ -32,15 +32,20 @@ import java.io.IOException; import java.io.Writer; import java.math.BigInteger; +import java.net.Socket; import java.net.URL; import java.security.GeneralSecurityException; import java.security.Key; +import java.security.KeyManagementException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; import java.security.SecureRandom; import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.HashMap; @@ -50,8 +55,15 @@ import java.security.NoSuchProviderException; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.bouncycastle.x509.X509V1CertificateGenerator; public class KeyStoreTestUtil { @@ -538,4 +550,97 @@ public static Configuration getSslConfig(){ sslConf.set(SSLFactory.SSL_CLIENT_CONF_KEY, sslClientConfFile); return sslConf; } + + /** + * Configures the passed in {@link HttpsURLConnection} to allow all SSL + * certificates. + * + * @param httpsConn The HttpsURLConnection to configure + * @throws KeyManagementException + * @throws NoSuchAlgorithmException + */ + public static void setAllowAllSSL(HttpsURLConnection httpsConn) + throws KeyManagementException, NoSuchAlgorithmException { + setAllowAllSSL(httpsConn, null); + } + + /** + * Configures the passed in {@link HttpsURLConnection} to allow all SSL + * certificates. Also presents a client certificate. + * + * @param httpsConn The HttpsURLConnection to configure + * @param clientCert The client certificate to present + * @param clientKeyPair The KeyPair for the client certificate + * @throws KeyManagementException + * @throws NoSuchAlgorithmException + */ + public static void setAllowAllSSL(HttpsURLConnection httpsConn, + X509Certificate clientCert, KeyPair clientKeyPair) + throws KeyManagementException, NoSuchAlgorithmException { + X509KeyManager km = new X509KeyManager() { + @Override + public String[] getClientAliases(String s, Principal[] principals) { + return new String[]{"client"}; + } + + @Override + public String chooseClientAlias(String[] strings, + Principal[] principals, Socket socket) { + return "client"; + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return null; + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, + Socket socket) { + return null; + } + + @Override + public X509Certificate[] getCertificateChain(String s) { + return new X509Certificate[]{clientCert}; + } + + @Override + public PrivateKey getPrivateKey(String s) { + return clientKeyPair.getPrivate(); + } + }; + setAllowAllSSL(httpsConn, km); + } + + private static void setAllowAllSSL(HttpsURLConnection httpsConn, + KeyManager km) throws KeyManagementException, NoSuchAlgorithmException { + // Create a TrustManager that trusts anything + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + } + } + }; + KeyManager[] kms = (km == null) ? null : new KeyManager[]{km}; + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(kms, trustAllCerts, new SecureRandom()); + httpsConn.setSSLSocketFactory(sc.getSocketFactory()); + // Don't check the hostname + httpsConn.setHostnameVerifier(new NoopHostnameVerifier()); + } } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/pom.xml b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/pom.xml index fab6466f7c..2d7ea982ac 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/pom.xml +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/pom.xml @@ -108,6 +108,11 @@ bcpkix-jdk15on test + + com.github.stefanbirkner + system-rules + test + diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/client/MRClientService.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/client/MRClientService.java index b2dc8ad962..328a4b98b4 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/client/MRClientService.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/client/MRClientService.java @@ -25,6 +25,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; +import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.http.HttpConfig.Policy; import org.apache.hadoop.ipc.Server; import org.apache.hadoop.mapreduce.JobACL; @@ -136,14 +137,18 @@ protected void serviceStart() throws Exception { server.getListenerAddress().getPort()); LOG.info("Instantiated MRClientService at " + this.bindAddress); try { - // Explicitly disabling SSL for map reduce task as we can't allow MR users - // to gain access to keystore file for opening SSL listener. We can trust - // RM/NM to issue SSL certificates but definitely not MR-AM as it is - // running in user-land. + HttpConfig.Policy httpPolicy = conf.getBoolean( + MRJobConfig.MR_AM_WEBAPP_HTTPS_ENABLED, + MRJobConfig.DEFAULT_MR_AM_WEBAPP_HTTPS_ENABLED) + ? Policy.HTTPS_ONLY : Policy.HTTP_ONLY; + boolean needsClientAuth = conf.getBoolean( + MRJobConfig.MR_AM_WEBAPP_HTTPS_CLIENT_AUTH, + MRJobConfig.DEFAULT_MR_AM_WEBAPP_HTTPS_CLIENT_AUTH); webApp = WebApps.$for("mapreduce", AppContext.class, appContext, "ws") - .withHttpPolicy(conf, Policy.HTTP_ONLY) + .withHttpPolicy(conf, httpPolicy) .withPortRange(conf, MRJobConfig.MR_AM_WEBAPP_PORT_RANGE) + .needsClientAuth(needsClientAuth) .start(new AMWebApp()); } catch (Exception e) { LOG.error("Webapps failed to start. Ignoring for now:", e); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebApp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebApp.java index 21d37c82c0..d55c9299c1 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebApp.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebApp.java @@ -22,15 +22,23 @@ import static org.junit.Assert.assertEquals; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.InputStream; import java.net.HttpURLConnection; +import java.net.SocketException; import java.net.URL; +import java.security.KeyPair; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLException; +import org.apache.hadoop.mapreduce.MRJobConfig; +import org.apache.hadoop.security.ssl.KeyStoreTestUtil; import org.junit.Assert; import org.apache.hadoop.conf.Configuration; @@ -57,13 +65,26 @@ import org.apache.hadoop.yarn.webapp.test.WebAppTests; import org.apache.hadoop.yarn.webapp.util.WebAppUtils; import org.apache.http.HttpStatus; +import org.junit.After; +import org.junit.Rule; import org.junit.Test; import com.google.common.net.HttpHeaders; import com.google.inject.Injector; +import org.junit.contrib.java.lang.system.EnvironmentVariables; public class TestAMWebApp { + private static final File TEST_DIR = new File( + System.getProperty("test.build.data", + System.getProperty("java.io.tmpdir")), + TestAMWebApp.class.getName()); + + @After + public void tearDown() { + TEST_DIR.delete(); + } + @Test public void testAppControllerIndex() { AppContext ctx = new MockAppContext(0, 1, 1, 1); Injector injector = WebAppTests.createMockInjector(AppContext.class, ctx); @@ -179,7 +200,7 @@ protected ClientService createClientService(AppContext context) { } }; Configuration conf = new Configuration(); - // MR is explicitly disabling SSL, even though setting as HTTPS_ONLY + // MR is explicitly disabling SSL, even though YARN setting as HTTPS_ONLY conf.set(YarnConfiguration.YARN_HTTP_POLICY_KEY, Policy.HTTPS_ONLY.name()); Job job = app.submit(conf); @@ -201,14 +222,145 @@ protected ClientService createClientService(AppContext context) { (HttpURLConnection) httpsUrl.openConnection(); httpsConn.getInputStream(); Assert.fail("https:// is not accessible, expected to fail"); - } catch (Exception e) { - Assert.assertTrue(e instanceof SSLException); + } catch (SSLException e) { + // expected } app.waitForState(job, JobState.SUCCEEDED); app.verifyCompleted(); } + @Rule + public final EnvironmentVariables environmentVariables + = new EnvironmentVariables(); + + @Test + public void testMRWebAppSSLEnabled() throws Exception { + MRApp app = new MRApp(2, 2, true, this.getClass().getName(), true) { + @Override + protected ClientService createClientService(AppContext context) { + return new MRClientService(context); + } + }; + Configuration conf = new Configuration(); + conf.setBoolean(MRJobConfig.MR_AM_WEBAPP_HTTPS_ENABLED, true); + + KeyPair keyPair = KeyStoreTestUtil.generateKeyPair("RSA"); + Certificate cert = KeyStoreTestUtil.generateCertificate( + "CN=foo", keyPair, 5, "SHA512WITHRSA"); + File keystoreFile = new File(TEST_DIR, "server.keystore"); + keystoreFile.getParentFile().mkdirs(); + KeyStoreTestUtil.createKeyStore(keystoreFile.getAbsolutePath(), "password", + "server", keyPair.getPrivate(), cert); + environmentVariables.set("KEYSTORE_FILE_LOCATION", + keystoreFile.getAbsolutePath()); + environmentVariables.set("KEYSTORE_PASSWORD", "password"); + + Job job = app.submit(conf); + + String hostPort = + NetUtils.getHostPortString(((MRClientService) app.getClientService()) + .getWebApp().getListenerAddress()); + // https:// should be accessible + URL httpsUrl = new URL("https://" + hostPort); + HttpsURLConnection httpsConn = + (HttpsURLConnection) httpsUrl.openConnection(); + KeyStoreTestUtil.setAllowAllSSL(httpsConn); + + InputStream in = httpsConn.getInputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copyBytes(in, out, 1024); + Assert.assertTrue(out.toString().contains("MapReduce Application")); + + // http:// is not accessible. + URL httpUrl = new URL("http://" + hostPort); + try { + HttpURLConnection httpConn = + (HttpURLConnection) httpUrl.openConnection(); + httpConn.getResponseCode(); + Assert.fail("http:// is not accessible, expected to fail"); + } catch (SocketException e) { + // expected + } + + app.waitForState(job, JobState.SUCCEEDED); + app.verifyCompleted(); + + keystoreFile.delete(); + } + + @Test + public void testMRWebAppSSLEnabledWithClientAuth() throws Exception { + MRApp app = new MRApp(2, 2, true, this.getClass().getName(), true) { + @Override + protected ClientService createClientService(AppContext context) { + return new MRClientService(context); + } + }; + Configuration conf = new Configuration(); + conf.setBoolean(MRJobConfig.MR_AM_WEBAPP_HTTPS_ENABLED, true); + conf.setBoolean(MRJobConfig.MR_AM_WEBAPP_HTTPS_CLIENT_AUTH, true); + + KeyPair keyPair = KeyStoreTestUtil.generateKeyPair("RSA"); + Certificate cert = KeyStoreTestUtil.generateCertificate( + "CN=foo", keyPair, 5, "SHA512WITHRSA"); + File keystoreFile = new File(TEST_DIR, "server.keystore"); + keystoreFile.getParentFile().mkdirs(); + KeyStoreTestUtil.createKeyStore(keystoreFile.getAbsolutePath(), "password", + "server", keyPair.getPrivate(), cert); + environmentVariables.set("KEYSTORE_FILE_LOCATION", + keystoreFile.getAbsolutePath()); + environmentVariables.set("KEYSTORE_PASSWORD", "password"); + + KeyPair clientKeyPair = KeyStoreTestUtil.generateKeyPair("RSA"); + X509Certificate clientCert = KeyStoreTestUtil.generateCertificate( + "CN=bar", clientKeyPair, 5, "SHA512WITHRSA"); + File truststoreFile = new File(TEST_DIR, "client.truststore"); + truststoreFile.getParentFile().mkdirs(); + KeyStoreTestUtil.createTrustStore(truststoreFile.getAbsolutePath(), + "password", "client", clientCert); + environmentVariables.set("TRUSTSTORE_FILE_LOCATION", + truststoreFile.getAbsolutePath()); + environmentVariables.set("TRUSTSTORE_PASSWORD", "password"); + + Job job = app.submit(conf); + + String hostPort = + NetUtils.getHostPortString(((MRClientService) app.getClientService()) + .getWebApp().getListenerAddress()); + // https:// should be accessible + URL httpsUrl = new URL("https://" + hostPort); + HttpsURLConnection httpsConn = + (HttpsURLConnection) httpsUrl.openConnection(); + KeyStoreTestUtil.setAllowAllSSL(httpsConn, clientCert, clientKeyPair); + + InputStream in = httpsConn.getInputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copyBytes(in, out, 1024); + Assert.assertTrue(out.toString().contains("MapReduce Application")); + + // Try with wrong client cert + KeyPair otherClientKeyPair = KeyStoreTestUtil.generateKeyPair("RSA"); + X509Certificate otherClientCert = KeyStoreTestUtil.generateCertificate( + "CN=bar", otherClientKeyPair, 5, "SHA512WITHRSA"); + KeyStoreTestUtil.setAllowAllSSL(httpsConn, otherClientCert, clientKeyPair); + + try { + HttpURLConnection httpConn = + (HttpURLConnection) httpsUrl.openConnection(); + httpConn.getResponseCode(); + Assert.fail("Wrong client certificate, expected to fail"); + } catch (SSLException e) { + // expected + } + + app.waitForState(job, JobState.SUCCEEDED); + app.verifyCompleted(); + + keystoreFile.delete(); + truststoreFile.delete(); + } + static String webProxyBase = null; public static class TestAMFilterInitializer extends AmFilterInitializer { diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/MRWebAppUtil.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/MRWebAppUtil.java index 6f2e21f1d3..8a6e138711 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/MRWebAppUtil.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/MRWebAppUtil.java @@ -24,6 +24,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.mapreduce.JobID; +import org.apache.hadoop.mapreduce.MRJobConfig; import org.apache.hadoop.mapreduce.TypeConverter; import org.apache.hadoop.mapreduce.v2.jobhistory.JHAdminConfig; import org.apache.hadoop.net.NetUtils; @@ -178,6 +179,9 @@ private static String getDefaultJHSWebappURLWithoutScheme() { } public static String getAMWebappScheme(Configuration conf) { - return "http://"; + return conf.getBoolean( + MRJobConfig.MR_AM_WEBAPP_HTTPS_ENABLED, + MRJobConfig.DEFAULT_MR_AM_WEBAPP_HTTPS_ENABLED) + ? "https://" : "http://"; } } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java index ca18bfe5a2..3592b3d75d 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/MRJobConfig.java @@ -762,6 +762,28 @@ public interface MRJobConfig { */ String MR_AM_WEBAPP_PORT_RANGE = MR_AM_PREFIX + "webapp.port-range"; + /** + * True if the MR AM should use HTTPS for its webapp. If + * {@link org.apache.hadoop.yarn.conf.YarnConfiguration#RM_APPLICATION_HTTPS_POLICY} + * is set to LENIENT or STRICT, the MR AM will automatically use the + * keystore provided by YARN with a certificate for the MR AM webapp, unless + * provided by the user. + */ + String MR_AM_WEBAPP_HTTPS_ENABLED = MR_AM_PREFIX + "webapp.https.enabled"; + boolean DEFAULT_MR_AM_WEBAPP_HTTPS_ENABLED = false; + + /** + * True if the MR AM webapp should require client HTTPS authentication (i.e. + * the proxy server (RM) should present a certificate to the MR AM webapp). + * If {@link org.apache.hadoop.yarn.conf.YarnConfiguration#RM_APPLICATION_HTTPS_POLICY} + * is set to LENIENT or STRICT, the MR AM will automatically use the + * truststore provided by YARN with the RMs certificate, unless provided by + * the user. + */ + String MR_AM_WEBAPP_HTTPS_CLIENT_AUTH = + MR_AM_PREFIX + "webapp.https.client.auth"; + boolean DEFAULT_MR_AM_WEBAPP_HTTPS_CLIENT_AUTH = false; + /** Enable blacklisting of nodes in the job.*/ public static final String MR_AM_JOB_NODE_BLACKLISTING_ENABLE = MR_AM_PREFIX + "job.node-blacklisting.enable"; diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/resources/mapred-default.xml b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/resources/mapred-default.xml index e5da41f9e8..ccc9c3df60 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/resources/mapred-default.xml +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/resources/mapred-default.xml @@ -1481,6 +1481,27 @@ For example 50000-50050,50100-50200 + + yarn.app.mapreduce.am.webapp.https.enabled + false + True if the MR AM should use HTTPS for its webapp. If + yarn.resourcemanager.application-https.policy is set to LENIENT or STRICT, + the MR AM will automatically use the keystore provided by YARN with a + certificate for the MR AM webapp, unless provided by the user. + + + + + yarn.app.mapreduce.am.webapp.https.client.auth + false + True if the MR AM webapp should require client HTTPS + authentication (i.e. the proxy server (RM) should present a certificate to + the MR AM webapp). If yarn.resourcemanager.application-https.policy is set + to LENIENT or STRICT, the MR AM will automatically use the truststore + provided by YARN with the RMs certificate, unless provided by the user. + + + yarn.app.mapreduce.am.job.committer.cancel-timeout 60000 diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml index e5f496585e..3a44e8c189 100644 --- a/hadoop-project/pom.xml +++ b/hadoop-project/pom.xml @@ -1073,6 +1073,11 @@ junit 4.11 + + com.github.stefanbirkner + system-rules + 1.18.0 + commons-collections commons-collections diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java index 0e9f0a77be..ced5cedd1e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApps.java @@ -95,6 +95,7 @@ static class ServletStruct { boolean findPort = false; Configuration conf; Policy httpPolicy = null; + boolean needsClientAuth = false; String portRangeConfigKey = null; boolean devMode = false; private String spnegoPrincipalKey; @@ -174,6 +175,11 @@ public Builder withHttpPolicy(Configuration conf, Policy httpPolicy) { return this; } + public Builder needsClientAuth(boolean needsClientAuth) { + this.needsClientAuth = needsClientAuth; + return this; + } + /** * Set port range config key and associated configuration object. * @param config configuration. @@ -335,7 +341,24 @@ public void setup() { } if (httpScheme.equals(WebAppUtils.HTTPS_PREFIX)) { - WebAppUtils.loadSslConfiguration(builder, conf); + String amKeystoreLoc = System.getenv("KEYSTORE_FILE_LOCATION"); + if (amKeystoreLoc != null) { + LOG.info("Setting keystore location to " + amKeystoreLoc); + String password = System.getenv("KEYSTORE_PASSWORD"); + builder.keyStore(amKeystoreLoc, password, "jks"); + } else { + LOG.info("Loading standard ssl config"); + WebAppUtils.loadSslConfiguration(builder, conf); + } + builder.needsClientAuth(needsClientAuth); + if (needsClientAuth) { + String amTruststoreLoc = System.getenv("TRUSTSTORE_FILE_LOCATION"); + if (amTruststoreLoc != null) { + LOG.info("Setting truststore location to " + amTruststoreLoc); + String password = System.getenv("TRUSTSTORE_PASSWORD"); + builder.trustStore(amTruststoreLoc, password, "jks"); + } + } } HttpServer2 server = builder.build();