environment){
LinuxContainerRuntime runtime;
- if (DockerLinuxContainerRuntime.isDockerContainerRequested(env)){
+ if (DockerLinuxContainerRuntime.isDockerContainerRequested(environment)){
runtime = dockerLinuxContainerRuntime;
- } else {
+ } else if (javaSandboxLinuxContainerRuntime.isSandboxContainerRequested()) {
+ runtime = javaSandboxLinuxContainerRuntime;
+ } else {
runtime = defaultLinuxContainerRuntime;
}
@@ -81,12 +87,14 @@ public class DelegatingLinuxContainerRuntime implements LinuxContainerRuntime {
return runtime;
}
+ private LinuxContainerRuntime pickContainerRuntime(Container container) {
+ return pickContainerRuntime(container.getLaunchContext().getEnvironment());
+ }
+
@Override
public void prepareContainer(ContainerRuntimeContext ctx)
throws ContainerExecutionException {
- Container container = ctx.getContainer();
- LinuxContainerRuntime runtime = pickContainerRuntime(container);
-
+ LinuxContainerRuntime runtime = pickContainerRuntime(ctx.getContainer());
runtime.prepareContainer(ctx);
}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/JavaSandboxLinuxContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/JavaSandboxLinuxContainerRuntime.java
new file mode 100644
index 00000000000..6dc627b0de1
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/JavaSandboxLinuxContainerRuntime.java
@@ -0,0 +1,495 @@
+/*
+ * 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.nodemanager.
+ containermanager.linux.runtime;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.security.Groups;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext;
+import org.apache.log4j.Logger;
+
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.security.AllPermission;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.hadoop.fs.Path.SEPARATOR;
+import static org.apache.hadoop.util.Shell.SYSPROP_HADOOP_HOME_DIR;
+import static org.apache.hadoop.yarn.api.ApplicationConstants.Environment.JAVA_HOME;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_ID_STR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_RUN_CMDS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCALIZED_RESOURCES;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER;
+/**
+ * This class extends the {@link DefaultLinuxContainerRuntime} specifically
+ * for containers which run Java commands. It generates a new java security
+ * policy file per container and modifies the java command to enable the
+ * Java Security Manager with the generated policy.
+ *
+ * The behavior of the {@link JavaSandboxLinuxContainerRuntime} can be modified
+ * using the following settings:
+ *
+ *
+ * -
+ * {@value YarnConfiguration#YARN_CONTAINER_SANDBOX} :
+ * This yarn-site.xml setting has three options:
+ *
+ * - disabled - Default behavior. {@link LinuxContainerRuntime}
+ * is disabled
+ * - permissive - JVM containers will run with Java Security Manager
+ * enabled. Non-JVM containers will run normally
+ * - enforcing - JVM containers will run with Java Security Manager
+ * enabled. Non-JVM containers will be prevented from executing and an
+ * {@link ContainerExecutionException} will be thrown.
+ *
+ *
+ * -
+ * {@value YarnConfiguration#YARN_CONTAINER_SANDBOX_FILE_PERMISSIONS} :
+ * Determines the file permissions for the application directories. The
+ * permissions come in the form of comma separated values
+ * (e.g. read,write,execute,delete). Defaults to {@code read} for read-only.
+ *
+ * -
+ * {@value YarnConfiguration#YARN_CONTAINER_SANDBOX_POLICY} :
+ * Accepts canonical path to a java policy file on the local filesystem.
+ * This file will be loaded as the base policy, any additional container
+ * grants will be appended to this base file. If not specified, the default
+ * java.policy file provided with hadoop resources will be used.
+ *
+ * -
+ * {@value YarnConfiguration#YARN_CONTAINER_SANDBOX_WHITELIST_GROUP} :
+ * Optional setting to specify a YARN queue which will be exempt from the
+ * sand-boxing process.
+ *
+ *
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class JavaSandboxLinuxContainerRuntime
+ extends DefaultLinuxContainerRuntime {
+ private static final Log LOG =
+ LogFactory.getLog(DefaultLinuxContainerRuntime.class);
+ private Configuration configuration;
+ private SandboxMode sandboxMode;
+
+ public static final String POLICY_FILE_DIR = "nm-sandbox-policies";
+
+ private static Path policyFileDir;
+ private static final FileAttribute> POLICY_ATTR =
+ PosixFilePermissions.asFileAttribute(
+ PosixFilePermissions.fromString("rwxr-xr-x"));
+
+ private Map containerPolicies = new HashMap<>();
+
+ /**
+ * Create an instance using the given {@link PrivilegedOperationExecutor}
+ * instance for performing operations.
+ *
+ * @param privilegedOperationExecutor the {@link PrivilegedOperationExecutor}
+ * instance
+ */
+ public JavaSandboxLinuxContainerRuntime(
+ PrivilegedOperationExecutor privilegedOperationExecutor) {
+ super(privilegedOperationExecutor);
+ }
+
+ @Override
+ public void initialize(Configuration conf)
+ throws ContainerExecutionException {
+ this.configuration = conf;
+ this.sandboxMode =
+ SandboxMode.get(
+ this.configuration.get(YarnConfiguration.YARN_CONTAINER_SANDBOX,
+ YarnConfiguration.DEFAULT_YARN_CONTAINER_SANDBOX));
+
+ initializePolicyDir();
+
+ super.initialize(conf);
+ }
+
+ /**
+ * Initialize the Java Security Policy directory. Either creates the
+ * directory if it doesn't exist, or clears the contents of the directory if
+ * already created.
+ * @throws ContainerExecutionException If unable to resolve policy directory
+ */
+ private void initializePolicyDir() throws ContainerExecutionException {
+ String hadoopTempDir = configuration.get("hadoop.tmp.dir");
+ if (hadoopTempDir == null) {
+ throw new ContainerExecutionException("hadoop.tmp.dir not set!");
+ }
+ policyFileDir = Paths.get(hadoopTempDir, POLICY_FILE_DIR);
+ //Delete any existing policy files if the directory has already been created
+ if(Files.exists(policyFileDir)){
+ try (DirectoryStream stream =
+ Files.newDirectoryStream(policyFileDir)){
+ for(Path policyFile : stream){
+ Files.delete(policyFile);
+ }
+ }catch(IOException e){
+ throw new ContainerExecutionException("Unable to initialize policy "
+ + "directory: " + e);
+ }
+ } else {
+ try {
+ policyFileDir = Files.createDirectories(
+ Paths.get(hadoopTempDir, POLICY_FILE_DIR), POLICY_ATTR);
+ } catch (IOException e) {
+ throw new ContainerExecutionException("Unable to create policy file " +
+ "directory: " + e);
+ }
+ }
+ }
+
+ /**
+ * Prior to environment from being written locally need to generate
+ * policy file which limits container access to a small set of directories.
+ * Additionally the container run command needs to be modified to include
+ * flags to enable the java security manager with the generated policy.
+ *
+ * The Java Sandbox will be circumvented if the user is a member of the
+ * group specified in:
+ * {@value YarnConfiguration#YARN_CONTAINER_SANDBOX_WHITELIST_GROUP} and if
+ * they do not include the JVM flag:
+ * {@value NMContainerPolicyUtils#SECURITY_FLAG}
+ *
+ * @param ctx The {@link ContainerRuntimeContext} containing container
+ * setup properties.
+ * @throws ContainerExecutionException Exception thrown if temporary policy
+ * file directory can't be created, or if any exceptions occur during policy
+ * file parsing and generation.
+ */
+ @Override
+ public void prepareContainer(ContainerRuntimeContext ctx)
+ throws ContainerExecutionException {
+
+ @SuppressWarnings("unchecked")
+ List localDirs =
+ ctx.getExecutionAttribute(CONTAINER_LOCAL_DIRS);
+ @SuppressWarnings("unchecked")
+ Map> resources =
+ ctx.getExecutionAttribute(LOCALIZED_RESOURCES);
+ @SuppressWarnings("unchecked")
+ List commands =
+ ctx.getExecutionAttribute(CONTAINER_RUN_CMDS);
+ Map env =
+ ctx.getContainer().getLaunchContext().getEnvironment();
+
+ if(!isSandboxContainerWhitelisted(ctx, commands)) {
+ String tmpDirBase = configuration.get("hadoop.tmp.dir");
+ if (tmpDirBase == null) {
+ throw new ContainerExecutionException("hadoop.tmp.dir not set!");
+ }
+
+ OutputStream policyOutputStream = null;
+ try {
+ String containerID = ctx.getExecutionAttribute(CONTAINER_ID_STR);
+
+ Path policyFilePath = Files.createFile(
+ Paths.get(policyFileDir.toString(),
+ containerID + "-" + NMContainerPolicyUtils.POLICY_FILE),
+ POLICY_ATTR);
+ policyOutputStream = Files.newOutputStream(policyFilePath);
+
+ containerPolicies.put(containerID, policyFilePath);
+
+ NMContainerPolicyUtils.generatePolicyFile(
+ policyOutputStream, localDirs, resources, configuration);
+ NMContainerPolicyUtils.appendSecurityFlags(
+ commands, env, policyFilePath, sandboxMode);
+
+ } catch (Exception e) {
+ throw new ContainerExecutionException(e);
+ } finally {
+ IOUtils.cleanup(LOG, policyOutputStream);
+ }
+ }
+ }
+
+ @Override
+ public void launchContainer(ContainerRuntimeContext ctx)
+ throws ContainerExecutionException {
+ try {
+ super.launchContainer(ctx);
+ } finally {
+ deletePolicyFiles(ctx);
+ }
+ }
+
+ /**
+ * Determine if JVMSandboxLinuxContainerRuntime should be used. This is
+ * decided based on the value of
+ * {@value YarnConfiguration#YARN_CONTAINER_SANDBOX}
+ * @return true if Sandbox is requested, false otherwise
+ */
+ boolean isSandboxContainerRequested() {
+ return sandboxMode != SandboxMode.disabled;
+ }
+
+ /**
+ * Determine if the container should be whitelisted (i.e. exempt from the
+ * Java Security Manager).
+ * @param ctx The container runtime context for the requested container
+ * @param commands The list of run commands for the container
+ * @return boolean value denoting whether the container should be whitelisted.
+ * @throws ContainerExecutionException If container user can not be resolved
+ */
+ private boolean isSandboxContainerWhitelisted(ContainerRuntimeContext ctx,
+ List commands) throws ContainerExecutionException {
+ String whitelistGroup = configuration.get(
+ YarnConfiguration.YARN_CONTAINER_SANDBOX_WHITELIST_GROUP);
+ Groups groups = Groups.getUserToGroupsMappingService(configuration);
+ List userGroups;
+ boolean isWhitelisted = false;
+
+ try {
+ userGroups = groups.getGroups(ctx.getExecutionAttribute(USER));
+ } catch (IOException e) {
+ throw new ContainerExecutionException("Container user does not exist");
+ }
+
+ if(whitelistGroup != null && userGroups.contains(whitelistGroup)) {
+ // If any command has security flag, whitelisting is disabled
+ for(String cmd : commands) {
+ if(cmd.contains(NMContainerPolicyUtils.SECURITY_FLAG)){
+ isWhitelisted = false;
+ break;
+ } else {
+ isWhitelisted = true;
+ }
+ }
+ }
+ return isWhitelisted;
+ }
+
+ /**
+ * Deletes policy files for container specified by parameter. Additionally
+ * this method will age off any stale policy files generated by
+ * {@link JavaSandboxLinuxContainerRuntime}
+ * @param ctx Container context for files to be deleted
+ * @throws ContainerExecutionException if unable to access or delete policy
+ * files or generated policy file directory
+ */
+ private void deletePolicyFiles(ContainerRuntimeContext ctx)
+ throws ContainerExecutionException {
+ try {
+ Files.delete(containerPolicies.remove(
+ ctx.getExecutionAttribute(CONTAINER_ID_STR)));
+ } catch (IOException e) {
+ throw new ContainerExecutionException("Unable to delete policy file: "
+ + e);
+ }
+ }
+
+ /**
+ * Enumeration of the modes the JavaSandboxLinuxContainerRuntime can use.
+ * See {@link JavaSandboxLinuxContainerRuntime} for details on the
+ * behavior of each setting.
+ */
+ public enum SandboxMode {
+ enforcing("enforcing"),
+ permissive("permissive"),
+ disabled("disabled");
+
+ private final String mode;
+ SandboxMode(String mode){
+ this.mode = mode;
+ }
+
+ public static SandboxMode get(String mode) {
+
+ if(enforcing.mode.equals(mode)) {
+ return enforcing;
+ } else if(permissive.mode.equals(mode)) {
+ return permissive;
+ } else {
+ return disabled;
+ }
+ }
+
+ public String toString(){
+ return mode;
+ }
+ }
+
+ /**
+ * Static utility class defining String constants and static methods for the
+ * use of the {@link JavaSandboxLinuxContainerRuntime}.
+ */
+ static final class NMContainerPolicyUtils{
+
+ static final String POLICY_FILE = "java.policy";
+ static final String SECURITY_DEBUG = " -Djava.security.debug=all";
+ static final String SECURITY_FLAG = "-Djava.security.manager";
+ static final String POLICY_APPEND_FLAG = "-Djava.security.policy=";
+ static final String POLICY_FLAG = POLICY_APPEND_FLAG + "=";
+ static final String JAVA_CMD = "/bin/java ";
+ static final String JVM_SECURITY_CMD =
+ JAVA_CMD + SECURITY_FLAG + " " + POLICY_FLAG;
+
+ static final String STRIP_POLICY_FLAG = POLICY_APPEND_FLAG + "[^ ]+";
+ static final String CONTAINS_JAVA_CMD = "\\$" + JAVA_HOME + JAVA_CMD + ".*";
+ static final String CHAINED_COMMAND_REGEX =
+ "^.*(&&.+$)|(\\|\\|.+$).*$"; //Matches any occurrences of '||' or '&&'
+ static final String CLEAN_CMD_REGEX =
+ "(" + SECURITY_FLAG + ")|" +
+ "(" + STRIP_POLICY_FLAG + ")";
+
+ static final String FILE_PERMISSION_FORMAT = " permission "
+ + FilePermission.class.getCanonicalName()
+ + " \"%1$s" + SEPARATOR + "-\", \"%2$s\";%n";
+ static final String HADOOP_HOME_PERMISSION = "%ngrant codeBase \"file:"
+ + Paths.get(System.getProperty(SYSPROP_HADOOP_HOME_DIR))
+ + SEPARATOR + "-\" {%n" +
+ " permission " + AllPermission.class.getCanonicalName() + ";%n};%n";
+ static final Logger LOG =
+ Logger.getLogger(NMContainerPolicyUtils.class);
+
+ /**
+ * Write new policy file to policyOutStream which will include read access
+ * to localize resources. Optionally a default policyFilePath can be
+ * specified to append a custom policy implementation to the new policy file
+ * @param policyOutStream OutputStream pointing to java.policy file
+ * @param localDirs Container local directories
+ * @param resources List of local container resources
+ * @param conf YARN configuration
+ * @throws IOException - If policy file generation is unable to read the
+ * base policy file or if it is unable to create a new policy file.
+ */
+ static void generatePolicyFile(OutputStream policyOutStream,
+ List localDirs, Map> resources, Configuration conf)
+ throws IOException {
+
+ String policyFilePath =
+ conf.get(YarnConfiguration.YARN_CONTAINER_SANDBOX_POLICY);
+ String filePermissions =
+ conf.get(YarnConfiguration.YARN_CONTAINER_SANDBOX_FILE_PERMISSIONS,
+ YarnConfiguration.DEFAULT_YARN_CONTAINER_SANDBOX_FILE_PERMISSIONS);
+
+ Set cacheDirs = new HashSet<>();
+ for(org.apache.hadoop.fs.Path path : resources.keySet()) {
+ cacheDirs.add(path.getParent().toString());
+ }
+
+ if(policyFilePath == null) {
+ IOUtils.copyBytes(
+ NMContainerPolicyUtils.class.getResourceAsStream("/" + POLICY_FILE),
+ policyOutStream, conf, false);
+ } else {
+ Files.copy(Paths.get(policyFilePath), policyOutStream);
+ policyOutStream.flush();
+ }
+
+ Formatter filePermissionFormat = new Formatter(policyOutStream,
+ StandardCharsets.UTF_8.name());
+ filePermissionFormat.format(HADOOP_HOME_PERMISSION);
+ filePermissionFormat.format("grant {%n");
+ for(String localDir : localDirs) {
+ filePermissionFormat.format(
+ FILE_PERMISSION_FORMAT, localDir, filePermissions);
+ }
+ for(String cacheDir : cacheDirs) {
+ filePermissionFormat.format(
+ FILE_PERMISSION_FORMAT, cacheDir, filePermissions);
+ }
+ filePermissionFormat.format("};%n");
+ filePermissionFormat.flush();
+ }
+
+ /**
+ * Modify command to enable the Java Security Manager and specify
+ * java.policy file. Will modify the passed commands to strip any
+ * existing java security configurations. Expects a java command to be the
+ * first and only executable provided in enforcing mode. In passive mode
+ * any commands with '||' or '&&' will not be modified.
+ * @param commands List of container commands
+ * @param env Container environment variables
+ * @param policyPath Path to the container specific policy file
+ * @param sandboxMode (enforcing, permissive, disabled) Determines
+ * whether non-java containers will be launched
+ * @throws ContainerExecutionException - Exception thrown if
+ * JVM Sandbox enabled in 'enforcing' mode and a non-java command is
+ * provided in the list of commands
+ */
+ static void appendSecurityFlags(List commands,
+ Map env, Path policyPath, SandboxMode sandboxMode)
+ throws ContainerExecutionException {
+
+ for(int i = 0; i < commands.size(); i++){
+ String command = commands.get(i);
+ if(validateJavaHome(env.get(JAVA_HOME.name()))
+ && command.matches(CONTAINS_JAVA_CMD)
+ && !command.matches(CHAINED_COMMAND_REGEX)){
+ command = command.replaceAll(CLEAN_CMD_REGEX, "");
+ String securityString = JVM_SECURITY_CMD + policyPath + " ";
+ if(LOG.isDebugEnabled()) {
+ securityString += SECURITY_DEBUG;
+ }
+ commands.set(i, command.replaceFirst(JAVA_CMD, securityString));
+ } else if (sandboxMode == SandboxMode.enforcing){
+ throw new ContainerExecutionException(
+ "Only JVM containers permitted in YARN sandbox mode (enforcing). "
+ + "The following command can not be executed securely: " + command);
+ } else if (sandboxMode == SandboxMode.permissive){
+ LOG.warn("The container will run without the java security manager"
+ + " due to an unsupported container command. The command"
+ + " will be permitted to run in Sandbox permissive mode: "
+ + command);
+ }
+ }
+ }
+
+ private static boolean validateJavaHome(String containerJavaHome)
+ throws ContainerExecutionException{
+ if (System.getenv(JAVA_HOME.name()) == null){
+ throw new ContainerExecutionException(
+ "JAVA_HOME is not set for NodeManager");
+ }
+ if (containerJavaHome == null) {
+ throw new ContainerExecutionException(
+ "JAVA_HOME is not set for container");
+ }
+ return System.getenv(JAVA_HOME.name()).equals(containerJavaHome);
+ }
+ }
+}
diff --git a/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 b/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 0c1ec3e14af..2e632faccc1 100644
--- a/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
+++ b/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
@@ -67,6 +67,8 @@ public final class LinuxContainerRuntimeConstants {
String.class, "resources_options");
public static final Attribute TC_COMMAND_FILE = Attribute.attribute(
String.class, "tc_command_file");
+ public static final Attribute CONTAINER_RUN_CMDS = Attribute.attribute(
+ List.class, "container_run_cmds");
public static final Attribute CGROUP_RELATIVE_PATH = Attribute
.attribute(String.class, "cgroup_relative_path");
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerPrepareContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerPrepareContext.java
new file mode 100644
index 00000000000..f711da20a15
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerPrepareContext.java
@@ -0,0 +1,119 @@
+/*
+ * *
+ * 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.nodemanager.executor;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Encapsulates information required for preparing containers.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public final class ContainerPrepareContext {
+ private final Container container;
+ private final Map> localizedResources;
+ private final String user;
+ private final List containerLocalDirs;
+ private final List commands;
+
+ /**
+ * Builder for ContainerPrepareContext.
+ */
+ public static final class Builder {
+ private Container container;
+ private Map> localizedResources;
+ private String user;
+ private List containerLocalDirs;
+ private List commands;
+
+ public Builder() {
+ }
+
+ public ContainerPrepareContext.Builder setContainer(Container container) {
+ this.container = container;
+ return this;
+ }
+
+ public ContainerPrepareContext.Builder setLocalizedResources(Map> localizedResources) {
+ this.localizedResources = localizedResources;
+ return this;
+ }
+
+ public ContainerPrepareContext.Builder setUser(String user) {
+ this.user = user;
+ return this;
+ }
+ public ContainerPrepareContext.Builder setContainerLocalDirs(
+ List containerLocalDirs) {
+ this.containerLocalDirs = containerLocalDirs;
+ return this;
+ }
+
+ public ContainerPrepareContext build() {
+ return new ContainerPrepareContext(this);
+ }
+
+ public ContainerPrepareContext.Builder setCommands(List commands) {
+ this.commands = commands;
+ return this;
+ }
+ }
+
+ private ContainerPrepareContext(ContainerPrepareContext.Builder builder) {
+ this.container = builder.container;
+ this.localizedResources = builder.localizedResources;
+ this.user = builder.user;
+ this.containerLocalDirs = builder.containerLocalDirs;
+ this.commands = builder.commands;
+ }
+
+ public Container getContainer() {
+ return this.container;
+ }
+
+ public Map> getLocalizedResources() {
+ if (this.localizedResources != null) {
+ return Collections.unmodifiableMap(this.localizedResources);
+ } else {
+ return null;
+ }
+ }
+
+ public String getUser() {
+ return this.user;
+ }
+
+ public List getContainerLocalDirs() {
+ return Collections.unmodifiableList(this.containerLocalDirs);
+ }
+
+ public List getCommands(){
+ return this.commands;
+ }
+}
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/resources/java.policy b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/resources/java.policy
new file mode 100644
index 00000000000..d9320c2568a
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/resources/java.policy
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+// Standard extensions get all permissions by default
+grant codeBase "file:${{java.ext.dirs}}/*" {
+ permission java.security.AllPermission;
+};
+
+// default permissions granted to all domains
+grant {
+ permission java.lang.RuntimePermission "accessDeclaredMembers";
+
+ permission java.util.PropertyPermission "java.version", "read";
+ permission java.util.PropertyPermission "java.vendor", "read";
+ permission java.util.PropertyPermission "java.vendor.url", "read";
+ permission java.util.PropertyPermission "java.class.version", "read";
+ permission java.util.PropertyPermission "os.name", "read";
+ permission java.util.PropertyPermission "os.version", "read";
+ permission java.util.PropertyPermission "os.arch", "read";
+ permission java.util.PropertyPermission "file.separator", "read";
+ permission java.util.PropertyPermission "path.separator", "read";
+ permission java.util.PropertyPermission "line.separator", "read";
+
+ permission java.util.PropertyPermission "java.specification.version", "read";
+ permission java.util.PropertyPermission "java.specification.vendor", "read";
+ permission java.util.PropertyPermission "java.specification.name", "read";
+
+ permission java.util.PropertyPermission "java.vm.specification.version", "read";
+ permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
+ permission java.util.PropertyPermission "java.vm.specification.name", "read";
+ permission java.util.PropertyPermission "java.vm.version", "read";
+ permission java.util.PropertyPermission "java.vm.vendor", "read";
+ permission java.util.PropertyPermission "java.vm.name", "read";
+
+ //additional hadoop permissions
+ permission java.util.PropertyPermission "awt.Toolkit", "read";
+ permission java.util.PropertyPermission "file.encoding", "read";
+ permission java.util.PropertyPermission "file.encoding.pkg", "read";
+ permission java.util.PropertyPermission "hadoop.metrics.log.level", "read";
+ permission java.util.PropertyPermission "hadoop.root.logger", "read";
+ permission java.util.PropertyPermission "java.awt.graphicsenv" ,"read";
+ permission java.util.PropertyPermission "java.awt.printerjob", "read";
+ permission java.util.PropertyPermission "java.class.path", "read";
+ permission java.util.PropertyPermission "yarn.app.container.log.dir", "read";
+ permission java.util.PropertyPermission "yarn.app.container.log.filesize", "read";
+ permission java.lang.RuntimePermission "loadLibrary.gplcompression";
+ permission javax.security.auth.AuthPermission "getSubject";
+};
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestJavaSandboxLinuxContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestJavaSandboxLinuxContainerRuntime.java
new file mode 100644
index 00000000000..e482c8da68c
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestJavaSandboxLinuxContainerRuntime.java
@@ -0,0 +1,364 @@
+/*
+ * 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.nodemanager.
+ containermanager.linux.runtime;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.hadoop.yarn.api.ApplicationConstants.Environment.JAVA_HOME;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils.CHAINED_COMMAND_REGEX;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils.CLEAN_CMD_REGEX;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils.CONTAINS_JAVA_CMD;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils.POLICY_FILE;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils.POLICY_FLAG;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils.SECURITY_FLAG;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.JavaSandboxLinuxContainerRuntime.POLICY_FILE_DIR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.APPID;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_ID_STR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_RUN_CMDS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_WORK_DIR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.FILECACHE_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCALIZED_RESOURCES;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOG_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.RUN_AS_USER;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER_LOCAL_DIRS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test policy file generation and policy enforcement for the
+ * {@link JavaSandboxLinuxContainerRuntime}.
+ */
+public class TestJavaSandboxLinuxContainerRuntime {
+
+ private static final String HADOOP_HOME = "hadoop.home.dir";
+ private static String hadoopHomeDir = System.getProperty(HADOOP_HOME);
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ private static File grantFile, denyFile, policyFile,
+ grantDir, denyDir, containerDir;
+ private static java.nio.file.Path policyFilePath;
+ private static SecurityManager securityManager;
+ private Map> resources;
+ private Map env;
+ private List whitelistGroup;
+
+ private PrivilegedOperationExecutor mockExecutor;
+ private JavaSandboxLinuxContainerRuntime runtime;
+ private ContainerRuntimeContext.Builder runtimeContextBuilder;
+ private Configuration conf;
+
+ private final static String NORMAL_USER = System.getProperty("user.name");
+ private final static String NORMAL_GROUP = "normalGroup";
+ private final static String WHITELIST_USER = "picard";
+ private final static String WHITELIST_GROUP = "captains";
+ private final static String CONTAINER_ID = "container_1234567890";
+ private final static String APPLICATION_ID = "application_1234567890";
+
+ @Before
+ public void setup() throws Exception {
+
+ File baseTestDirectory = new File(System.getProperty("test.build.data",
+ System.getProperty("java.io.tmpdir", "target")),
+ TestJavaSandboxLinuxContainerRuntime.class.getName());
+
+ whitelistGroup = new ArrayList<>();
+ whitelistGroup.add(WHITELIST_GROUP);
+
+ conf = new Configuration();
+ conf.set(CommonConfigurationKeys.HADOOP_USER_GROUP_STATIC_OVERRIDES,
+ WHITELIST_USER + "=" + WHITELIST_GROUP + ";"
+ + NORMAL_USER + "=" + NORMAL_GROUP + ";");
+ conf.set(YarnConfiguration.YARN_CONTAINER_SANDBOX_WHITELIST_GROUP,
+ WHITELIST_GROUP);
+ conf.set("hadoop.tmp.dir", baseTestDirectory.getAbsolutePath());
+
+ Files.deleteIfExists(Paths.get(baseTestDirectory.getAbsolutePath(),
+ POLICY_FILE_DIR, CONTAINER_ID + "-" + POLICY_FILE));
+
+ mockExecutor = mock(PrivilegedOperationExecutor.class);
+ runtime = new JavaSandboxLinuxContainerRuntime(mockExecutor);
+ runtime.initialize(conf);
+
+ resources = new HashMap<>();
+ grantDir = new File(baseTestDirectory, "grantDir");
+ denyDir = new File(baseTestDirectory, "denyDir");
+ containerDir = new File(baseTestDirectory,
+ APPLICATION_ID + Path.SEPARATOR + CONTAINER_ID);
+ grantDir.mkdirs();
+ denyDir.mkdirs();
+ containerDir.mkdirs();
+
+ grantFile = File.createTempFile("grantFile", "tmp", grantDir);
+ denyFile = File.createTempFile("denyFile", "tmp", denyDir);
+
+ List symLinks = new ArrayList<>();
+ symLinks.add(grantFile.getName());
+ resources.put(new Path(grantFile.getCanonicalPath()), symLinks);
+
+ env = new HashMap();
+ env.put(JAVA_HOME.name(), System.getenv(JAVA_HOME.name()));
+
+ policyFile = File.createTempFile("java", "policy", containerDir);
+ policyFilePath = Paths.get(policyFile.getAbsolutePath());
+
+ runtimeContextBuilder = createRuntimeContext();
+
+ if (hadoopHomeDir == null) {
+ System.setProperty(HADOOP_HOME, policyFile.getParent());
+ }
+
+ OutputStream outStream = new FileOutputStream(policyFile);
+ JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils
+ .generatePolicyFile(outStream, symLinks, resources, conf);
+ outStream.close();
+
+ System.setProperty("java.security.policy", policyFile.getCanonicalPath());
+ securityManager = new SecurityManager();
+
+ }
+
+ public ContainerRuntimeContext.Builder createRuntimeContext(){
+
+ Container container = mock(Container.class);
+ ContainerLaunchContext ctx = mock(ContainerLaunchContext.class);
+
+ when(container.getLaunchContext()).thenReturn(ctx);
+ when(ctx.getEnvironment()).thenReturn(env);
+
+ ContainerRuntimeContext.Builder builder =
+ new ContainerRuntimeContext.Builder(container);
+
+ List localDirs = new ArrayList<>();
+
+ builder.setExecutionAttribute(LOCALIZED_RESOURCES, resources)
+ .setExecutionAttribute(RUN_AS_USER, NORMAL_USER)
+ .setExecutionAttribute(CONTAINER_ID_STR, CONTAINER_ID)
+ .setExecutionAttribute(APPID, APPLICATION_ID)
+ .setExecutionAttribute(CONTAINER_WORK_DIR,
+ new Path(containerDir.toString()))
+ .setExecutionAttribute(LOCAL_DIRS, localDirs)
+ .setExecutionAttribute(LOG_DIRS, localDirs)
+ .setExecutionAttribute(FILECACHE_DIRS, localDirs)
+ .setExecutionAttribute(USER_LOCAL_DIRS, localDirs)
+ .setExecutionAttribute(CONTAINER_LOCAL_DIRS, localDirs)
+ .setExecutionAttribute(CONTAINER_RUN_CMDS, localDirs);
+
+ return builder;
+ }
+
+ @Test
+ public void testGrant() throws Exception {
+ FilePermission grantPermission =
+ new FilePermission(grantFile.getAbsolutePath(), "read");
+ securityManager.checkPermission(grantPermission);
+ }
+
+ @Test
+ public void testDeny() throws Exception {
+ FilePermission denyPermission =
+ new FilePermission(denyFile.getAbsolutePath(), "read");
+ exception.expect(java.security.AccessControlException.class);
+ securityManager.checkPermission(denyPermission);
+ }
+
+ @Test
+ public void testEnforcingMode() throws ContainerExecutionException {
+ String[] nonJavaCommands = {
+ "bash malicious_script.sh",
+ "python malicious_script.py"
+ };
+
+ List commands = Arrays.asList(nonJavaCommands);
+ exception.expect(ContainerExecutionException.class);
+ JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils
+ .appendSecurityFlags(commands, env, policyFilePath,
+ JavaSandboxLinuxContainerRuntime.SandboxMode.enforcing);
+
+ }
+
+ @Test
+ public void testPermissiveMode() throws ContainerExecutionException {
+ String[] nonJavaCommands = {
+ "bash non-java-script.sh",
+ "python non-java-script.py"
+ };
+
+ List commands = Arrays.asList(nonJavaCommands);
+ JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils
+ .appendSecurityFlags(commands, env, policyFilePath,
+ JavaSandboxLinuxContainerRuntime.SandboxMode.permissive);
+
+ }
+
+ @Test
+ public void testDisabledSandboxWithWhitelist()
+ throws ContainerExecutionException {
+
+ String[] inputCommand = {
+ "java jar MyJob.jar"
+ };
+ List commands = Arrays.asList(inputCommand);
+
+ runtimeContextBuilder.setExecutionAttribute(USER, WHITELIST_USER);
+ runtimeContextBuilder.setExecutionAttribute(CONTAINER_RUN_CMDS, commands);
+ runtime.prepareContainer(runtimeContextBuilder.build());
+
+ Assert.assertTrue("Command should not be modified when user is " +
+ "member of whitelisted group",
+ inputCommand[0].equals(commands.get(0)));
+
+ }
+
+ @Test
+ public void testEnabledSandboxWithWhitelist()
+ throws ContainerExecutionException{
+ String[] inputCommand = {
+ "$JAVA_HOME/bin/java jar -Djava.security.manager MyJob.jar"
+ };
+ List commands = Arrays.asList(inputCommand);
+
+ runtimeContextBuilder.setExecutionAttribute(USER, WHITELIST_USER);
+ runtimeContextBuilder.setExecutionAttribute(CONTAINER_RUN_CMDS, commands);
+ runtime.prepareContainer(runtimeContextBuilder.build());
+
+ Assert.assertTrue("Command should be modified to include " +
+ "policy file in whitelisted Sandbox mode",
+ commands.get(0).contains(SECURITY_FLAG)
+ && commands.get(0).contains(POLICY_FLAG));
+ }
+
+ @Test
+ public void testDeniedWhitelistGroup() throws ContainerExecutionException {
+
+ String[] inputCommand = {
+ "$JAVA_HOME/bin/java jar MyJob.jar"
+ };
+ List commands = Arrays.asList(inputCommand);
+
+ runtimeContextBuilder.setExecutionAttribute(USER, NORMAL_USER);
+ runtimeContextBuilder.setExecutionAttribute(CONTAINER_RUN_CMDS, commands);
+ runtime.prepareContainer(runtimeContextBuilder.build());
+
+ Assert.assertTrue("Java security manager must be enabled for "
+ + "unauthorized users",
+ commands.get(0).contains(SECURITY_FLAG));
+ }
+
+ @Test
+ public void testChainedCmdRegex(){
+ Assert.assertTrue("cmd1 && cmd2 || cmd3".matches(CHAINED_COMMAND_REGEX));
+ Assert.assertFalse("cmd1 &> logfile".matches(CHAINED_COMMAND_REGEX));
+ }
+
+ @Test
+ public void testContainsJavaRegex(){
+ String[] javaCmds = {
+ "$JAVA_HOME/bin/java -cp App.jar AppClass",
+ "$JAVA_HOME/bin/java -jar App.jar AppClass &> logfile"
+ };
+ String[] nonJavaCmds = {
+ "$JAVA_HOME/bin/jajavava -cp App.jar AppClass",
+ "/nm/app/container/usercache/badjava -cp Bad.jar ChaosClass"
+ };
+ for(String javaCmd : javaCmds) {
+ Assert.assertTrue(javaCmd.matches(CONTAINS_JAVA_CMD));
+ }
+ for(String nonJavaCmd : nonJavaCmds) {
+ Assert.assertFalse(nonJavaCmd.matches(CONTAINS_JAVA_CMD));
+ }
+ }
+
+ @Test
+ public void testCleanCmdRegex(){
+ String[] securityManagerCmds = {
+ "/usr/bin/java -Djava.security.manager -cp $CLASSPATH $MainClass",
+ "-Djava.security.manager -Djava.security.policy==testpolicy keepThis"
+ };
+ String[] cleanedCmdsResult = {
+ "/usr/bin/java -cp $CLASSPATH $MainClass",
+ "keepThis"
+ };
+ for(int i = 0; i < securityManagerCmds.length; i++){
+ Assert.assertEquals(
+ securityManagerCmds[i].replaceAll(CLEAN_CMD_REGEX, "").trim(),
+ cleanedCmdsResult[i]);
+ }
+ }
+
+ @Test
+ public void testAppendSecurityFlags() throws ContainerExecutionException {
+ String securityString = "-Djava.security.manager -Djava.security.policy=="
+ + policyFile.getAbsolutePath();
+ String[] badCommands = {
+ "$JAVA_HOME/bin/java -Djava.security.manager "
+ + "-Djava.security.policy=/home/user/java.policy",
+ "$JAVA_HOME/bin/java -cp MyApp.jar MrAppMaster"
+ };
+ String[] cleanCommands = {
+ "$JAVA_HOME/bin/java " + securityString,
+ "$JAVA_HOME/bin/java " + securityString
+ + " -cp MyApp.jar MrAppMaster"
+ };
+
+ List commands = Arrays.asList(badCommands);
+ JavaSandboxLinuxContainerRuntime.NMContainerPolicyUtils
+ .appendSecurityFlags(commands, env, policyFilePath,
+ JavaSandboxLinuxContainerRuntime.SandboxMode.enforcing);
+
+ for(int i = 0; i < commands.size(); i++) {
+ Assert.assertTrue(commands.get(i).trim().equals(cleanCommands[i].trim()));
+ }
+ }
+
+ @After
+ public void cleanup(){
+ System.setSecurityManager(null);
+ }
+}
\ No newline at end of file