diff --git hadoop-common-project/hadoop-common/pom.xml hadoop-common-project/hadoop-common/pom.xml
index 42554daaddb..e96acb2c80c 100644
--- hadoop-common-project/hadoop-common/pom.xml
+++ hadoop-common-project/hadoop-common/pom.xml
@@ -298,7 +298,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java
index 6a9527af375..27edfb06e93 100644
--- hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java
+++ hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Credentials.java
@@ -59,6 +59,16 @@
@InterfaceAudience.Public
@InterfaceStability.Evolving
public class Credentials implements Writable {
+
+ public final static Text YARN_APPLICATION_AM_KEYSTORE =
+ new Text("yarn.application.am.keystore");
+ public final static Text YARN_APPLICATION_AM_KEYSTORE_PASSWORD =
+ new Text("yarn.application.am.keystore.password");
+ public final static Text YARN_APPLICATION_AM_TRUSTSTORE =
+ new Text("yarn.application.am.truststore");
+ public final static Text YARN_APPLICATION_AM_TRUSTSTORE_PASSWORD =
+ new Text("yarn.application.am.truststore.password");
+
public enum SerializedFormat {
WRITABLE((byte) 0x00),
PROTOBUF((byte) 0x01);
diff --git hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java
index 898c94ec33b..6beb3a99746 100644
--- hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java
+++ hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java
@@ -25,21 +25,27 @@
import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
import org.apache.hadoop.test.GenericTestUtils;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
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;
@@ -49,7 +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 {
@@ -127,9 +141,16 @@ public static void createKeyStore(String filename,
String password, String alias,
Key privateKey, Certificate cert)
throws GeneralSecurityException, IOException {
- KeyStore ks = createEmptyKeyStore();
- ks.setKeyEntry(alias, privateKey, password.toCharArray(),
+ createKeyStore(filename, password, alias, privateKey,
new Certificate[]{cert});
+ }
+
+ public static void createKeyStore(String filename,
+ String password, String alias,
+ Key privateKey, Certificate[] certs)
+ throws GeneralSecurityException, IOException {
+ KeyStore ks = createEmptyKeyStore();
+ ks.setKeyEntry(alias, privateKey, password.toCharArray(), certs);
saveKeyStore(ks, filename, password);
}
@@ -174,6 +195,14 @@ public static void createTrustStore(String filename,
saveKeyStore(ks, filename, password);
}
+ public static KeyStore bytesToKeyStore(byte[] bytes, String password)
+ throws GeneralSecurityException, IOException {
+ KeyStore keyStore = createEmptyKeyStore();
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ keyStore.load(bais, password.toCharArray());
+ return keyStore;
+ }
+
public static void cleanupSSLConfig(String keystoresDir, String sslConfDir)
throws Exception {
File f = new File(keystoresDir + "/clientKS.jks");
@@ -521,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 hadoop-common-project/hadoop-kms/pom.xml hadoop-common-project/hadoop-kms/pom.xml
index 03a68c5d357..ec491618173 100644
--- hadoop-common-project/hadoop-kms/pom.xml
+++ hadoop-common-project/hadoop-kms/pom.xml
@@ -171,7 +171,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-hdds/server-scm/pom.xml hadoop-hdds/server-scm/pom.xml
index 1330be8fe35..b96f4310d7a 100644
--- hadoop-hdds/server-scm/pom.xml
+++ hadoop-hdds/server-scm/pom.xml
@@ -102,7 +102,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml
index eaf5c529d32..dd90a27ccfe 100644
--- hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml
+++ hadoop-hdfs-project/hadoop-hdfs-httpfs/pom.xml
@@ -204,7 +204,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-hdfs-project/hadoop-hdfs-nfs/pom.xml hadoop-hdfs-project/hadoop-hdfs-nfs/pom.xml
index 42d2c008a60..cf2625d303a 100644
--- hadoop-hdfs-project/hadoop-hdfs-nfs/pom.xml
+++ hadoop-hdfs-project/hadoop-hdfs-nfs/pom.xml
@@ -165,7 +165,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-hdfs-project/hadoop-hdfs/pom.xml hadoop-hdfs-project/hadoop-hdfs/pom.xml
index fcd5ae1940f..b5e10c434f3 100644
--- hadoop-hdfs-project/hadoop-hdfs/pom.xml
+++ hadoop-hdfs-project/hadoop-hdfs/pom.xml
@@ -190,7 +190,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/pom.xml hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/pom.xml
index aec0fe3e577..dc8f372a54f 100644
--- hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/pom.xml
+++ hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/pom.xml
@@ -108,7 +108,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-ozone/ozone-manager/pom.xml hadoop-ozone/ozone-manager/pom.xml
index 50e7b456ae9..1d03576b4a0 100644
--- hadoop-ozone/ozone-manager/pom.xml
+++ hadoop-ozone/ozone-manager/pom.xml
@@ -49,7 +49,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd">
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-project/pom.xml hadoop-project/pom.xml
index 387a3da0f80..e87cbf814f0 100644
--- hadoop-project/pom.xml
+++ hadoop-project/pom.xml
@@ -1378,9 +1378,8 @@
org.bouncycastle
- bcprov-jdk16
- 1.46
- test
+ bcprov-jdk15on
+ 1.59
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
index bbf877f0390..88c38cda009 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
@@ -2104,6 +2104,24 @@ public static boolean isAclEnabled(Configuration conf) {
public static final long DEFAULT_RM_APPLICATION_MONITOR_INTERVAL_MS =
3000;
+ /**
+ * Sets the policy the RM should use when enforcing HTTPS certificates for AM
+ * Web Servers. Possible values are:
+ *
+ *
OFF - the RM will do nothing special.
+ *
OPTIONAL - the RM will generate and provide a keystore and truststore
+ * whenever an AM gives it an HTTPS tracking URL. It will still accept HTTP
+ * URLs though.
+ *
REQUIRED - the RM will always generate and provide a keystore and
+ * truststore and require that the tracking URL for all applications is
+ * HTTPS.
+ *
+ */
+ public static final String RM_APPLICATION_HTTPS_POLICY =
+ RM_PREFIX + "application-https.policy";
+
+ public static final String DEFAULT_RM_APPLICATION_HTTPS_POLICY = "OPTIONAL";
+
/**
* Interval of time the linux container executor should try cleaning up
* cgroups entry when cleaning up a container. This is required due to what
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/pom.xml hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/pom.xml
index eddcbaae678..54e2cda8ae9 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/pom.xml
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/pom.xml
@@ -139,7 +139,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
@@ -187,6 +187,11 @@
com.fasterxml.jackson.jaxrsjackson-jaxrs-json-provider
+
+ org.bouncycastle
+ bcprov-jdk15on
+ compile
+
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/pom.xml hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/pom.xml
index 0527095d8d4..f0d97952197 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/pom.xml
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/pom.xml
@@ -177,7 +177,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java
index 27224a599e3..e6ad85a6b90 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java
@@ -214,6 +214,8 @@ public int launchContainer(ContainerStartContext ctx)
Container container = ctx.getContainer();
Path nmPrivateContainerScriptPath = ctx.getNmPrivateContainerScriptPath();
Path nmPrivateTokensPath = ctx.getNmPrivateTokensPath();
+ Path nmPrivateKeystorePath = ctx.getNmPrivateKeystorePath();
+ Path nmPrivateTruststorePath = ctx.getNmPrivateTruststorePath();
String user = ctx.getUser();
Path containerWorkDir = ctx.getContainerWorkDir();
List localDirs = ctx.getLocalDirs();
@@ -249,6 +251,18 @@ public int launchContainer(ContainerStartContext ctx)
new Path(containerWorkDir, ContainerLaunch.FINAL_CONTAINER_TOKENS_FILE);
copyFile(nmPrivateTokensPath, tokenDst, user);
+ if (nmPrivateKeystorePath != null) {
+ Path keystoreDst =
+ new Path(containerWorkDir, ContainerLaunch.KEYSTORE_FILE);
+ copyFile(nmPrivateKeystorePath, keystoreDst, user);
+ }
+
+ if (nmPrivateTruststorePath != null) {
+ Path truststoreDst =
+ new Path(containerWorkDir, ContainerLaunch.TRUSTSTORE_FILE);
+ copyFile(nmPrivateTruststorePath, truststoreDst, user);
+ }
+
// copy launch script to work dir
Path launchDst =
new Path(containerWorkDir, ContainerLaunch.CONTAINER_SCRIPT);
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java
index 03b88a44997..53fed3f71b5 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java
@@ -669,6 +669,10 @@ private ContainerRuntimeContext buildContainerRuntimeContext(
ctx.getNmPrivateContainerScriptPath())
.setExecutionAttribute(NM_PRIVATE_TOKENS_PATH,
ctx.getNmPrivateTokensPath())
+ .setExecutionAttribute(NM_PRIVATE_KEYSTORE_PATH,
+ ctx.getNmPrivateKeystorePath())
+ .setExecutionAttribute(NM_PRIVATE_TRUSTSTORE_PATH,
+ ctx.getNmPrivateTruststorePath())
.setExecutionAttribute(PID_FILE_PATH, pidFilePath)
.setExecutionAttribute(LOCAL_DIRS, ctx.getLocalDirs())
.setExecutionAttribute(LOG_DIRS, ctx.getLogDirs())
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java
index 04295e13678..eb650da432c 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java
@@ -21,6 +21,7 @@
import static org.apache.hadoop.fs.CreateFlag.CREATE;
import static org.apache.hadoop.fs.CreateFlag.OVERWRITE;
+import org.apache.hadoop.io.Text;
import org.apache.hadoop.yarn.server.nodemanager.executor.DeletionAsUserContext;
import java.io.DataOutputStream;
@@ -112,6 +113,9 @@
public static final String FINAL_CONTAINER_TOKENS_FILE = "container_tokens";
+ public static final String KEYSTORE_FILE = "yarn_provided.keystore";
+ public static final String TRUSTSTORE_FILE = "yarn_provided.truststore";
+
private static final String PID_FILE_NAME_FMT = "%s.pid";
private static final String EXIT_CODE_FILE_SUFFIX = ".exitcode";
@@ -234,6 +238,12 @@ public Integer call() {
getContainerPrivateDir(appIdStr, containerIdStr) + Path.SEPARATOR
+ String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT,
containerIdStr));
+ Path nmPrivateKeystorePath = dirsHandler.getLocalPathForWrite(
+ getContainerPrivateDir(appIdStr, containerIdStr) + Path.SEPARATOR
+ + KEYSTORE_FILE);
+ Path nmPrivateTruststorePath = dirsHandler.getLocalPathForWrite(
+ getContainerPrivateDir(appIdStr, containerIdStr) + Path.SEPARATOR
+ + TRUSTSTORE_FILE);
Path nmPrivateClasspathJarDir = dirsHandler.getLocalPathForWrite(
getContainerPrivateDir(appIdStr, containerIdStr));
@@ -268,6 +278,39 @@ public Integer call() {
appDirs.add(new Path(appsdir, appIdStr));
}
+ byte[] keystore = container.getCredentials().getSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE);
+ if (keystore != null) {
+ try (DataOutputStream keystoreOutStream =
+ lfs.create(nmPrivateKeystorePath, EnumSet.of(CREATE, OVERWRITE))) {
+ keystoreOutStream.write(keystore);
+ environment.put("KEYSTORE_FILE_LOCATION", new Path(containerWorkDir,
+ ContainerLaunch.KEYSTORE_FILE).toUri().getPath());
+ environment.put("KEYSTORE_PASSWORD",
+ new String(container.getCredentials().getSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE_PASSWORD)));
+ }
+ } else {
+ nmPrivateKeystorePath = null;
+ }
+ byte[] truststore = container.getCredentials().getSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE);
+ if (truststore != null) {
+ try (DataOutputStream truststoreOutStream =
+ lfs.create(nmPrivateTruststorePath,
+ EnumSet.of(CREATE, OVERWRITE))) {
+ truststoreOutStream.write(truststore);
+ environment.put("TRUSTSTORE_FILE_LOCATION",
+ new Path(containerWorkDir,
+ ContainerLaunch.TRUSTSTORE_FILE).toUri().getPath());
+ environment.put("TRUSTSTORE_PASSWORD",
+ new String(container.getCredentials().getSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE_PASSWORD)));
+ }
+ } else {
+ nmPrivateTruststorePath = null;
+ }
+
// Set the token location too.
addToEnvMap(environment, nmEnvVars,
ApplicationConstants.CONTAINER_TOKEN_FILE_ENV_NAME,
@@ -305,6 +348,8 @@ public Integer call() {
.setLocalizedResources(localResources)
.setNmPrivateContainerScriptPath(nmPrivateContainerScriptPath)
.setNmPrivateTokensPath(nmPrivateTokensPath)
+ .setNmPrivateKeystorePath(nmPrivateKeystorePath)
+ .setNmPrivateTruststorePath(nmPrivateTruststorePath)
.setUser(user)
.setAppId(appIdStr)
.setContainerWorkDir(containerWorkDir)
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerRelaunch.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerRelaunch.java
index a34ed625050..c724d28d027 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerRelaunch.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerRelaunch.java
@@ -20,6 +20,7 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.yarn.api.records.ContainerExitStatus;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.event.Dispatcher;
@@ -81,6 +82,12 @@ public Integer call() {
getNmPrivateContainerScriptPath(appIdStr, containerIdStr);
Path nmPrivateTokensPath =
getNmPrivateTokensPath(appIdStr, containerIdStr);
+ Path nmPrivateKeystorePath = (container.getCredentials().getSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE) == null) ? null :
+ getNmPrivateKeystorePath(appIdStr, containerIdStr);
+ Path nmPrivateTruststorePath = (container.getCredentials().getSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE) == null) ? null :
+ getNmPrivateTruststorePath(appIdStr, containerIdStr);
pidFilePath = getPidFilePath(appIdStr, containerIdStr);
LOG.info("Relaunch container with "
@@ -112,6 +119,8 @@ public Integer call() {
.setLocalizedResources(localResources)
.setNmPrivateContainerScriptPath(nmPrivateContainerScriptPath)
.setNmPrivateTokensPath(nmPrivateTokensPath)
+ .setNmPrivateKeystorePath(nmPrivateKeystorePath)
+ .setNmPrivateTruststorePath(nmPrivateTruststorePath)
.setUser(container.getUser())
.setAppId(appIdStr)
.setContainerWorkDir(containerWorkDir)
@@ -173,6 +182,20 @@ private Path getNmPrivateTokensPath(String appIdStr,
containerIdStr));
}
+ private Path getNmPrivateKeystorePath(String appIdStr,
+ String containerIdStr) throws IOException {
+ return dirsHandler.getLocalPathForRead(
+ getContainerPrivateDir(appIdStr, containerIdStr) + Path.SEPARATOR
+ + ContainerLaunch.KEYSTORE_FILE);
+ }
+
+ private Path getNmPrivateTruststorePath(String appIdStr,
+ String containerIdStr) throws IOException {
+ return dirsHandler.getLocalPathForRead(
+ getContainerPrivateDir(appIdStr, containerIdStr) + Path.SEPARATOR
+ + ContainerLaunch.TRUSTSTORE_FILE);
+ }
+
private Path getPidFilePath(String appIdStr,
String containerIdStr) throws IOException {
return dirsHandler.getLocalPathForRead(
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DefaultLinuxContainerRuntime.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DefaultLinuxContainerRuntime.java
index b5c933aff23..a235e606f16 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DefaultLinuxContainerRuntime.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DefaultLinuxContainerRuntime.java
@@ -23,6 +23,7 @@
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor;
import org.apache.hadoop.yarn.server.nodemanager.Context;
@@ -94,8 +95,17 @@ public void launchContainer(ContainerRuntimeContext ctx)
ctx.getExecutionAttribute(CONTAINER_WORK_DIR).toString(),
ctx.getExecutionAttribute(NM_PRIVATE_CONTAINER_SCRIPT_PATH).toUri()
.getPath(),
- ctx.getExecutionAttribute(NM_PRIVATE_TOKENS_PATH).toUri().getPath(),
- ctx.getExecutionAttribute(PID_FILE_PATH).toString(),
+ ctx.getExecutionAttribute(NM_PRIVATE_TOKENS_PATH).toUri().getPath());
+ Path keystorePath = ctx.getExecutionAttribute(NM_PRIVATE_KEYSTORE_PATH);
+ if (keystorePath != null) {
+ launchOp.appendArgs("--https",
+ keystorePath.toUri().getPath(),
+ ctx.getExecutionAttribute(NM_PRIVATE_TRUSTSTORE_PATH)
+ .toUri().getPath());
+ } else {
+ launchOp.appendArgs("--http");
+ }
+ launchOp.appendArgs(ctx.getExecutionAttribute(PID_FILE_PATH).toString(),
StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
ctx.getExecutionAttribute(LOCAL_DIRS)),
StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java
index 88e6c91dac7..f90ae9bcaef 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java
@@ -1163,19 +1163,28 @@ private PrivilegedOperation buildLaunchOp(ContainerRuntimeContext ctx,
launchOp.appendArgs(runAsUser, ctx.getExecutionAttribute(USER),
Integer.toString(PrivilegedOperation
- .RunAsUserCommand.LAUNCH_DOCKER_CONTAINER.getValue()),
- ctx.getExecutionAttribute(APPID),
- containerIdStr,
- containerWorkDir.toString(),
- nmPrivateContainerScriptPath.toUri().getPath(),
- ctx.getExecutionAttribute(NM_PRIVATE_TOKENS_PATH).toUri().getPath(),
- ctx.getExecutionAttribute(PID_FILE_PATH).toString(),
- StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
- localDirs),
- StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
- logDirs),
- commandFile,
- resourcesOpts);
+ .RunAsUserCommand.LAUNCH_DOCKER_CONTAINER.getValue()),
+ ctx.getExecutionAttribute(APPID),
+ containerIdStr,
+ containerWorkDir.toString(),
+ nmPrivateContainerScriptPath.toUri().getPath(),
+ ctx.getExecutionAttribute(NM_PRIVATE_TOKENS_PATH).toUri().getPath());
+ Path keystorePath = ctx.getExecutionAttribute(NM_PRIVATE_KEYSTORE_PATH);
+ if (keystorePath != null) {
+ launchOp.appendArgs("--https",
+ keystorePath.toUri().getPath(),
+ ctx.getExecutionAttribute(NM_PRIVATE_TRUSTSTORE_PATH)
+ .toUri().getPath());
+ } else {
+ launchOp.appendArgs("--http");
+ }
+ launchOp.appendArgs(ctx.getExecutionAttribute(PID_FILE_PATH).toString(),
+ StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
+ localDirs),
+ StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
+ logDirs),
+ commandFile,
+ resourcesOpts);
String tcCommandFile = ctx.getExecutionAttribute(TC_COMMAND_FILE);
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/LinuxContainerRuntimeConstants.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/LinuxContainerRuntimeConstants.java
index 2f4aad49a04..fc86b17ed9e 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/LinuxContainerRuntimeConstants.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/LinuxContainerRuntimeConstants.java
@@ -58,6 +58,10 @@ private LinuxContainerRuntimeConstants() {
Attribute.attribute(Path.class, "nm_private_container_script_path");
public static final Attribute NM_PRIVATE_TOKENS_PATH = Attribute
.attribute(Path.class, "nm_private_tokens_path");
+ public static final Attribute NM_PRIVATE_KEYSTORE_PATH = Attribute
+ .attribute(Path.class, "nm_private_keystore_path");
+ public static final Attribute NM_PRIVATE_TRUSTSTORE_PATH = Attribute
+ .attribute(Path.class, "nm_private_truststore_path");
public static final Attribute PID_FILE_PATH = Attribute.attribute(
Path.class, "pid_file_path");
public static final Attribute LOCAL_DIRS = Attribute.attribute(
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java
index ff415727c08..444a1e0a64c 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java
@@ -40,6 +40,8 @@
private final Map> localizedResources;
private final Path nmPrivateContainerScriptPath;
private final Path nmPrivateTokensPath;
+ private final Path nmPrivateKeystorePath;
+ private final Path nmPrivateTruststorePath;
private final String user;
private final String appId;
private final Path containerWorkDir;
@@ -57,6 +59,8 @@
private Map> localizedResources;
private Path nmPrivateContainerScriptPath;
private Path nmPrivateTokensPath;
+ private Path nmPrivateKeystorePath;
+ private Path nmPrivateTruststorePath;
private String user;
private String appId;
private Path containerWorkDir;
@@ -94,6 +98,16 @@ public Builder setNmPrivateTokensPath(Path nmPrivateTokensPath) {
return this;
}
+ public Builder setNmPrivateKeystorePath(Path nmPrivateKeystorePath) {
+ this.nmPrivateKeystorePath = nmPrivateKeystorePath;
+ return this;
+ }
+
+ public Builder setNmPrivateTruststorePath(Path nmPrivateTruststorePath) {
+ this.nmPrivateTruststorePath = nmPrivateTruststorePath;
+ return this;
+ }
+
public Builder setUser(String user) {
this.user = user;
return this;
@@ -161,6 +175,8 @@ private ContainerStartContext(Builder builder) {
this.localizedResources = builder.localizedResources;
this.nmPrivateContainerScriptPath = builder.nmPrivateContainerScriptPath;
this.nmPrivateTokensPath = builder.nmPrivateTokensPath;
+ this.nmPrivateKeystorePath = builder.nmPrivateKeystorePath;
+ this.nmPrivateTruststorePath = builder.nmPrivateTruststorePath;
this.user = builder.user;
this.appId = builder.appId;
this.containerWorkDir = builder.containerWorkDir;
@@ -194,6 +210,14 @@ public Path getNmPrivateTokensPath() {
return this.nmPrivateTokensPath;
}
+ public Path getNmPrivateKeystorePath() {
+ return this.nmPrivateKeystorePath;
+ }
+
+ public Path getNmPrivateTruststorePath() {
+ return this.nmPrivateTruststorePath;
+ }
+
public String getUser() {
return this.user;
}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c
index effeeeece3b..575e3e5cbf2 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c
@@ -596,6 +596,16 @@ char *get_container_credentials_file(const char* work_dir) {
CREDENTIALS_FILENAME);
}
+char *get_container_keystore_file(const char* work_dir) {
+ return concatenate("%s/%s", "container keystore", 2, work_dir,
+ KEYSTORE_FILENAME);
+}
+
+char *get_container_truststore_file(const char* work_dir) {
+ return concatenate("%s/%s", "container truststore", 2, work_dir,
+ TRUSTSTORE_FILENAME);
+}
+
/**
* Get the app log directory under the given log_root
*/
@@ -1388,9 +1398,11 @@ int exec_docker_command(char *docker_command, char **argv,
}
int create_script_paths(const char *work_dir,
- const char *script_name, const char *cred_file,
- char** script_file_dest, char** cred_file_dest,
- int* container_file_source, int* cred_file_source ) {
+ const char *script_name, const char *cred_file, const int https,
+ const char *keystore_file, const char *truststore_file,
+ char** script_file_dest, char** cred_file_dest, char** keystore_file_dest,
+ char** truststore_file_dest, int* container_file_source,
+ int* cred_file_source, int* keystore_file_source, int* truststore_file_source) {
int exit_code = -1;
*script_file_dest = get_container_launcher_file(work_dir);
@@ -1408,6 +1420,24 @@ int create_script_paths(const char *work_dir,
fflush(ERRORFILE);
return exit_code;
}
+
+ if (https == 1) {
+ *keystore_file_dest = get_container_keystore_file(work_dir);
+ if (NULL == keystore_file_dest) {
+ exit_code = OUT_OF_MEMORY;
+ fprintf(ERRORFILE, "Could not create keystore_file_dest");
+ fflush(ERRORFILE);
+ return exit_code;
+ }
+ *truststore_file_dest = get_container_truststore_file(work_dir);
+ if (NULL == truststore_file_dest) {
+ exit_code = OUT_OF_MEMORY;
+ fprintf(ERRORFILE, "Could not create truststore_file_dest");
+ fflush(ERRORFILE);
+ return exit_code;
+ }
+ }
+
// open launch script
*container_file_source = open_file_as_nm(script_name);
if (*container_file_source == -1) {
@@ -1425,6 +1455,25 @@ int create_script_paths(const char *work_dir,
return exit_code;
}
+ if (https == 1) {
+ // open keystore
+ *keystore_file_source = open_file_as_nm(keystore_file);
+ if (*keystore_file_source == -1) {
+ exit_code = INVALID_ARGUMENT_NUMBER;
+ fprintf(ERRORFILE, "Could not open keystore file");
+ fflush(ERRORFILE);
+ return exit_code;
+ }
+ // open truststore
+ *truststore_file_source = open_file_as_nm(truststore_file);
+ if (*truststore_file_source == -1) {
+ exit_code = INVALID_ARGUMENT_NUMBER;
+ fprintf(ERRORFILE, "Could not open truststore file");
+ fflush(ERRORFILE);
+ return exit_code;
+ }
+ }
+
exit_code = 0;
return exit_code;
}
@@ -1432,10 +1481,14 @@ int create_script_paths(const char *work_dir,
int create_local_dirs(const char * user, const char *app_id,
const char *container_id, const char *work_dir,
const char *script_name, const char *cred_file,
+ const int https,
+ const char *keystore_file, const char *truststore_file,
char* const* local_dirs,
char* const* log_dirs, int effective_user,
char* script_file_dest, char* cred_file_dest,
- int container_file_source, int cred_file_source) {
+ char* keystore_file_dest, char* truststore_file_dest,
+ int container_file_source, int cred_file_source,
+ int keystore_file_source, int truststore_file_source) {
int exit_code = -1;
// create the user directory on all disks
int result = initialize_user(user, local_dirs);
@@ -1494,6 +1547,26 @@ int create_local_dirs(const char * user, const char *app_id,
goto cleanup;
}
+ if (https == 1) {
+ // Copy keystore file to permissions 600
+ if (copy_file(keystore_file_source, keystore_file, keystore_file_dest,
+ S_IRUSR | S_IWUSR) != 0) {
+ exit_code = COULD_NOT_CREATE_KEYSTORE_FILE;
+ fprintf(ERRORFILE, "Could not copy file");
+ fflush(ERRORFILE);
+ goto cleanup;
+ }
+
+ // Copy truststore file to permissions 600
+ if (copy_file(truststore_file_source, truststore_file, truststore_file_dest,
+ S_IRUSR | S_IWUSR) != 0) {
+ exit_code = COULD_NOT_CREATE_TRUSTSTORE_FILE;
+ fprintf(ERRORFILE, "Could not copy file");
+ fflush(ERRORFILE);
+ goto cleanup;
+ }
+ }
+
if (chdir(work_dir) != 0) {
fprintf(ERRORFILE, "Can't change directory to %s -%s\n", work_dir,
strerror(errno));
@@ -1531,6 +1604,8 @@ int create_user_filecache_dirs(const char * user, char* const* local_dirs) {
int launch_docker_container_as_user(const char * user, const char *app_id,
const char *container_id, const char *work_dir,
const char *script_name, const char *cred_file,
+ const int https,
+ const char *keystore_file, const char *truststore_file,
const char *pid_file, char* const* local_dirs,
char* const* log_dirs, const char *command_file,
const char *resources_key,
@@ -1538,12 +1613,16 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
int exit_code = -1;
char *script_file_dest = NULL;
char *cred_file_dest = NULL;
+ char *keystore_file_dest = NULL;
+ char *truststore_file_dest = NULL;
char *exit_code_file = NULL;
char *docker_command_with_binary = NULL;
char *docker_inspect_command = NULL;
char *docker_inspect_exitcode_command = NULL;
int container_file_source =-1;
int cred_file_source = -1;
+ int keystore_file_source = -1;
+ int truststore_file_source = -1;
int use_entry_point = 0;
gid_t user_gid = getegid();
@@ -1554,8 +1633,8 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
fprintf(LOGFILE, "Creating script paths...\n");
exit_code = create_script_paths(
- work_dir, script_name, cred_file, &script_file_dest, &cred_file_dest,
- &container_file_source, &cred_file_source);
+ work_dir, script_name, cred_file, https, keystore_file, truststore_file, &script_file_dest, &cred_file_dest,
+ &keystore_file_dest, &truststore_file_dest, &container_file_source, &cred_file_source, &keystore_file_source, &truststore_file_source);
if (exit_code != 0) {
fprintf(ERRORFILE, "Could not create script path\n");
fflush(ERRORFILE);
@@ -1564,9 +1643,9 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
fprintf(LOGFILE, "Creating local dirs...\n");
exit_code = create_local_dirs(user, app_id, container_id,
- work_dir, script_name, cred_file, local_dirs, log_dirs,
- 1, script_file_dest, cred_file_dest,
- container_file_source, cred_file_source);
+ work_dir, script_name, cred_file, https, keystore_file, truststore_file, local_dirs, log_dirs,
+ 1, script_file_dest, cred_file_dest, keystore_file_dest, truststore_file_dest,
+ container_file_source, cred_file_source, keystore_file_source, truststore_file_source);
if (exit_code != 0) {
fprintf(ERRORFILE, "Could not create local files and directories %d %d\n", container_file_source, cred_file_source);
fflush(ERRORFILE);
@@ -1815,6 +1894,10 @@ cleanup:
free(exit_code_file);
free(script_file_dest);
free(cred_file_dest);
+ if (https == 1) {
+ free(keystore_file_dest);
+ free(truststore_file_dest);
+ }
free(docker_command_with_binary);
free(docker_inspect_command);
free_values(docker_command);
@@ -1825,12 +1908,16 @@ cleanup:
int launch_container_as_user(const char *user, const char *app_id,
const char *container_id, const char *work_dir,
const char *script_name, const char *cred_file,
+ const int https,
+ const char *keystore_file, const char *truststore_file,
const char* pid_file, char* const* local_dirs,
char* const* log_dirs, const char *resources_key,
char* const* resources_values) {
int exit_code = -1;
char *script_file_dest = NULL;
char *cred_file_dest = NULL;
+ char *keystore_file_dest = NULL;
+ char *truststore_file_dest = NULL;
char *exit_code_file = NULL;
fprintf(LOGFILE, "Getting exit code file...\n");
@@ -1842,11 +1929,13 @@ int launch_container_as_user(const char *user, const char *app_id,
int container_file_source =-1;
int cred_file_source = -1;
+ int keystore_file_source = -1;
+ int truststore_file_source = -1;
fprintf(LOGFILE, "Creating script paths...\n");
exit_code = create_script_paths(
- work_dir, script_name, cred_file, &script_file_dest, &cred_file_dest,
- &container_file_source, &cred_file_source);
+ work_dir, script_name, cred_file, https, keystore_file, truststore_file, &script_file_dest, &cred_file_dest,
+ &keystore_file_dest, &truststore_file_dest, &container_file_source, &cred_file_source, &keystore_file_source, &truststore_file_source);
if (exit_code != 0) {
fprintf(ERRORFILE, "Could not create local files and directories");
fflush(ERRORFILE);
@@ -1894,9 +1983,9 @@ int launch_container_as_user(const char *user, const char *app_id,
fprintf(LOGFILE, "Creating local dirs...\n");
exit_code = create_local_dirs(user, app_id, container_id,
- work_dir, script_name, cred_file, local_dirs, log_dirs,
- 0, script_file_dest, cred_file_dest,
- container_file_source, cred_file_source);
+ work_dir, script_name, cred_file, https, keystore_file, truststore_file, local_dirs, log_dirs,
+ 0, script_file_dest, cred_file_dest, keystore_file_dest, truststore_file_dest,
+ container_file_source, cred_file_source, keystore_file_source, truststore_file_source);
if (exit_code != 0) {
fprintf(ERRORFILE, "Could not create local files and directories");
fflush(ERRORFILE);
@@ -1929,6 +2018,10 @@ int launch_container_as_user(const char *user, const char *app_id,
free(exit_code_file);
free(script_file_dest);
free(cred_file_dest);
+ if (https == 1) {
+ free(keystore_file_dest);
+ free(truststore_file_dest);
+ }
return exit_code;
}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h
index 32e953d89a6..00ce7afbb8c 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h
@@ -59,6 +59,8 @@ enum operations {
#define CONTAINER_DIR_PATTERN NM_APP_DIR_PATTERN "/%s"
#define CONTAINER_SCRIPT "launch_container.sh"
#define CREDENTIALS_FILENAME "container_tokens"
+#define KEYSTORE_FILENAME "yarn_provided.keystore"
+#define TRUSTSTORE_FILENAME "yarn_provided.truststore"
#define MIN_USERID_KEY "min.user.id"
#define BANNED_USERS_KEY "banned.users"
#define ALLOWED_SYSTEM_USERS_KEY "allowed.system.users"
@@ -102,6 +104,8 @@ int initialize_app(const char *user, const char *app_id,
int launch_docker_container_as_user(const char * user, const char *app_id,
const char *container_id, const char *work_dir,
const char *script_name, const char *cred_file,
+ const int https,
+ const char *keystore_file, const char *truststore_file,
const char *pid_file, char* const* local_dirs,
char* const* log_dirs,
const char *command_file,const char *resources_key,
@@ -119,8 +123,13 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
* @param container_id the container id
* @param work_dir the working directory for the container.
* @param script_name the name of the script to be run to launch the container.
- * @param cred_file the credentials file that needs to be compied to the
+ * @param cred_file the credentials file that needs to be copied to the
* working directory.
+ * @param https 1 if a keystore and truststore will be provided, 0 if not
+ * @param keystore_file the keystore file that needs to be copied to the
+ * working directory.
+ * @param truststore_file the truststore file that needs to be copied to the
+ * working directory
* @param pid_file file where pid of process should be written to
* @param local_dirs nodemanager-local-directories to be used
* @param log_dirs nodemanager-log-directories to be used
@@ -131,6 +140,8 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
int launch_container_as_user(const char * user, const char *app_id,
const char *container_id, const char *work_dir,
const char *script_name, const char *cred_file,
+ const int https,
+ const char *keystore_file, const char *truststore_file,
const char *pid_file, char* const* local_dirs,
char* const* log_dirs, const char *resources_key,
char* const* resources_value);
@@ -195,6 +206,10 @@ char *get_container_launcher_file(const char* work_dir);
char *get_container_credentials_file(const char* work_dir);
+char *get_container_keystore_file(const char* work_dir);
+
+char *get_container_truststore_file(const char* work_dir);
+
/**
* Get the app log directory under log_root
*/
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c
index 76fa39f2a39..81b3d19171b 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c
@@ -225,6 +225,9 @@ static struct {
char **resources_values;
const char *app_id;
const char *container_id;
+ int https;
+ const char *keystore_file;
+ const char *truststore_file;
const char *cred_file;
const char *script_file;
const char *current_dir;
@@ -430,8 +433,8 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
case LAUNCH_DOCKER_CONTAINER:
if(is_docker_support_enabled()) {
//kill me now.
- if (!(argc == 14 || argc == 15)) {
- fprintf(ERRORFILE, "Wrong number of arguments (%d vs 14 or 15) for"
+ if (!(argc >= 15 && argc <= 18)) {
+ fprintf(ERRORFILE, "Wrong number of arguments (%d vs 15 - 18) for"
" launch docker container\n", argc);
fflush(ERRORFILE);
return INVALID_ARGUMENT_NUMBER;
@@ -442,6 +445,13 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
cmd_input.current_dir = argv[optind++];
cmd_input.script_file = argv[optind++];
cmd_input.cred_file = argv[optind++];
+ if (strcmp("--https", argv[optind++]) == 0) {
+ cmd_input.https = 1;
+ cmd_input.keystore_file = argv[optind++];
+ cmd_input.truststore_file = argv[optind++];
+ } else {
+ cmd_input.https = 0;
+ }
cmd_input.pid_file = argv[optind++];
// good local dirs as a comma separated list
cmd_input.local_dirs = argv[optind++];
@@ -462,7 +472,7 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
return INVALID_ARGUMENT_NUMBER;
}
//network isolation through tc
- if (argc == 15) {
+ if ((argc == 16 && !cmd_input.https) || (argc == 18 && cmd_input.https)) {
if(is_tc_support_enabled()) {
cmd_input.traffic_control_command_file = argv[optind++];
} else {
@@ -483,8 +493,8 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
case LAUNCH_CONTAINER:
//kill me now.
- if (!(argc == 13 || argc == 14)) {
- fprintf(ERRORFILE, "Wrong number of arguments (%d vs 13 or 14)"
+ if (!(argc >= 14 && argc <= 17)) {
+ fprintf(ERRORFILE, "Wrong number of arguments (%d vs 14 - 17)"
" for launch container\n", argc);
fflush(ERRORFILE);
return INVALID_ARGUMENT_NUMBER;
@@ -495,6 +505,13 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
cmd_input.current_dir = argv[optind++];
cmd_input.script_file = argv[optind++];
cmd_input.cred_file = argv[optind++];
+ if (strcmp("--https", argv[optind++]) == 0) {
+ cmd_input.https = 1;
+ cmd_input.keystore_file = argv[optind++];
+ cmd_input.truststore_file = argv[optind++];
+ } else {
+ cmd_input.https = 0;
+ }
cmd_input.pid_file = argv[optind++];
cmd_input.local_dirs = argv[optind++];// good local dirs as a comma separated list
cmd_input.log_dirs = argv[optind++];// good log dirs as a comma separated list
@@ -513,7 +530,7 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
}
//network isolation through tc
- if (argc == 14) {
+ if ((argc == 15 && !cmd_input.https) || (argc == 17 && cmd_input.https)) {
if(is_tc_support_enabled()) {
cmd_input.traffic_control_command_file = argv[optind++];
} else {
@@ -650,6 +667,9 @@ int main(int argc, char **argv) {
cmd_input.current_dir,
cmd_input.script_file,
cmd_input.cred_file,
+ cmd_input.https,
+ cmd_input.keystore_file,
+ cmd_input.truststore_file,
cmd_input.pid_file,
split(cmd_input.local_dirs),
split(cmd_input.log_dirs),
@@ -678,6 +698,9 @@ int main(int argc, char **argv) {
cmd_input.current_dir,
cmd_input.script_file,
cmd_input.cred_file,
+ cmd_input.https,
+ cmd_input.keystore_file,
+ cmd_input.truststore_file,
cmd_input.pid_file,
split(cmd_input.local_dirs),
split(cmd_input.log_dirs),
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
index 6aac1fe1af6..d8b6fe00e85 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
@@ -68,7 +68,9 @@ enum errorcodes {
DOCKER_IMAGE_INVALID = 40,
// DOCKER_CONTAINER_NAME_INVALID = 41, (NOT USED)
ERROR_COMPILING_REGEX = 42,
- INVALID_CONTAINER_ID = 43
+ INVALID_CONTAINER_ID = 43,
+ COULD_NOT_CREATE_KEYSTORE_FILE = 44,
+ COULD_NOT_CREATE_TRUSTSTORE_FILE = 45
};
/* Macros for min/max. */
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c
index 5607823c666..597197bb08f 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c
@@ -165,13 +165,13 @@ void test_get_user_directory() {
void test_check_nm_local_dir() {
// check filesystem is same as running user.
int expected = 0;
- char *local_path = TEST_ROOT "target";
+ char *local_path = TEST_ROOT "/target";
char *root_path = "/";
if (mkdirs(local_path, 0700) != 0) {
printf("FAIL: unble to create node manager local directory: %s\n", local_path);
exit(1);
}
- int actual = check_nm_local_dir(nm_uid, local_path);
+ int actual = check_nm_local_dir(user_detail->pw_uid, local_path);
if (expected != actual) {
printf("test_nm_local_dir expected %d got %d\n", expected, actual);
exit(1);
@@ -196,31 +196,76 @@ void test_get_app_directory() {
free(app_dir);
}
-void test_get_container_directory() {
- char *container_dir = get_container_work_directory(TEST_ROOT, "owen", "app_1",
+void test_get_container_work_directory() {
+ char *expected_file = TEST_ROOT "/usercache/user/appcache/app_1/container_1";
+ char *work_dir = get_container_work_directory(TEST_ROOT, "user", "app_1",
"container_1");
- char *expected = TEST_ROOT "/usercache/owen/appcache/app_1/container_1";
- if (strcmp(container_dir, expected) != 0) {
- printf("Fail get_container_work_directory got %s expected %s\n",
- container_dir, expected);
+ if (strcmp(work_dir, expected_file) != 0) {
+ printf("Fail get_container_work_directory expected %s got %s\n",
+ expected_file, work_dir);
exit(1);
}
- free(container_dir);
+ free(work_dir);
}
void test_get_container_launcher_file() {
- char *expected_file = (TEST_ROOT "/usercache/user/appcache/app_200906101234_0001"
- "/launch_container.sh");
- char *app_dir = get_app_directory(TEST_ROOT, "user",
- "app_200906101234_0001");
- char *container_file = get_container_launcher_file(app_dir);
- if (strcmp(container_file, expected_file) != 0) {
- printf("failure to match expected container file %s vs %s\n", container_file,
- expected_file);
+ char *expected_file = (TEST_ROOT "/usercache/user/appcache/"
+ "app_200906101234_0001/container_1/launch_container.sh");
+ char *work_dir = get_container_work_directory(TEST_ROOT, "user",
+ "app_200906101234_0001", "container_1");
+ char *launcher_file = get_container_launcher_file(work_dir);
+ if (strcmp(launcher_file, expected_file) != 0) {
+ printf("failure to match expected launcher file %s got %s\n",
+ expected_file, launcher_file);
+ exit(1);
+ }
+ free(work_dir);
+ free(launcher_file);
+}
+
+void test_get_container_credentials_file() {
+ char *expected_file = (TEST_ROOT "/usercache/user/appcache/"
+ "app_200906101234_0001/container_1/container_tokens");
+ char *work_dir = get_container_work_directory(TEST_ROOT, "user",
+ "app_200906101234_0001", "container_1");
+ char *credentials_file = get_container_credentials_file(work_dir);
+ if (strcmp(credentials_file, expected_file) != 0) {
+ printf("failure to match expected credentials file %s got %s\n",
+ expected_file, credentials_file);
exit(1);
}
- free(app_dir);
- free(container_file);
+ free(work_dir);
+ free(credentials_file);
+}
+
+void test_get_container_keystore_file() {
+ char *expected_file = (TEST_ROOT "/usercache/user/appcache/"
+ "app_200906101234_0001/container_1/yarn_provided.keystore");
+ char *work_dir = get_container_work_directory(TEST_ROOT, "user",
+ "app_200906101234_0001", "container_1");
+ char *keystore_file = get_container_keystore_file(work_dir);
+ if (strcmp(keystore_file, expected_file) != 0) {
+ printf("failure to match expected keystore file %s got %s\n",
+ expected_file, keystore_file);
+ exit(1);
+ }
+ free(work_dir);
+ free(keystore_file);
+}
+
+void test_get_container_truststore_file() {
+ char *expected_file = (TEST_ROOT "/usercache/user/appcache/"
+ "app_200906101234_0001/container_1/yarn_provided.truststore");
+ char *work_dir = get_container_work_directory(TEST_ROOT, "user",
+ "app_200906101234_0001", "container_1");
+ char *truststore_file = get_container_truststore_file(work_dir);
+ if (strcmp(truststore_file, expected_file) != 0) {
+ printf("failure to match expected truststore file %s got %s\n",
+ expected_file, truststore_file);
+ exit(1);
+ }
+ free(work_dir);
+ free(truststore_file);
}
void test_get_app_log_dir() {
@@ -759,38 +804,30 @@ void test_signal_container_group() {
}
}
-void test_init_app() {
- printf("\nTesting init app\n");
- if (seteuid(0) != 0) {
- printf("FAIL: seteuid to root failed - %s\n", strerror(errno));
- exit(1);
- }
- FILE* creds = fopen(TEST_ROOT "/creds.txt", "w");
+void create_text_file(const char* filename, const char* contents) {
+ FILE* creds = fopen(filename, "w");
if (creds == NULL) {
- printf("FAIL: failed to create credentials file - %s\n", strerror(errno));
+ printf("FAIL: failed to create %s file - %s\n", filename, strerror(errno));
exit(1);
}
- if (fprintf(creds, "secret key\n") < 0) {
- printf("FAIL: fprintf failed - %s\n", strerror(errno));
+ if (fprintf(creds, contents) < 0) {
+ printf("FAIL: fprintf failed on file %s- %s\n", filename, strerror(errno));
exit(1);
}
if (fclose(creds) != 0) {
- printf("FAIL: fclose failed - %s\n", strerror(errno));
- exit(1);
- }
- FILE* job_xml = fopen(TEST_ROOT "/job.xml", "w");
- if (job_xml == NULL) {
- printf("FAIL: failed to create job file - %s\n", strerror(errno));
- exit(1);
- }
- if (fprintf(job_xml, "\n") < 0) {
- printf("FAIL: fprintf failed - %s\n", strerror(errno));
+ printf("FAIL: fclose failed on file %s - %s\n", filename, strerror(errno));
exit(1);
}
- if (fclose(job_xml) != 0) {
- printf("FAIL: fclose failed - %s\n", strerror(errno));
+}
+
+void test_init_app() {
+ printf("\nTesting init app\n");
+ if (seteuid(0) != 0) {
+ printf("FAIL: seteuid to root failed - %s\n", strerror(errno));
exit(1);
}
+ create_text_file(TEST_ROOT "/creds.txt", "secret key");
+ create_text_file(TEST_ROOT "/job.xml", "\n");
if (seteuid(user_detail->pw_uid) != 0) {
printf("FAIL: failed to seteuid back to user - %s\n", strerror(errno));
exit(1);
@@ -804,14 +841,9 @@ void test_init_app() {
exit(1);
} else if (child == 0) {
char *final_pgm[] = {"touch", "my-touch-file", 0};
- if (initialize_app(yarn_username, "app_4", "container_1",
+ exit(initialize_app(yarn_username, "app_4", "container_1",
TEST_ROOT "/creds.txt",
- local_dirs, log_dirs, final_pgm) != 0) {
- printf("FAIL: failed in child\n");
- exit(42);
- }
- // should never return
- exit(1);
+ local_dirs, log_dirs, final_pgm));
}
int status = 0;
if (waitpid(child, &status, 0) <= 0) {
@@ -819,6 +851,11 @@ void test_init_app() {
strerror(errno));
exit(1);
}
+ if (WEXITSTATUS(status) != 0) {
+ printf("FAIL: child %" PRId64 " exited with bad status %d\n",
+ (int64_t)child, WEXITSTATUS(status));
+ exit(1);
+ }
if (access(TEST_ROOT "/logs/userlogs/app_4", R_OK) != 0) {
printf("FAIL: failed to create app log directory\n");
exit(1);
@@ -856,24 +893,24 @@ void test_init_app() {
free(container_dir);
}
-void test_run_container() {
- printf("\nTesting run container\n");
+void test_launch_container(const char* app, int https) {
+ if (https == 1) {
+ printf("\nTesting launch container with HTTPS\n");
+ } else {
+ printf("\nTesting launch container without HTTPS\n");
+ }
if (seteuid(0) != 0) {
printf("FAIL: seteuid to root failed - %s\n", strerror(errno));
exit(1);
}
- FILE* creds = fopen(TEST_ROOT "/creds.txt", "w");
- if (creds == NULL) {
- printf("FAIL: failed to create credentials file - %s\n", strerror(errno));
- exit(1);
- }
- if (fprintf(creds, "secret key\n") < 0) {
- printf("FAIL: fprintf failed - %s\n", strerror(errno));
- exit(1);
- }
- if (fclose(creds) != 0) {
- printf("FAIL: fclose failed - %s\n", strerror(errno));
- exit(1);
+ create_text_file(TEST_ROOT "/creds.txt", "secret key");
+ char* keystore_file = NULL;
+ char* truststore_file = NULL;
+ if (https == 1) {
+ keystore_file = TEST_ROOT "/yarn_provided.keystore";
+ truststore_file = TEST_ROOT "/yarn_provided.truststore";
+ create_text_file(keystore_file, "keystore");
+ create_text_file(truststore_file, "truststore");
}
char * cgroups_pids[] = { TEST_ROOT "/cgroups-pid1.txt", TEST_ROOT "/cgroups-pid2.txt", 0 };
@@ -903,32 +940,36 @@ void test_run_container() {
fflush(stdout);
fflush(stderr);
char* container_dir = get_container_work_directory(TEST_ROOT "/local-1",
- yarn_username, "app_4", "container_1");
+ yarn_username, app, "container_1");
const char * pid_file = TEST_ROOT "/pid.txt";
pid_t child = fork();
if (child == -1) {
printf("FAIL: failed to fork process for init_app - %s\n",
- strerror(errno));
+ strerror(errno));
exit(1);
} else if (child == 0) {
- if (launch_container_as_user(yarn_username, "app_4", "container_1",
- container_dir, script_name, TEST_ROOT "/creds.txt", pid_file,
- local_dirs, log_dirs,
- "cgroups", cgroups_pids) != 0) {
- printf("FAIL: failed in child\n");
- exit(42);
- }
- // should never return
- exit(1);
+ exit(launch_container_as_user(yarn_username, app, "container_1",
+ container_dir, script_name, TEST_ROOT "/creds.txt",
+ https, keystore_file, truststore_file,
+ pid_file, local_dirs, log_dirs,
+ "cgroups", cgroups_pids));
}
int status = 0;
if (waitpid(child, &status, 0) <= 0) {
printf("FAIL: failed waiting for process %" PRId64 " - %s\n", (int64_t)child,
- strerror(errno));
+ strerror(errno));
+ exit(1);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ printf("FAIL: child %" PRId64 " exited with bad status %d\n",
+ (int64_t)child, WEXITSTATUS(status));
exit(1);
}
- if (access(TEST_ROOT "/logs/userlogs/app_4/container_1", R_OK) != 0) {
+ char logpath[100000];
+ snprintf(logpath, sizeof logpath, "%s%s%s%s", TEST_ROOT, "/logs/userlogs/",
+ app, "/container_1");
+ if (access(logpath, R_OK) != 0) {
printf("FAIL: failed to create container log directory\n");
exit(1);
}
@@ -936,14 +977,17 @@ void test_run_container() {
printf("FAIL: failed to create container directory %s\n", container_dir);
exit(1);
}
- char buffer[100000];
- sprintf(buffer, "%s/foobar", container_dir);
- if (access(buffer, R_OK) != 0) {
- printf("FAIL: failed to create touch file %s\n", buffer);
+ char touchfile[100000];
+ sprintf(touchfile, "%s/foobar", container_dir);
+ if (access(touchfile, R_OK) != 0) {
+ printf("FAIL: failed to create touch file %s\n", touchfile);
exit(1);
}
free(container_dir);
- container_dir = get_app_log_directory(TEST_ROOT "/logs/userlogs", "app_4/container_1");
+ char logpathapp[100000];
+ snprintf(logpathapp, sizeof logpathapp, "%s%s%s", TEST_ROOT, "/logs/userlogs/",
+ app);
+ container_dir = get_app_log_directory(logpathapp, "container_1");
if (access(container_dir, R_OK) != 0) {
printf("FAIL: failed to create app log directory %s\n", container_dir);
exit(1);
@@ -1250,6 +1294,13 @@ int main(int argc, char **argv) {
exit(1);
}
+ if (mkdirs(TEST_ROOT, 0777) != 0) {
+ exit(1);
+ }
+ if (chmod(TEST_ROOT, 0777) != 0) { // in case of umask
+ exit(1);
+ }
+
if (mkdirs(TEST_ROOT "/logs/userlogs", 0755) != 0) {
exit(1);
}
@@ -1303,12 +1354,21 @@ int main(int argc, char **argv) {
printf("\nTesting get_app_directory()\n");
test_get_app_directory();
- printf("\nTesting get_container_directory()\n");
- test_get_container_directory();
+ printf("\nTesting get_container_work_directory()\n");
+ test_get_container_work_directory();
printf("\nTesting get_container_launcher_file()\n");
test_get_container_launcher_file();
+ printf("\nTesting get_container_credentials_file()\n");
+ test_get_container_credentials_file();
+
+ printf("\nTesting get_container_keystore_file()\n");
+ test_get_container_keystore_file();
+
+ printf("\nTesting get_container_truststore_file()\n");
+ test_get_container_truststore_file();
+
printf("\nTesting get_app_log_dir()\n");
test_get_app_log_dir();
@@ -1363,7 +1423,8 @@ int main(int argc, char **argv) {
// these tests do internal forks so that the change_owner and execs
// don't mess up our process.
test_init_app();
- test_run_container();
+ test_launch_container("app_4", 0);
+ test_launch_container("app_5", 1);
}
/*
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java
index 9b180c7eff6..c68c63b87fe 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java
@@ -47,6 +47,7 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.FileStatus;
@@ -69,6 +70,7 @@
import org.apache.hadoop.yarn.server.nodemanager.api.protocolrecords.LocalizerStatus;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerDiagnosticsUpdateEvent;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.MockLocalizerHeartbeatResponse;
import org.apache.hadoop.yarn.server.nodemanager.executor.ContainerStartContext;
@@ -225,6 +227,157 @@ public void testDirPermissions() throws Exception {
}
}
+ private void writeStringToRelativePath(FileContext fc, Path p, String str)
+ throws IOException {
+ p = p.makeQualified(fc.getDefaultFileSystem().getUri(),
+ new Path(new File(".").getAbsolutePath()));
+ try (FSDataOutputStream os = fc.create(p).build()) {
+ os.writeUTF(str);
+ }
+ }
+
+ private String readStringFromPath(FileContext fc, Path p) throws IOException {
+ try (FSDataInputStream is = fc.open(p)) {
+ return is.readUTF();
+ }
+ }
+
+ @Test
+ public void testLaunchContainerCopyFilesWithoutHTTPS() throws Exception {
+ testLaunchContainerCopyFiles(false);
+ }
+
+ @Test
+ public void testLaunchContainerCopyFilesWithHTTPS() throws Exception {
+ testLaunchContainerCopyFiles(true);
+ }
+
+ private void testLaunchContainerCopyFiles(boolean https) throws Exception {
+ if (Shell.WINDOWS) {
+ BASE_TMP_PATH =
+ new Path(new File("target").getAbsolutePath(),
+ TestDefaultContainerExecutor.class.getSimpleName());
+ }
+
+ Path localDir = new Path(BASE_TMP_PATH, "localDir");
+ List localDirs = new ArrayList();
+ localDirs.add(localDir.toString());
+ List logDirs = new ArrayList();
+ Path logDir = new Path(BASE_TMP_PATH, "logDir");
+ logDirs.add(logDir.toString());
+
+ Configuration conf = new Configuration();
+ conf.set(CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY, "077");
+ conf.set(YarnConfiguration.NM_LOCAL_DIRS, localDir.toString());
+ conf.set(YarnConfiguration.NM_LOG_DIRS, logDir.toString());
+
+ FileContext lfs = FileContext.getLocalFSFileContext(conf);
+ deleteTmpFiles();
+ lfs.mkdir(BASE_TMP_PATH, FsPermission.getDefault(), true);
+ DefaultContainerExecutor dce = new DefaultContainerExecutor(lfs);
+ dce.setConf(conf);
+
+ Container container = mock(Container.class);
+ ContainerId cId = mock(ContainerId.class);
+ ContainerLaunchContext context = mock(ContainerLaunchContext.class);
+ HashMap env = new HashMap();
+ env.put("LANG", "C");
+
+ String appSubmitter = "nobody";
+ String appId = "APP_ID";
+ String containerId = "CONTAINER_ID";
+
+ when(container.getContainerId()).thenReturn(cId);
+ when(container.getLaunchContext()).thenReturn(context);
+ when(cId.toString()).thenReturn(containerId);
+ when(cId.getApplicationAttemptId()).thenReturn(
+ ApplicationAttemptId.newInstance(ApplicationId.newInstance(0, 1), 0));
+ when(context.getEnvironment()).thenReturn(env);
+
+ Path scriptPath = new Path(BASE_TMP_PATH, "script");
+ Path tokensPath = new Path(BASE_TMP_PATH, "tokens");
+ Path keystorePath = new Path(BASE_TMP_PATH, "keystore");
+ Path truststorePath = new Path(BASE_TMP_PATH, "truststore");
+ writeStringToRelativePath(lfs, scriptPath, "script");
+ writeStringToRelativePath(lfs, tokensPath, "tokens");
+ if (https) {
+ writeStringToRelativePath(lfs, keystorePath, "keystore");
+ writeStringToRelativePath(lfs, truststorePath, "truststore");
+ }
+
+ Path workDir = localDir;
+ Path pidFile = new Path(workDir, "pid.txt");
+
+ dce.init(null);
+ dce.activateContainer(cId, pidFile);
+ ContainerStartContext.Builder ctxBuilder =
+ new ContainerStartContext.Builder()
+ .setContainer(container)
+ .setNmPrivateContainerScriptPath(scriptPath)
+ .setNmPrivateTokensPath(tokensPath)
+ .setUser(appSubmitter)
+ .setAppId(appId)
+ .setContainerWorkDir(workDir)
+ .setLocalDirs(localDirs)
+ .setLogDirs(logDirs);
+ if (https) {
+ ctxBuilder.setNmPrivateTruststorePath(truststorePath)
+ .setNmPrivateKeystorePath(keystorePath);
+ }
+ ContainerStartContext ctx = ctxBuilder.build();
+
+ // #launchContainer will copy a number of files to this directory.
+ // Ensure that it doesn't exist first
+ lfs.delete(workDir, true);
+ try {
+ lfs.getFileStatus(workDir);
+ Assert.fail("Expected FileNotFoundException on " + workDir);
+ } catch (FileNotFoundException e) {
+ // expected
+ }
+
+ dce.launchContainer(ctx);
+
+ Path finalScriptPath = new Path(workDir,
+ ContainerLaunch.CONTAINER_SCRIPT);
+ Path finalTokensPath = new Path(workDir,
+ ContainerLaunch.FINAL_CONTAINER_TOKENS_FILE);
+ Path finalKeystorePath = new Path(workDir,
+ ContainerLaunch.KEYSTORE_FILE);
+ Path finalTrustorePath = new Path(workDir,
+ ContainerLaunch.TRUSTSTORE_FILE);
+
+ Assert.assertTrue(lfs.getFileStatus(workDir).isDirectory());
+ Assert.assertTrue(lfs.getFileStatus(finalScriptPath).isFile());
+ Assert.assertTrue(lfs.getFileStatus(finalTokensPath).isFile());
+ if (https) {
+ Assert.assertTrue(lfs.getFileStatus(finalKeystorePath).isFile());
+ Assert.assertTrue(lfs.getFileStatus(finalTrustorePath).isFile());
+ } else {
+ try {
+ lfs.getFileStatus(finalKeystorePath);
+ Assert.fail("Expected FileNotFoundException on " + finalKeystorePath);
+ } catch (FileNotFoundException e) {
+ // expected
+ }
+ try {
+ lfs.getFileStatus(finalTrustorePath);
+ Assert.fail("Expected FileNotFoundException on " + finalKeystorePath);
+ } catch (FileNotFoundException e) {
+ // expected
+ }
+ }
+
+ Assert.assertEquals("script", readStringFromPath(lfs, finalScriptPath));
+ Assert.assertEquals("tokens", readStringFromPath(lfs, finalTokensPath));
+ if (https) {
+ Assert.assertEquals("keystore", readStringFromPath(lfs,
+ finalKeystorePath));
+ Assert.assertEquals("truststore", readStringFromPath(lfs,
+ finalTrustorePath));
+ }
+ }
+
@Test
public void testContainerLaunchError()
throws IOException, InterruptedException, ConfigurationException {
@@ -303,6 +456,8 @@ public Object answer(InvocationOnMock invocationOnMock)
Path scriptPath = new Path("file:///bin/echo");
Path tokensPath = new Path("file:///dev/null");
+ Path keystorePath = new Path("file:///dev/null");
+ Path truststorePath = new Path("file:///dev/null");
if (Shell.WINDOWS) {
File tmp = new File(BASE_TMP_PATH.toString(), "test_echo.cmd");
BufferedWriter output = new BufferedWriter(new FileWriter(tmp));
@@ -323,6 +478,8 @@ public Object answer(InvocationOnMock invocationOnMock)
.setContainer(container)
.setNmPrivateContainerScriptPath(scriptPath)
.setNmPrivateTokensPath(tokensPath)
+ .setNmPrivateKeystorePath(keystorePath)
+ .setNmPrivateTruststorePath(truststorePath)
.setUser(appSubmitter)
.setAppId(appId)
.setContainerWorkDir(workDir)
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java
index a110f103314..a7463486cc5 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java
@@ -37,6 +37,8 @@
import java.io.IOException;
import java.io.LineNumberReader;
import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -88,12 +90,11 @@
private static final Logger LOG =
LoggerFactory.getLogger(TestLinuxContainerExecutorWithMocks.class);
- private static final String MOCK_EXECUTOR =
- "./src/test/resources/mock-container-executor";
+ private static final String MOCK_EXECUTOR = "mock-container-executor";
private static final String MOCK_EXECUTOR_WITH_ERROR =
- "./src/test/resources/mock-container-executer-with-error";
+ "mock-container-executer-with-error";
private static final String MOCK_EXECUTOR_WITH_CONFIG_ERROR =
- "./src/test/resources/mock-container-executer-with-configuration-error";
+ "mock-container-executer-with-configuration-error";
private String tmpMockExecutor;
private LinuxContainerExecutor mockExec = null;
@@ -121,11 +122,13 @@ private void deleteMockParamFile() {
return ret;
}
- private void setupMockExecutor(String executorPath, Configuration conf)
- throws IOException {
+ private void setupMockExecutor(String executorName, Configuration conf)
+ throws IOException, URISyntaxException {
//we'll always use the tmpMockExecutor - since
// PrivilegedOperationExecutor can only be initialized once.
+ URI executorPath = getClass().getClassLoader().getResource(executorName)
+ .toURI();
Files.copy(Paths.get(executorPath), Paths.get(tmpMockExecutor),
REPLACE_EXISTING);
@@ -140,7 +143,7 @@ private void setupMockExecutor(String executorPath, Configuration conf)
}
@Before
- public void setup() throws IOException, ContainerExecutionException {
+ public void setup() throws IOException, ContainerExecutionException, URISyntaxException {
assumeNotWindows();
tmpMockExecutor = System.getProperty("test.build.data") +
@@ -172,7 +175,18 @@ public void tearDown() {
}
@Test
- public void testContainerLaunch()
+ public void testContainerLaunchWithoutHTTPS()
+ throws IOException, ConfigurationException {
+ testContainerLaunch(false);
+ }
+
+ @Test
+ public void testContainerLaunchWithHTTPS()
+ throws IOException, ConfigurationException {
+ testContainerLaunch(true);
+ }
+
+ private void testContainerLaunch(boolean https)
throws IOException, ConfigurationException {
String appSubmitter = "nobody";
String cmd = String.valueOf(
@@ -193,41 +207,64 @@ public void testContainerLaunch()
Path scriptPath = new Path("file:///bin/echo");
Path tokensPath = new Path("file:///dev/null");
+ Path keystorePath = new Path("file:///dev/null");
+ Path truststorePath = new Path("file:///dev/null");
Path workDir = new Path("/tmp");
Path pidFile = new Path(workDir, "pid.txt");
mockExec.activateContainer(cId, pidFile);
- int ret = mockExec.launchContainer(new ContainerStartContext.Builder()
- .setContainer(container)
- .setNmPrivateContainerScriptPath(scriptPath)
- .setNmPrivateTokensPath(tokensPath)
- .setUser(appSubmitter)
- .setAppId(appId)
- .setContainerWorkDir(workDir)
- .setLocalDirs(dirsHandler.getLocalDirs())
- .setLogDirs(dirsHandler.getLogDirs())
- .setFilecacheDirs(new ArrayList<>())
- .setUserLocalDirs(new ArrayList<>())
- .setContainerLocalDirs(new ArrayList<>())
- .setContainerLogDirs(new ArrayList<>())
- .setUserFilecacheDirs(new ArrayList<>())
- .setApplicationLocalDirs(new ArrayList<>())
- .build());
+ ContainerStartContext.Builder ctxBuilder =
+ new ContainerStartContext.Builder()
+ .setContainer(container)
+ .setNmPrivateContainerScriptPath(scriptPath)
+ .setNmPrivateTokensPath(tokensPath)
+ .setUser(appSubmitter)
+ .setAppId(appId)
+ .setContainerWorkDir(workDir)
+ .setLocalDirs(dirsHandler.getLocalDirs())
+ .setLogDirs(dirsHandler.getLogDirs())
+ .setFilecacheDirs(new ArrayList<>())
+ .setUserLocalDirs(new ArrayList<>())
+ .setContainerLocalDirs(new ArrayList<>())
+ .setContainerLogDirs(new ArrayList<>())
+ .setUserFilecacheDirs(new ArrayList<>())
+ .setApplicationLocalDirs(new ArrayList<>());
+ if (https) {
+ ctxBuilder.setNmPrivateKeystorePath(keystorePath);
+ ctxBuilder.setNmPrivateTruststorePath(truststorePath);
+ }
+ int ret = mockExec.launchContainer(ctxBuilder.build());
assertEquals(0, ret);
- assertEquals(Arrays.asList(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER,
- appSubmitter, cmd, appId, containerId,
- workDir.toString(), "/bin/echo", "/dev/null", pidFile.toString(),
- StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
- dirsHandler.getLocalDirs()),
- StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
- dirsHandler.getLogDirs()), "cgroups=none"),
- readMockParams());
-
+ if (https) {
+ assertEquals(Arrays.asList(
+ YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER,
+ appSubmitter, cmd, appId, containerId,
+ workDir.toString(), scriptPath.toUri().getPath(),
+ tokensPath.toUri().getPath(), "--https",
+ keystorePath.toUri().getPath(), truststorePath.toUri().getPath(),
+ pidFile.toString(),
+ StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
+ dirsHandler.getLocalDirs()),
+ StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
+ dirsHandler.getLogDirs()), "cgroups=none"),
+ readMockParams());
+ } else {
+ assertEquals(Arrays.asList(
+ YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER,
+ appSubmitter, cmd, appId, containerId,
+ workDir.toString(), scriptPath.toUri().getPath(),
+ tokensPath.toUri().getPath(), "--http", pidFile.toString(),
+ StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
+ dirsHandler.getLocalDirs()),
+ StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
+ dirsHandler.getLogDirs()), "cgroups=none"),
+ readMockParams());
+ }
}
@Test (timeout = 5000)
public void testContainerLaunchWithPriority()
- throws IOException, ConfigurationException {
+ throws IOException, ConfigurationException, URISyntaxException {
// set the scheduler priority to make sure still works with nice -n prio
Configuration conf = new Configuration();
@@ -242,7 +279,7 @@ public void testContainerLaunchWithPriority()
assertEquals("third should be the priority", Integer.toString(2),
command.get(2));
- testContainerLaunch();
+ testContainerLaunchWithoutHTTPS();
}
@Test (timeout = 5000)
@@ -306,7 +343,7 @@ public void testStartLocalizer() throws IOException {
@Test
public void testContainerLaunchError()
- throws IOException, ContainerExecutionException {
+ throws IOException, ContainerExecutionException, URISyntaxException {
final String[] expecetedMessage = {"badcommand", "Exit code: 24"};
final String[] executor = {
@@ -410,7 +447,8 @@ public Object answer(InvocationOnMock invocationOnMock)
assertEquals(Arrays.asList(YarnConfiguration.
DEFAULT_NM_NONSECURE_MODE_LOCAL_USER,
appSubmitter, cmd, appId, containerId,
- workDir.toString(), "/bin/echo", "/dev/null", pidFile.toString(),
+ workDir.toString(), "/bin/echo", "/dev/null", "--http",
+ pidFile.toString(),
StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
dirsHandler.getLocalDirs()),
StringUtils.join(PrivilegedOperation.LINUX_FILE_PATH_SEPARATOR,
@@ -462,7 +500,7 @@ public void testContainerKill() throws IOException {
}
@Test
- public void testDeleteAsUser() throws IOException {
+ public void testDeleteAsUser() throws IOException, URISyntaxException {
String appSubmitter = "nobody";
String cmd = String.valueOf(
PrivilegedOperation.RunAsUserCommand.DELETE_AS_USER.getValue());
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java
index ddf46a63acc..2366423c04d 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/TestContainerLaunch.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.*;
import java.io.BufferedReader;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
@@ -55,9 +56,12 @@
import com.google.common.collect.Lists;
import org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.UnsupportedFileSystemException;
+import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
import org.apache.hadoop.test.GenericTestUtils;
@@ -107,6 +111,8 @@
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.DockerLinuxContainerRuntime;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ResourceLocalizationService;
+import org.apache.hadoop.yarn.server.nodemanager.executor.ContainerStartContext;
import org.apache.hadoop.yarn.server.nodemanager.recovery.NMNullStateStoreService;
import org.apache.hadoop.yarn.server.nodemanager.security.NMContainerTokenSecretManager;
import org.apache.hadoop.yarn.server.nodemanager.security.NMTokenSecretManagerInNM;
@@ -121,6 +127,9 @@
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
public class TestContainerLaunch extends BaseContainerManagerTest {
@@ -2362,4 +2371,143 @@ void generateCombinationAndTest(int nbItems,
}
}
}
+
+ @Test(timeout = 20000)
+ public void testFilesAndEnvWithoutHTTPS() throws Exception {
+ testFilesAndEnv(false);
+ }
+
+ @Test(timeout = 20000)
+ public void testFilesAndEnvWithHTTPS() throws Exception {
+ testFilesAndEnv(true);
+ }
+
+ private void testFilesAndEnv(boolean https) throws Exception {
+ // setup mocks
+ Dispatcher dispatcher = mock(Dispatcher.class);
+ EventHandler handler = mock(EventHandler.class);
+ when(dispatcher.getEventHandler()).thenReturn(handler);
+ ContainerExecutor containerExecutor = mock(ContainerExecutor.class);
+ doAnswer(new Answer() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ DataOutputStream dos = (DataOutputStream) args[0];
+ dos.writeBytes("script");
+ return null;
+ }
+ }).when(containerExecutor).writeLaunchEnv(
+ any(),any(), any(), any(), any(), any(), any());
+ Application app = mock(Application.class);
+ ApplicationId appId = mock(ApplicationId.class);
+ when(appId.toString()).thenReturn("1");
+ when(app.getAppId()).thenReturn(appId);
+ Container container = mock(Container.class);
+ ContainerId id = mock(ContainerId.class);
+ when(id.toString()).thenReturn("1");
+ when(container.getContainerId()).thenReturn(id);
+ when(container.getUser()).thenReturn("user");
+ ContainerLaunchContext clc = mock(ContainerLaunchContext.class);
+ when(clc.getCommands()).thenReturn(Lists.newArrayList());
+ when(container.getLaunchContext()).thenReturn(clc);
+ Credentials credentials = mock(Credentials.class);
+ when(container.getCredentials()).thenReturn(credentials);
+ doAnswer(new Answer() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ DataOutputStream dos = (DataOutputStream) args[0];
+ dos.writeBytes("credentials");
+ return null;
+ }
+ }).when(credentials).writeTokenStorageToStream(any(DataOutputStream.class));
+ if (https) {
+ when(credentials.getSecretKey(Credentials.YARN_APPLICATION_AM_KEYSTORE))
+ .thenReturn("keystore".getBytes());
+ when(credentials.getSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE_PASSWORD))
+ .thenReturn("keystore_password".getBytes());
+ when(credentials.getSecretKey(Credentials.YARN_APPLICATION_AM_TRUSTSTORE))
+ .thenReturn("truststore".getBytes());
+ when(credentials.getSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE_PASSWORD))
+ .thenReturn("truststore_password".getBytes());
+ }
+
+ // call containerLaunch
+ ContainerLaunch containerLaunch = new ContainerLaunch(
+ distContext, conf, dispatcher,
+ containerExecutor, app, container, dirsHandler, containerManager);
+ containerLaunch.call();
+
+ // verify the nmPrivate paths and files
+ ArgumentCaptor cscArgument =
+ ArgumentCaptor.forClass(ContainerStartContext.class);
+ verify(containerExecutor, times(1)).launchContainer(cscArgument.capture());
+ ContainerStartContext csc = cscArgument.getValue();
+ Path nmPrivate = dirsHandler.getLocalPathForWrite(
+ ResourceLocalizationService.NM_PRIVATE_DIR + Path.SEPARATOR +
+ appId.toString() + Path.SEPARATOR + id.toString());
+ Assert.assertEquals(new Path(nmPrivate, ContainerLaunch.CONTAINER_SCRIPT),
+ csc.getNmPrivateContainerScriptPath());
+ Assert.assertEquals(new Path(nmPrivate,
+ String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT,
+ id.toString())), csc.getNmPrivateTokensPath());
+ Assert.assertEquals("script",
+ readStringFromPath(csc.getNmPrivateContainerScriptPath()));
+ Assert.assertEquals("credentials",
+ readStringFromPath(csc.getNmPrivateTokensPath()));
+ if (https) {
+ Assert.assertEquals(new Path(nmPrivate, ContainerLaunch.KEYSTORE_FILE),
+ csc.getNmPrivateKeystorePath());
+ Assert.assertEquals(new Path(nmPrivate, ContainerLaunch.TRUSTSTORE_FILE),
+ csc.getNmPrivateTruststorePath());
+ Assert.assertEquals("keystore",
+ readStringFromPath(csc.getNmPrivateKeystorePath()));
+ Assert.assertEquals("truststore",
+ readStringFromPath(csc.getNmPrivateTruststorePath()));
+ } else {
+ Assert.assertNull(csc.getNmPrivateKeystorePath());
+ Assert.assertNull(csc.getNmPrivateTruststorePath());
+ }
+
+ // verify env
+ ArgumentCaptor
+
+ org.bouncycastle
+ bcprov-jdk15on
+ compile
+
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMActiveServiceContext.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMActiveServiceContext.java
index 66065e33bae..bb1a50d0255 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMActiveServiceContext.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMActiveServiceContext.java
@@ -52,6 +52,7 @@
import org.apache.hadoop.yarn.server.resourcemanager.security.NMTokenSecretManagerInRM;
import org.apache.hadoop.yarn.server.resourcemanager.security.RMContainerTokenSecretManager;
import org.apache.hadoop.yarn.server.resourcemanager.security.RMDelegationTokenSecretManager;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCAManager;
import org.apache.hadoop.yarn.util.Clock;
import org.apache.hadoop.yarn.util.SystemClock;
@@ -114,6 +115,8 @@
private PlacementConstraintManager placementConstraintManager;
private ResourceProfilesManager resourceProfilesManager;
+ private ProxyCAManager proxyCAManager;
+
public RMActiveServiceContext() {
queuePlacementManager = new PlacementManager();
}
@@ -524,4 +527,16 @@ public void setResourceProfilesManager(
ResourceProfilesManager resourceProfilesManager) {
this.resourceProfilesManager = resourceProfilesManager;
}
+
+ @Private
+ @Unstable
+ public ProxyCAManager getProxyCAManager() {
+ return proxyCAManager;
+ }
+
+ @Private
+ @Unstable
+ public void setProxyCAManager(ProxyCAManager proxyCAManager) {
+ this.proxyCAManager = proxyCAManager;
+ }
}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContext.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContext.java
index eb91a311a3a..ef19b2d3c68 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContext.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContext.java
@@ -53,6 +53,7 @@
import org.apache.hadoop.yarn.server.resourcemanager.security.RMContainerTokenSecretManager;
import org.apache.hadoop.yarn.server.resourcemanager.security.RMDelegationTokenSecretManager;
import org.apache.hadoop.yarn.server.resourcemanager.timelineservice.RMTimelineCollectorManager;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCAManager;
/**
* Context of the ResourceManager.
@@ -177,4 +178,8 @@ void setRMDelegatedNodeLabelsUpdater(
void setPlacementConstraintManager(
PlacementConstraintManager placementConstraintManager);
+
+ ProxyCAManager getProxyCAManager();
+
+ void setProxyCAManager(ProxyCAManager proxyCAManager);
}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContextImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContextImpl.java
index 84e0f6f6b58..7dc3e428acb 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContextImpl.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMContextImpl.java
@@ -59,6 +59,7 @@
import org.apache.hadoop.yarn.server.resourcemanager.security.RMContainerTokenSecretManager;
import org.apache.hadoop.yarn.server.resourcemanager.security.RMDelegationTokenSecretManager;
import org.apache.hadoop.yarn.server.resourcemanager.timelineservice.RMTimelineCollectorManager;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCAManager;
import org.apache.hadoop.yarn.server.webproxy.ProxyUriUtils;
import org.apache.hadoop.yarn.util.Clock;
@@ -619,5 +620,15 @@ public String getAppProxyUrl(Configuration conf, ApplicationId applicationId)
public void setResourceProfilesManager(ResourceProfilesManager mgr) {
this.activeServiceContext.setResourceProfilesManager(mgr);
}
+
+ @Override
+ public ProxyCAManager getProxyCAManager() {
+ return this.activeServiceContext.getProxyCAManager();
+ }
+
+ @Override
+ public void setProxyCAManager(ProxyCAManager proxyCAManager) {
+ this.activeServiceContext.setProxyCAManager(proxyCAManager);
+ }
// Note: Read java doc before adding any services over here.
}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
index 0b7e87cc0b1..af306be8794 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
@@ -108,6 +108,7 @@
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.server.service.SystemServiceManager;
import org.apache.hadoop.yarn.server.webproxy.AppReportFetcher;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCAManager;
import org.apache.hadoop.yarn.server.webproxy.ProxyUriUtils;
import org.apache.hadoop.yarn.server.webproxy.WebAppProxy;
import org.apache.hadoop.yarn.server.webproxy.WebAppProxyServlet;
@@ -796,6 +797,10 @@ protected void serviceInit(Configuration configuration) throws Exception {
LOG.info("Initialized Federation membership.");
}
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ addService(proxyCAManager);
+ rmContext.setProxyCAManager(proxyCAManager);
+
new RMNMInfo(rmContext, scheduler);
if (conf.getBoolean(YarnConfiguration.YARN_API_SERVICES_ENABLE,
@@ -1129,6 +1134,8 @@ protected void startWepApp() {
}
builder.withServlet(ProxyUriUtils.PROXY_SERVLET_NAME,
ProxyUriUtils.PROXY_PATH_SPEC, WebAppProxyServlet.class);
+ builder.withAttribute(WebAppProxy.PROXY_CA_MANAGER,
+ rmContext.getProxyCAManager());
builder.withAttribute(WebAppProxy.FETCHER_ATTRIBUTE, fetcher);
String[] proxyParts = proxyHostAndPort.split(":");
builder.withAttribute(WebAppProxy.PROXY_HOST_ATTRIBUTE, proxyParts[0]);
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/AMLauncher.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/AMLauncher.java
index 0bedb528dc4..a738307360e 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/AMLauncher.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/AMLauncher.java
@@ -62,6 +62,7 @@
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptEvent;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptEventType;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptImpl;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCAManager;
import org.apache.hadoop.yarn.util.ConverterUtils;
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
@@ -233,6 +234,34 @@ protected void setupTokens(
if (amrmToken != null) {
credentials.addToken(amrmToken.getService(), amrmToken);
}
+
+ // Setup Keystore and Truststore
+ String httpsPolicy = conf.get(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY,
+ YarnConfiguration.DEFAULT_RM_APPLICATION_HTTPS_POLICY);
+ if (httpsPolicy.equals("OPTIONAL") || httpsPolicy.equals("REQUIRED")) {
+ ProxyCAManager proxyCAManager = rmContext.getProxyCAManager();
+ try {
+ String kPass = proxyCAManager.generateKeyStorePassword();
+ byte[] keyStore = proxyCAManager.createChildKeyStore(
+ applicationId, kPass);
+ credentials.addSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE, keyStore);
+ credentials.addSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE_PASSWORD,
+ kPass.getBytes());
+ String tPass = proxyCAManager.generateKeyStorePassword();
+ byte[] trustStore =
+ proxyCAManager.getChildTrustStore(tPass);
+ credentials.addSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE, trustStore);
+ credentials.addSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE_PASSWORD,
+ tPass.getBytes());
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
DataOutputBuffer dob = new DataOutputBuffer();
credentials.writeTokenStorageToStream(dob);
container.setTokens(ByteBuffer.wrap(dob.getData(), 0, dob.getLength()));
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationMasterLauncher.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationMasterLauncher.java
index 38181e256be..66f2e23597a 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationMasterLauncher.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationMasterLauncher.java
@@ -29,8 +29,11 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.io.DataInputByteBuffer;
import org.apache.hadoop.io.DataOutputBuffer;
+import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.Credentials;
+import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.yarn.api.ApplicationConstants;
import org.apache.hadoop.yarn.api.ContainerManagementProtocol;
@@ -80,6 +83,7 @@
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptState;
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCAManager;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
@@ -91,6 +95,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
public class TestApplicationMasterLauncher {
@@ -424,16 +429,45 @@ public void testallocateBeforeAMRegistration() throws Exception {
}
@Test
- public void testSetupTokens() throws Exception {
- MockRM rm = new MockRM();
+ public void testSetupTokensWithoutHTTPS() throws Exception {
+ YarnConfiguration conf = new YarnConfiguration();
+ conf.set(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY, "OFF");
+ testSetupTokens(false, conf);
+ }
+
+ @Test
+ public void testSetupTokensWithHTTPS() throws Exception {
+ YarnConfiguration conf = new YarnConfiguration();
+ // default conf
+ testSetupTokens(true, conf);
+ conf.set(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY, "OPTIONAL");
+ testSetupTokens(true, conf);
+ conf.set(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY, "REQUIRED");
+ testSetupTokens(true, conf);
+ }
+
+ public void testSetupTokens(boolean https, YarnConfiguration conf)
+ throws Exception {
+ MockRM rm = new MockRM(conf);
rm.start();
MockNM nm1 = rm.registerNode("h1:1234", 5000);
RMApp app = rm.submitApp(2000);
/// kick the scheduling
nm1.nodeHeartbeat(true);
RMAppAttempt attempt = app.getCurrentAppAttempt();
- MyAMLauncher launcher = new MyAMLauncher(rm.getRMContext(),
- attempt, AMLauncherEventType.LAUNCH, rm.getConfig());
+ AMRMTokenIdentifier tokenIdentifier =
+ new AMRMTokenIdentifier(attempt.getAppAttemptId(), 1);
+ ProxyCAManager proxyCAManager = mock(ProxyCAManager.class);
+ when(proxyCAManager.generateKeyStorePassword())
+ .thenReturn("kPassword").thenReturn("tPassword");
+ when(proxyCAManager.createChildKeyStore(any(), any()))
+ .thenReturn("keystore".getBytes());
+ when(proxyCAManager.getChildTrustStore(any()))
+ .thenReturn("truststore".getBytes());
+ RMContext rmContext = spy(rm.getRMContext());
+ when(rmContext.getProxyCAManager()).thenReturn(proxyCAManager);
+ MyAMLauncher launcher = new MyAMLauncher(rmContext,
+ attempt, AMLauncherEventType.LAUNCH, rm.getConfig(), tokenIdentifier);
DataOutputBuffer dob = new DataOutputBuffer();
Credentials ts = new Credentials();
ts.writeTokenStorageToStream(dob);
@@ -455,14 +489,46 @@ public void testSetupTokens() throws Exception {
} catch (java.io.EOFException e) {
Assert.fail("EOFException should not happen.");
}
+
+ // verify token
+ DataInputByteBuffer dibb = new DataInputByteBuffer();
+ dibb.reset(amContainer.getTokens());
+ Credentials credentials = new Credentials();
+ credentials.readTokenStorageStream(dibb);
+ Assert.assertEquals(1, credentials.numberOfTokens());
+ org.apache.hadoop.security.token.Token extends TokenIdentifier> token =
+ credentials.getAllTokens().iterator().next();
+ Assert.assertEquals(tokenIdentifier.getKind(), token.getKind());
+ Assert.assertArrayEquals(tokenIdentifier.getBytes(), token.getIdentifier());
+ Assert.assertArrayEquals("password".getBytes(), token.getPassword());
+
+ // verify keystore and truststore
+ if (https) {
+ Assert.assertEquals(4, credentials.numberOfSecretKeys());
+ Assert.assertArrayEquals("keystore".getBytes(),
+ credentials.getSecretKey(Credentials.YARN_APPLICATION_AM_KEYSTORE));
+ Assert.assertArrayEquals("kPassword".getBytes(),
+ credentials.getSecretKey(
+ Credentials.YARN_APPLICATION_AM_KEYSTORE_PASSWORD));
+ Assert.assertArrayEquals("truststore".getBytes(),
+ credentials.getSecretKey(Credentials.YARN_APPLICATION_AM_TRUSTSTORE));
+ Assert.assertArrayEquals("tPassword".getBytes(),
+ credentials.getSecretKey(
+ Credentials.YARN_APPLICATION_AM_TRUSTSTORE_PASSWORD));
+ } else {
+ Assert.assertEquals(0, credentials.numberOfSecretKeys());
+ }
}
static class MyAMLauncher extends AMLauncher {
int count;
+ AMRMTokenIdentifier tokenIdentifier;
public MyAMLauncher(RMContext rmContext, RMAppAttempt application,
- AMLauncherEventType eventType, Configuration conf) {
+ AMLauncherEventType eventType, Configuration conf,
+ AMRMTokenIdentifier tokenIdentifier) {
super(rmContext, application, eventType, conf);
count = 0;
+ this.tokenIdentifier = tokenIdentifier;
}
protected org.apache.hadoop.security.token.Token
@@ -471,7 +537,9 @@ public MyAMLauncher(RMContext rmContext, RMAppAttempt application,
if (count == 1) {
throw new RuntimeException("createAndSetAMRMToken failure");
}
- return null;
+ return new org.apache.hadoop.security.token.Token(
+ tokenIdentifier.getBytes(), "password".getBytes(),
+ tokenIdentifier.getKind(), new Text());
}
protected void setupTokens(ContainerLaunchContext container,
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/pom.xml hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/pom.xml
index 8329fd78712..45ae347ddb5 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/pom.xml
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/pom.xml
@@ -127,7 +127,7 @@
org.bouncycastle
- bcprov-jdk16
+ bcprov-jdk15ontest
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/pom.xml hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/pom.xml
index 0d1b92b0894..1b27f4dd2bd 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/pom.xml
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/pom.xml
@@ -115,6 +115,10 @@
jersey-test-framework-grizzly2test
+
+ org.bouncycastle
+ bcprov-jdk15on
+
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCAManager.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCAManager.java
new file mode 100644
index 00000000000..ac39eb725a9
--- /dev/null
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCAManager.java
@@ -0,0 +1,343 @@
+package org.apache.hadoop.yarn.server.webproxy;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.util.PublicSuffixMatcherLoader;
+import org.bouncycastle.asn1.x509.X509Extensions;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.Socket;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Random;
+import java.util.UUID;
+
+public class ProxyCAManager extends AbstractService {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(ProxyCAManager.class);
+
+ private X509Certificate caCert;
+ private KeyPair caKeyPair;
+ private KeyStore childTrustStore;
+ private final Random srand;
+ private X509TrustManager defaultTrustManager;
+ private X509KeyManager x509KeyManager;
+ private HostnameVerifier hostnameVerifier;
+
+ public ProxyCAManager() {
+ super(ProxyCAManager.class.getName());
+ srand = new SecureRandom();
+ }
+
+ @Override
+ protected void serviceInit(Configuration conf) throws Exception {
+ super.serviceInit(conf);
+ // This is needed for the child certs, but only has to be done once
+ Security.addProvider(new BouncyCastleProvider());
+
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(
+ TrustManagerFactory.getDefaultAlgorithm());
+ factory.init((KeyStore) null);
+ for (TrustManager manager : factory.getTrustManagers()) {
+ if (manager instanceof X509TrustManager) {
+ defaultTrustManager = (X509TrustManager) manager;
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void serviceStart() throws Exception {
+ super.serviceStart();
+ // TODO: handle RM HA: YARN-8449
+ createCACert();
+ x509KeyManager = createKeyManager();
+ hostnameVerifier = createHostnameVerifier();
+ childTrustStore = createTrustStore("client", caCert);
+ }
+
+ @Override
+ protected void serviceStop() throws Exception {
+ super.serviceStop();
+ }
+
+ public void createCACert() throws Exception {
+ Date from = new Date();
+ Date to = new GregorianCalendar(2037, Calendar.DECEMBER, 31).getTime();
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ caKeyPair = keyGen.genKeyPair();
+ X509V1CertificateGenerator gen = new X509V1CertificateGenerator();
+ X500Principal dn = new X500Principal("OU=YARN-" + UUID.randomUUID());
+ gen.setSerialNumber(new BigInteger(64, new SecureRandom()));
+ gen.setIssuerDN(dn);
+ gen.setNotBefore(from);
+ gen.setNotAfter(to);
+ gen.setSubjectDN(dn);
+ gen.setPublicKey(caKeyPair.getPublic());
+ gen.setSignatureAlgorithm("SHA512WITHRSA");
+ caCert = gen.generate(caKeyPair.getPrivate());
+ LOG.debug("Created CA Certificate");
+ }
+
+ public byte[] createChildKeyStore(ApplicationId appId, String ksPassword)
+ throws Exception {
+ // We don't check the expiration date, and this will provide further reason
+ // for outside users to not accept these certificates
+ Date from = new Date();
+ Date to = from;
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ KeyPair keyPair = keyGen.genKeyPair();
+ X509V3CertificateGenerator gen = new X509V3CertificateGenerator();
+ X500Principal dn = new X500Principal("CN=" + appId);
+ gen.setSerialNumber(new BigInteger(64, new SecureRandom()));
+ gen.setIssuerDN(caCert.getSubjectX500Principal());
+ gen.setNotBefore(from);
+ gen.setNotAfter(to);
+ gen.setSubjectDN(dn);
+ gen.setPublicKey(keyPair.getPublic());
+ gen.setSignatureAlgorithm("SHA512WITHRSA");
+ gen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ new AuthorityKeyIdentifierStructure(caCert));
+ X509Certificate cert = gen.generate(caKeyPair.getPrivate(), "BC");
+ LOG.debug("Created certificate for {}", appId);
+
+ KeyStore keyStore = createChildKeyStore(ksPassword, "server",
+ keyPair.getPrivate(), cert);
+ return keyStoreToBytes(keyStore, ksPassword);
+ }
+
+ public byte[] getChildTrustStore(String password)
+ throws GeneralSecurityException, IOException {
+ return keyStoreToBytes(childTrustStore, password);
+ }
+
+ private KeyStore createEmptyKeyStore()
+ throws GeneralSecurityException, IOException {
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null, null); // initialize
+ return ks;
+ }
+
+ private KeyStore createChildKeyStore(String password, String alias,
+ Key privateKey, Certificate cert)
+ throws GeneralSecurityException, IOException {
+ KeyStore ks = createEmptyKeyStore();
+ ks.setKeyEntry(alias, privateKey, password.toCharArray(),
+ new Certificate[]{cert, caCert});
+ return ks;
+ }
+
+ public String generateKeyStorePassword() {
+ return RandomStringUtils.random(16, 0, 0, true, true, null, srand);
+ }
+
+ private byte[] keyStoreToBytes(KeyStore ks, String password)
+ throws GeneralSecurityException, IOException {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ ks.store(out, password.toCharArray());
+ return out.toByteArray();
+ }
+ }
+
+ private KeyStore createTrustStore(String alias, Certificate cert)
+ throws GeneralSecurityException, IOException {
+ KeyStore ks = createEmptyKeyStore();
+ ks.setCertificateEntry(alias, cert);
+ return ks;
+ }
+
+ public SSLContext createSSLContext(ApplicationId appId)
+ throws GeneralSecurityException {
+ // We need the normal TrustManager, plus our custom one. While the
+ // SSLContext accepts an array of TrustManagers, the docs indicate that only
+ // the first instance of any particular implementation type is used
+ // (e.g. X509KeyManager) - this means that simply putting both TrustManagers
+ // in won't work. We need to have ours do both.
+ TrustManager[] trustManagers = new TrustManager[] {
+ createTrustManager(appId)};
+ KeyManager[] keyManagers = new KeyManager[]{x509KeyManager};
+
+ SSLContext sc = SSLContext.getInstance("SSL");
+ sc.init(keyManagers, trustManagers, new SecureRandom());
+ return sc;
+ }
+
+ @VisibleForTesting
+ X509TrustManager createTrustManager(ApplicationId appId) {
+ return new X509TrustManager() {
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return defaultTrustManager.getAcceptedIssuers();
+ }
+
+ @Override
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs, String authType)
+ throws CertificateException {
+ // not used
+ }
+
+ @Override
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs, String authType)
+ throws CertificateException {
+ // Our certs will always have 2 in the chain, with 0 being the app's
+ // cert and 1 being the RM's cert
+ boolean issuedByRM = false;
+ if (certs.length == 2) {
+ try {
+ // We can verify both certs using the CA cert's public key - the
+ // child cert's info is not needed
+ certs[0].verify(caKeyPair.getPublic());
+ certs[1].verify(caKeyPair.getPublic());
+ issuedByRM = true;
+ } catch (CertificateException | NoSuchAlgorithmException
+ | InvalidKeyException | NoSuchProviderException
+ | SignatureException e) {
+ // Fall back to the default trust manager
+ LOG.debug("Could not verify certificate with RM CA, falling " +
+ "back to default", e);
+ defaultTrustManager.checkServerTrusted(certs, authType);
+ }
+ } else {
+ LOG.debug("Certificate not issued by RM CA, falling back to " +
+ "default");
+ defaultTrustManager.checkServerTrusted(certs, authType);
+ }
+ if (issuedByRM) {
+ // Check that it has the correct App ID
+ if (!certs[0].getSubjectX500Principal().getName()
+ .equals("CN=" + appId)) {
+ throw new CertificateException(
+ "Expected to find Subject X500 Principal with CN="
+ + appId + " but found "
+ + certs[0].getSubjectX500Principal().getName());
+ }
+ LOG.debug("Verified certificate signed by RM CA");
+ }
+ }
+ };
+ }
+
+ @VisibleForTesting
+ X509KeyManager getX509KeyManager() {
+ return x509KeyManager;
+ }
+
+ private X509KeyManager createKeyManager() {
+ return 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[]{caCert};
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String s) {
+ return caKeyPair.getPrivate();
+ }
+ };
+ }
+
+ public HostnameVerifier getHostnameVerifier() {
+ return hostnameVerifier;
+ }
+
+ private HostnameVerifier createHostnameVerifier() {
+ HostnameVerifier defaultHostnameVerifier =
+ new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault());
+ return new HostnameVerifier() {
+ @Override
+ public boolean verify(String host, SSLSession sslSession) {
+ try {
+ Certificate[] certs = sslSession.getPeerCertificates();
+ if (certs.length == 2) {
+ // Make sure this is one of our certs. More thorough checking would
+ // have already been done by the SSLContext
+ certs[0].verify(caKeyPair.getPublic());
+ LOG.debug("Verified certificate signed by RM CA, " +
+ "skipping hostname verification");
+ return true;
+ }
+ } catch (SSLPeerUnverifiedException e) {
+ // No certificate
+ return false;
+ } catch (CertificateException | NoSuchAlgorithmException
+ | InvalidKeyException | SignatureException
+ | NoSuchProviderException e) {
+ // fall back to normal verifier below
+ LOG.debug("Could not verify certificate with RM CA, " +
+ "falling back to default hostname verification", e);
+ }
+ return defaultHostnameVerifier.verify(host, sslSession);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ void setDefaultTrustManager(X509TrustManager trustManager) {
+ this.defaultTrustManager = trustManager;
+ }
+}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxy.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxy.java
index 71679ccca91..d9cf086a5ff 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxy.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxy.java
@@ -39,6 +39,7 @@
public static final String FETCHER_ATTRIBUTE= "AppUrlFetcher";
public static final String IS_SECURITY_ENABLED_ATTRIBUTE = "IsSecurityEnabled";
public static final String PROXY_HOST_ATTRIBUTE = "proxyHost";
+ public static final String PROXY_CA_MANAGER = "ProxyCAManager";
private static final Logger LOG = LoggerFactory.getLogger(
WebAppProxy.class);
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java
index f21ff2c37df..c3a947a4f08 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java
@@ -45,6 +45,7 @@
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;
+import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -63,15 +64,14 @@
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.params.ClientPNames;
-import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -177,6 +177,39 @@ private static void warnUserPage(HttpServletResponse resp, String link,
__().
__();
}
+
+ /**
+ * Show the user a page that says that HTTPS is required but was not provided.
+ * @param resp the http response
+ * @param link the link to point to
+ * @return true if HTTPS is required but was not provided, false otherwise
+ * @throws IOException on any error.
+ */
+ @VisibleForTesting
+ static boolean checkHttpsRequiredAndNotProvided(
+ HttpServletResponse resp, URI link, YarnConfiguration conf)
+ throws IOException {
+ String httpsPolicy = conf.get(
+ YarnConfiguration.RM_APPLICATION_HTTPS_POLICY,
+ YarnConfiguration.DEFAULT_RM_APPLICATION_HTTPS_POLICY);
+ boolean required = httpsPolicy.equals("REQUIRED");
+ boolean provided = link.getScheme().equals("https");
+ if (required && !provided) {
+ resp.setContentType(MimeType.HTML);
+ Page p = new Page(resp.getWriter());
+ p.html().
+ h1("HTTPS is required").
+ h3().
+ __(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY,
+ "is set to REQUIRED, which means that the tracking URL ",
+ "must be an HTTPS URL, but it is not.").
+ __("The tracking URL is: ", link).
+ __().
+ __();
+ return true;
+ }
+ return false;
+ }
/**
* Download link and have it be the response.
@@ -186,17 +219,31 @@ private static void warnUserPage(HttpServletResponse resp, String link,
* @param c the cookie to set if any
* @param proxyHost the proxy host
* @param method the http method
+ * @param appId the ApplicationID
* @throws IOException on any error.
*/
- private static void proxyLink(final HttpServletRequest req,
+ private void proxyLink(final HttpServletRequest req,
final HttpServletResponse resp, final URI link, final Cookie c,
- final String proxyHost, final HTTP method) throws IOException {
- DefaultHttpClient client = new DefaultHttpClient();
- client
- .getParams()
- .setParameter(ClientPNames.COOKIE_POLICY,
- CookiePolicy.BROWSER_COMPATIBILITY)
- .setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
+ final String proxyHost, final HTTP method, final ApplicationId appId) throws IOException {
+ HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
+
+ String httpsPolicy = conf.get(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY,
+ YarnConfiguration.DEFAULT_RM_APPLICATION_HTTPS_POLICY);
+ if (httpsPolicy.equals("OPTIONAL") || httpsPolicy.equals("REQUIRED")) {
+ ProxyCAManager proxyCAManager = getProxyCAManager();
+ // ProxyCAManager could be null when the Proxy is run outside the RM
+ if (proxyCAManager != null) {
+ try {
+ httpClientBuilder.setSSLContext(
+ proxyCAManager.createSSLContext(appId));
+ httpClientBuilder.setSSLHostnameVerifier(
+ proxyCAManager.getHostnameVerifier());
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
// Make sure we send the request from the proxy address in the config
// since that is what the AM filter checks against. IP aliasing or
// similar could cause issues otherwise.
@@ -204,8 +251,11 @@ private static void proxyLink(final HttpServletRequest req,
if (LOG.isDebugEnabled()) {
LOG.debug("local InetAddress for proxy host: {}", localAddress);
}
- client.getParams()
- .setParameter(ConnRoutePNames.LOCAL_ADDRESS, localAddress);
+ httpClientBuilder.setDefaultRequestConfig(
+ RequestConfig.custom()
+ .setCircularRedirectsAllowed(true)
+ .setLocalAddress(localAddress)
+ .build());
HttpRequestBase base = null;
if (method.equals(HTTP.GET)) {
@@ -247,6 +297,7 @@ private static void proxyLink(final HttpServletRequest req,
PROXY_USER_COOKIE_NAME + "=" + URLEncoder.encode(user, "ASCII"));
}
OutputStream out = resp.getOutputStream();
+ HttpClient client = httpClientBuilder.build();
try {
HttpResponse httpResp = client.execute(base);
resp.setStatus(httpResp.getStatusLine().getStatusCode());
@@ -287,6 +338,11 @@ private FetchedAppReport getApplicationReport(ApplicationId id)
return ((AppReportFetcher) getServletContext()
.getAttribute(WebAppProxy.FETCHER_ATTRIBUTE)).getApplicationReport(id);
}
+
+ private ProxyCAManager getProxyCAManager() {
+ return ((ProxyCAManager) getServletContext()
+ .getAttribute(WebAppProxy.PROXY_CA_MANAGER));
+ }
private String getProxyHost() throws IOException {
return ((String) getServletContext()
@@ -420,6 +476,10 @@ private void methodAction(final HttpServletRequest req,
return;
}
+ if (checkHttpsRequiredAndNotProvided(resp, trackingUri, conf)) {
+ return;
+ }
+
String runningUser = applicationReport.getUser();
if (checkUser && !runningUser.equals(remoteUser)) {
@@ -453,7 +513,7 @@ private void methodAction(final HttpServletRequest req,
if (userWasWarned && userApproved) {
c = makeCheckCookie(id, true);
}
- proxyLink(req, resp, toFetch, c, getProxyHost(), method);
+ proxyLink(req, resp, toFetch, c, getProxyHost(), method, id);
} catch(URISyntaxException | YarnException e) {
throw new IOException(e);
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCAManager.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCAManager.java
new file mode 100644
index 00000000000..89084504ea8
--- /dev/null
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCAManager.java
@@ -0,0 +1,427 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.hadoop.yarn.server.webproxy;
+
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
+import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+public class TestProxyCAManager {
+
+ @Test
+ public void testCreateChildKeyStore() throws Exception {
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ proxyCAManager.init(new YarnConfiguration());
+ proxyCAManager.start();
+ ApplicationId appId =
+ ApplicationId.newInstance(System.currentTimeMillis(), 1);
+ byte[] keystoreBytes = proxyCAManager.createChildKeyStore(appId,
+ "password");
+ KeyStore keyStore = KeyStoreTestUtil.bytesToKeyStore(keystoreBytes,
+ "password");
+ Assert.assertEquals(1, keyStore.size());
+ Certificate[] certChain = keyStore.getCertificateChain("server");
+ Assert.assertEquals(2, certChain.length);
+ X509Certificate caCert = (X509Certificate) certChain[1];
+ X509Certificate cert = (X509Certificate) certChain[0];
+
+ // check child cert
+ Assert.assertEquals(caCert.getSubjectX500Principal().toString(),
+ cert.getIssuerDN().toString());
+ Assert.assertEquals(new X500Principal("CN=" + appId),
+ cert.getSubjectX500Principal());
+ Assert.assertFalse("Found multiple fields in X500 Principal, when there " +
+ "should have only been one: " + cert.getSubjectX500Principal(),
+ cert.getSubjectX500Principal().toString().contains(","));
+ Assert.assertEquals("SHA512withRSA", cert.getSigAlgName());
+ Assert.assertEquals(cert.getNotBefore(), cert.getNotAfter());
+ Assert.assertTrue("Expected certificate to be expired but was not: "
+ + cert.getNotAfter(), cert.getNotAfter().before(new Date()));
+ Assert.assertEquals(new X500Principal("CN=" + appId).toString(),
+ cert.getSubjectDN().toString());
+ Key privateKey = keyStore.getKey("server", "password".toCharArray());
+ Assert.assertEquals("RSA", privateKey.getAlgorithm());
+
+ // verify signature on child cert
+ PublicKey caPublicKey = caCert.getPublicKey();
+ cert.verify(caPublicKey);
+
+ // check CA cert
+ checkCACert(caCert);
+
+ // verify signature on CA cert
+ caCert.verify(caPublicKey);
+
+ // verify CA public key matches private key
+ PrivateKey caPrivateKey =
+ proxyCAManager.getX509KeyManager().getPrivateKey(null);
+ checkPrivatePublicKeys(caPrivateKey, caPublicKey);
+
+ proxyCAManager.stop();
+ }
+
+ @Test
+ public void testGetChildTrustStore() throws Exception {
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ proxyCAManager.init(new YarnConfiguration());
+ proxyCAManager.start();
+ byte[] truststoreBytes = proxyCAManager.getChildTrustStore("password");
+ KeyStore truststore = KeyStoreTestUtil.bytesToKeyStore(truststoreBytes,
+ "password");
+ Assert.assertEquals(1, truststore.size());
+ X509Certificate caCert =
+ (X509Certificate) truststore.getCertificate("client");
+
+ // check CA cert
+ checkCACert(caCert);
+
+ // verify signature on CA cert
+ PublicKey caPublicKey = caCert.getPublicKey();
+ caCert.verify(caPublicKey);
+
+ // verify CA public key matches private key
+ PrivateKey caPrivateKey =
+ proxyCAManager.getX509KeyManager().getPrivateKey(null);
+ checkPrivatePublicKeys(caPrivateKey, caPublicKey);
+
+ proxyCAManager.stop();
+ }
+
+ @Test
+ public void testGenerateKeyStorePassword() {
+ // We can't possibly test every possible string, but we can at least verify
+ // a few things about a few of the generated strings as a sanity check
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ proxyCAManager.init(new YarnConfiguration());
+ proxyCAManager.start();
+ Set passwords = new HashSet<>();
+
+ for (int i = 0; i < 5; i++) {
+ String password = proxyCAManager.generateKeyStorePassword();
+ Assert.assertEquals(16, password.length());
+ for (char c : password.toCharArray()) {
+ Assert.assertFalse("Found character '" + c + "' in password '"
+ + password + "' which is outside of the expected range", c < ' ');
+ Assert.assertFalse("Found character '" + c + "' in password '"
+ + password + "' which is outside of the expected range", c > 'z');
+ }
+ Assert.assertFalse("Password " + password
+ + " was generated twice, which is _extremely_ unlikely"
+ + " and shouldn't practically happen: " + passwords,
+ passwords.contains(password));
+ passwords.add(password);
+ }
+
+ proxyCAManager.stop();
+ }
+
+ @Test
+ public void testCreateTrustManager() throws Exception {
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ proxyCAManager.init(new YarnConfiguration());
+ X509TrustManager defaultTrustManager = Mockito.mock(X509TrustManager.class);
+ proxyCAManager.setDefaultTrustManager(defaultTrustManager);
+ proxyCAManager.start();
+ ApplicationId appId =
+ ApplicationId.newInstance(System.currentTimeMillis(), 1);
+ ApplicationId appId2 =
+ ApplicationId.newInstance(System.currentTimeMillis(), 2);
+ X509TrustManager trustManager = proxyCAManager.createTrustManager(appId);
+
+ Mockito.when(defaultTrustManager.getAcceptedIssuers()).thenReturn(
+ new X509Certificate[]{KeyStoreTestUtil.generateCertificate(
+ "CN=foo", KeyStoreTestUtil.generateKeyPair("RSA"), 30,
+ "SHA1withRSA")});
+ Assert.assertArrayEquals(defaultTrustManager.getAcceptedIssuers(),
+ trustManager.getAcceptedIssuers());
+ trustManager.checkClientTrusted(null, null);
+
+ // good case with our cert
+ X509Certificate[] certChain = castCertificateArrayToX509CertificateArray(
+ KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager.createChildKeyStore(appId, "password"), "password")
+ .getCertificateChain("server"));
+ trustManager.checkServerTrusted(certChain, "RSA");
+
+ // wrong application
+ certChain = castCertificateArrayToX509CertificateArray(
+ KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager.createChildKeyStore(appId2, "password"), "password")
+ .getCertificateChain("server"));
+ try {
+ trustManager.checkServerTrusted(certChain, "RSA");
+ Assert.fail("Should have thrown a CertificateException, but did not");
+ } catch (CertificateException ce) {
+ Assert.assertEquals("Expected to find Subject X500 Principal with CN=" +
+ appId + " but found CN=" + appId2, ce.getMessage());
+ }
+
+ // cert issued by another RM
+ ProxyCAManager proxyCAManager2 = new ProxyCAManager();
+ proxyCAManager2.init(new YarnConfiguration());
+ proxyCAManager2.start();
+ certChain = castCertificateArrayToX509CertificateArray(
+ KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager2.createChildKeyStore(appId, "password"), "password")
+ .getCertificateChain("server"));
+ Mockito.verify(defaultTrustManager, Mockito.times(0))
+ .checkServerTrusted(certChain, "RSA");
+ trustManager.checkServerTrusted(certChain, "RSA");
+ Mockito.verify(defaultTrustManager, Mockito.times(1))
+ .checkServerTrusted(certChain, "RSA");
+ proxyCAManager2.stop();
+
+ // "real" cert
+ certChain = new X509Certificate[]{
+ KeyStoreTestUtil.generateCertificate("CN=foo.com",
+ KeyStoreTestUtil.generateKeyPair("RSA"), 30, "SHA1withRSA")};
+ Mockito.verify(defaultTrustManager, Mockito.times(0))
+ .checkServerTrusted(certChain, "RSA");
+ trustManager.checkServerTrusted(certChain, "RSA");
+ Mockito.verify(defaultTrustManager, Mockito.times(1))
+ .checkServerTrusted(certChain, "RSA");
+
+ // "real" cert x2
+ certChain = new X509Certificate[]{
+ KeyStoreTestUtil.generateCertificate("CN=foo.com",
+ KeyStoreTestUtil.generateKeyPair("RSA"), 30, "SHA1withRSA"),
+ KeyStoreTestUtil.generateCertificate("CN=foo.com",
+ KeyStoreTestUtil.generateKeyPair("RSA"), 30, "SHA1withRSA")};
+ Mockito.verify(defaultTrustManager, Mockito.times(0))
+ .checkServerTrusted(certChain, "RSA");
+ trustManager.checkServerTrusted(certChain, "RSA");
+ Mockito.verify(defaultTrustManager, Mockito.times(1))
+ .checkServerTrusted(certChain, "RSA");
+
+ // unable to verify our certs
+ for (Exception e : new Exception[]{
+ new CertificateException(), new NoSuchAlgorithmException(),
+ new InvalidKeyException(), new SignatureException(),
+ new NoSuchProviderException()}) {
+ certChain = castCertificateArrayToX509CertificateArray(
+ KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager.createChildKeyStore(appId, "password"), "password")
+ .getCertificateChain("server"));
+ X509Certificate cert = Mockito.spy(certChain[0]);
+ certChain[0] = cert;
+ Mockito.doThrow(e).when(certChain[0]).verify(Mockito.any());
+ Mockito.verify(defaultTrustManager, Mockito.times(0))
+ .checkServerTrusted(certChain, "RSA");
+ trustManager.checkServerTrusted(certChain, "RSA");
+ Mockito.verify(defaultTrustManager, Mockito.times(1))
+ .checkServerTrusted(certChain, "RSA");
+ }
+
+ proxyCAManager.stop();
+ }
+
+ @Test
+ public void testCreateKeyManager() throws Exception {
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ proxyCAManager.init(new YarnConfiguration());
+ proxyCAManager.start();
+ X509KeyManager keyManager = proxyCAManager.getX509KeyManager();
+
+ Assert.assertArrayEquals(new String[]{"client"},
+ keyManager.getClientAliases(null, null));
+ Assert.assertEquals("client",
+ keyManager.chooseClientAlias(null, null, null));
+ Assert.assertNull(keyManager.getServerAliases(null, null));
+ Assert.assertNull(keyManager.chooseServerAlias(null, null, null));
+
+ byte[] truststoreBytes = proxyCAManager.getChildTrustStore("password");
+ KeyStore truststore = KeyStoreTestUtil.bytesToKeyStore(truststoreBytes,
+ "password");
+ Assert.assertEquals(1, truststore.size());
+ X509Certificate caCert =
+ (X509Certificate) truststore.getCertificate("client");
+ Assert.assertArrayEquals(new X509Certificate[]{caCert},
+ keyManager.getCertificateChain(null));
+
+ PrivateKey caPrivateKey = keyManager.getPrivateKey(null);
+ PublicKey caPublicKey = caCert.getPublicKey();
+ checkPrivatePublicKeys(caPrivateKey, caPublicKey);
+
+ proxyCAManager.stop();
+ }
+
+ @Test
+ public void testCreateHostnameVerifier() throws Exception {
+ ProxyCAManager proxyCAManager = new ProxyCAManager();
+ proxyCAManager.init(new YarnConfiguration());
+ proxyCAManager.start();
+ HostnameVerifier verifier = proxyCAManager.getHostnameVerifier();
+
+ // good case with our cert
+ SSLSession sslSession = Mockito.mock(SSLSession.class);
+ Mockito.when(sslSession.getPeerCertificates()).thenReturn(
+ KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager.createChildKeyStore(
+ ApplicationId.newInstance(System.currentTimeMillis(), 1),
+ "password"), "password").getCertificateChain("server"));
+ Assert.assertTrue(verifier.verify("foo", sslSession));
+
+ // throws SSLPeerUnverifiedException
+ sslSession = Mockito.mock(SSLSession.class);
+ Mockito.when(sslSession.getPeerCertificates()).thenThrow(
+ new SSLPeerUnverifiedException(""));
+ Assert.assertFalse(verifier.verify("foo", sslSession));
+
+ // cert issued by another RM
+ sslSession = Mockito.mock(SSLSession.class);
+ ProxyCAManager proxyCAManager2 = new ProxyCAManager();
+ proxyCAManager2.init(new YarnConfiguration());
+ proxyCAManager2.start();
+ Mockito.when(sslSession.getPeerCertificates()).thenReturn(
+ KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager2.createChildKeyStore(
+ ApplicationId.newInstance(System.currentTimeMillis(), 1),
+ "password"), "password").getCertificateChain("server"));
+ Assert.assertFalse(verifier.verify("foo", sslSession));
+ proxyCAManager2.stop();
+
+ // unable to verify our certs
+ for (Exception e : new Exception[]{
+ new CertificateException(), new NoSuchAlgorithmException(),
+ new InvalidKeyException(), new SignatureException(),
+ new NoSuchProviderException()}) {
+ sslSession = Mockito.mock(SSLSession.class);
+ Mockito.when(sslSession.getPeerCertificates()).thenAnswer(
+ new Answer() {
+ @Override
+ public Certificate[] answer(InvocationOnMock invocation)
+ throws Throwable {
+ Certificate[] certChain = KeyStoreTestUtil.bytesToKeyStore(
+ proxyCAManager.createChildKeyStore(
+ ApplicationId.newInstance(System.currentTimeMillis(), 1),
+ "password"), "password").getCertificateChain("server");
+ Certificate cert = Mockito.spy(certChain[0]);
+ certChain[0] = cert;
+ Mockito.doThrow(e).when(cert).verify(Mockito.any());
+ return certChain;
+ }
+ });
+ Assert.assertFalse(verifier.verify("foo", sslSession));
+ }
+
+ // good case with "real" cert on DefaultHostnameVerifier
+ sslSession = Mockito.mock(SSLSession.class);
+ Mockito.when(sslSession.getPeerCertificates()).thenAnswer(
+ new Answer() {
+ @Override
+ public Certificate[] answer(InvocationOnMock invocation)
+ throws Throwable {
+ Certificate[] certChain = new Certificate[]{
+ KeyStoreTestUtil.generateCertificate("CN=foo.com",
+ KeyStoreTestUtil.generateKeyPair("RSA"), 30, "SHA1withRSA")
+ };
+ return certChain;
+ }
+ });
+ Assert.assertTrue(verifier.verify("foo.com", sslSession));
+
+ // bad case with "real" cert on DefaultHostnameVerifier
+ sslSession = Mockito.mock(SSLSession.class);
+ Mockito.when(sslSession.getPeerCertificates()).thenAnswer(
+ new Answer() {
+ @Override
+ public Certificate[] answer(InvocationOnMock invocation)
+ throws Throwable {
+ Certificate[] certChain = new Certificate[]{
+ KeyStoreTestUtil.generateCertificate("CN=foo.com",
+ KeyStoreTestUtil.generateKeyPair("RSA"), 30, "SHA1withRSA")
+ };
+ return certChain;
+ }
+ });
+ Assert.assertFalse(verifier.verify("bar.com", sslSession));
+
+ proxyCAManager.stop();
+ }
+
+ private void checkCACert(X509Certificate caCert) {
+ Assert.assertEquals(caCert.getSubjectX500Principal().toString(),
+ caCert.getIssuerDN().toString());
+ Assert.assertEquals(caCert.getSubjectX500Principal().toString(),
+ caCert.getSubjectDN().toString());
+ Assert.assertTrue("Expected CA certificate X500 Principal to start with" +
+ " 'OU=YARN-', but did not: " + caCert.getSubjectX500Principal(),
+ caCert.getSubjectX500Principal().toString().startsWith("OU=YARN-"));
+ Assert.assertFalse("Found multiple fields in X500 Principal, when there " +
+ "should have only been one: " + caCert.getSubjectX500Principal(),
+ caCert.getSubjectX500Principal().toString().contains(","));
+ Assert.assertEquals("SHA512withRSA", caCert.getSigAlgName());
+ Assert.assertEquals(
+ new GregorianCalendar(2037, Calendar.DECEMBER, 31).getTime(),
+ caCert.getNotAfter());
+ Assert.assertTrue("Expected certificate to have started but was not: "
+ + caCert.getNotBefore(), caCert.getNotBefore().before(new Date()));
+ }
+
+ private void checkPrivatePublicKeys(PrivateKey privateKey,
+ PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException,
+ SignatureException {
+ byte[] data = new byte[2000];
+ new Random().nextBytes(data);
+ Signature signer = Signature.getInstance("SHA512withRSA");
+ signer.initSign(privateKey);
+ signer.update(data);
+ byte[] sig = signer.sign();
+ signer = Signature.getInstance("SHA512withRSA");
+ signer.initVerify(publicKey);
+ signer.update(data);
+ Assert.assertTrue(signer.verify(sig));
+ }
+
+ private X509Certificate[] castCertificateArrayToX509CertificateArray(
+ Certificate[] certs) {
+ return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
+ }
+}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java
index fc9738779db..12ff41d0635 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java
@@ -27,6 +27,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.ConnectException;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
@@ -54,15 +56,18 @@
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
import org.apache.hadoop.yarn.exceptions.YarnException;
+import org.apache.hadoop.yarn.webapp.MimeType;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -333,12 +338,13 @@ public void testWebAppProxyPassThroughHeaders() throws Exception {
assertEquals(proxyConn.getRequestProperties().size(), 4);
proxyConn.connect();
assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode());
- // Verify if number of headers received by end server is 8.
- // Eight headers include Accept, Host, Connection, User-Agent, Cookie,
- // Origin, Access-Control-Request-Method and
+ // Verify if number of headers received by end server is 9.
+ // This should match WebAppProxyServlet#PASS_THROUGH_HEADERS.
+ // Nine headers include Accept, Host, Connection, User-Agent, Cookie,
+ // Origin, Access-Control-Request-Method, Accept-Encoding, and
// Access-Control-Request-Headers. Pls note that Unknown-Header is dropped
// by proxy as it is not in the list of allowed headers.
- assertEquals(numberOfHeaders, 8);
+ assertEquals(numberOfHeaders, 9);
assertFalse(hasUnknownHeader);
} finally {
proxy.close();
@@ -383,6 +389,51 @@ public void testWebAppProxyServerMainMethod() throws Exception {
}
}
+ @Test(timeout=5000)
+ public void testCheckHttpsRequiredAndNotProvided() throws Exception {
+ HttpServletResponse resp = Mockito.mock(HttpServletResponse.class);
+ StringWriter sw = new StringWriter();
+ Mockito.when(resp.getWriter()).thenReturn(new PrintWriter(sw));
+ YarnConfiguration conf = new YarnConfiguration();
+ final URI httpLink = new URI("http://foo.com");
+ final URI httpsLink = new URI("https://foo.com");
+
+ // OFF policy
+ conf.set(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY, "OFF");
+ assertFalse(WebAppProxyServlet.checkHttpsRequiredAndNotProvided(
+ resp, httpsLink, conf));
+ assertEquals("", sw.toString());
+ Mockito.verify(resp, Mockito.times(0)).setContentType(Mockito.any());
+ assertFalse(WebAppProxyServlet.checkHttpsRequiredAndNotProvided(
+ resp, httpLink, conf));
+ assertEquals("", sw.toString());
+ Mockito.verify(resp, Mockito.times(0)).setContentType(Mockito.any());
+
+ // OPTIONAL policy
+ conf.set(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY, "OPTIONAL");
+ assertFalse(WebAppProxyServlet.checkHttpsRequiredAndNotProvided(
+ resp, httpsLink, conf));
+ assertEquals("", sw.toString());
+ Mockito.verify(resp, Mockito.times(0)).setContentType(Mockito.any());
+ assertFalse(WebAppProxyServlet.checkHttpsRequiredAndNotProvided(
+ resp, httpLink, conf));
+ assertEquals("", sw.toString());
+ Mockito.verify(resp, Mockito.times(0)).setContentType(Mockito.any());
+
+ // REQUIRED policy
+ conf.set(YarnConfiguration.RM_APPLICATION_HTTPS_POLICY, "REQUIRED");
+ assertFalse(WebAppProxyServlet.checkHttpsRequiredAndNotProvided(
+ resp, httpsLink, conf));
+ assertEquals("", sw.toString());
+ Mockito.verify(resp, Mockito.times(0)).setContentType(Mockito.any());
+ assertTrue(WebAppProxyServlet.checkHttpsRequiredAndNotProvided(
+ resp, httpLink, conf));
+ String s = sw.toString();
+ assertTrue("Was expecting an HTML page explaining that an HTTPS tracking" +
+ " url is required but found " + s, s.contains("HTTPS is required"));
+ Mockito.verify(resp, Mockito.times(1)).setContentType(MimeType.HTML);
+ }
+
private String readInputStream(InputStream input) throws Exception {
ByteArrayOutputStream data = new ByteArrayOutputStream();
byte[] buffer = new byte[512];