YARN-1972. Added a secure container-executor for Windows. Contributed by Remus Rusanu.

commit ba7f31c2ee is the corresponding trunk commit, this is a slightly different patch for branch-2.
This commit is contained in:
Vinod Kumar Vavilapalli 2014-10-01 17:04:08 -07:00
parent 625456746c
commit 3326fba382
12 changed files with 459 additions and 43 deletions

View File

@ -94,6 +94,9 @@ Release 2.6.0 - UNRELEASED
YARN-2613. Support retry in NMClient for rolling-upgrades. (Jian He via
junping_du)
YARN-1972. Added a secure container-executor for Windows. (Remus Rusanu via
vinodkv)
IMPROVEMENTS
YARN-2242. Improve exception information on AM launch crashes. (Li Lu

View File

@ -928,6 +928,12 @@ public class YarnConfiguration extends Configuration {
public static final long DEFAULT_NM_LINUX_CONTAINER_CGROUPS_DELETE_TIMEOUT =
1000;
/**
/* The Windows group that the windows-secure-container-executor should run as.
*/
public static final String NM_WINDOWS_SECURE_CONTAINER_GROUP =
NM_PREFIX + "windows-secure-container-executor.group";
/** T-file compression types used to compress aggregated logs.*/
public static final String NM_LOG_AGG_COMPRESSION_TYPE =

View File

@ -78,6 +78,20 @@ public abstract class ContainerExecutor implements Configurable {
*/
public abstract void init() throws IOException;
/**
* On Windows the ContainerLaunch creates a temporary empty jar to workaround the CLASSPATH length
* In a secure cluster this jar must be localized so that the container has access to it
* This function localizes on-demand the jar.
*
* @param classPathJar
* @param owner
* @throws IOException
*/
public void localizeClasspathJar(Path classPathJar, String owner) throws IOException {
// For the default container this is a no-op
// The WindowsSecureContainerExecutor overrides this
}
/**
* Prepare the environment for containers in this application to execute.
* For $x in local.dirs
@ -264,8 +278,8 @@ public abstract class ContainerExecutor implements Configurable {
* and associate the given groupId in a process group. On
* non-Windows, groupId is ignored.
*/
protected static String[] getRunCommand(String command, String groupId,
Configuration conf) {
protected String[] getRunCommand(String command, String groupId,
String userName, Path pidFile, Configuration conf) {
int containerSchedPriorityAdjustment =
YarnConfiguration.DEFAULT_NM_CONTAINER_EXECUTOR_SCHED_PRIORITY;
if (conf.get(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY) !=
@ -389,5 +403,4 @@ public abstract class ContainerExecutor implements Configurable {
}
}
}
}

View File

@ -61,7 +61,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
private static final int WIN_MAX_PATH = 260;
private final FileContext lfs;
protected final FileContext lfs;
public DefaultContainerExecutor() {
try {
@ -75,11 +75,19 @@ public class DefaultContainerExecutor extends ContainerExecutor {
this.lfs = lfs;
}
protected void copyFile(Path src, Path dst, String owner) throws IOException {
lfs.util().copy(src, dst);
}
protected void setScriptExecutable(Path script, String owner) throws IOException {
lfs.setPermission(script, ContainerExecutor.TASK_LAUNCH_SCRIPT_PERMISSION);
}
@Override
public void init() throws IOException {
// nothing to do or verify here
}
@Override
public synchronized void startLocalizer(Path nmPrivateContainerTokensPath,
InetSocketAddress nmAddr, String user, String appId, String locId,
@ -93,14 +101,14 @@ public class DefaultContainerExecutor extends ContainerExecutor {
createUserLocalDirs(localDirs, user);
createUserCacheDirs(localDirs, user);
createAppDirs(localDirs, user, appId);
createAppLogDirs(appId, logDirs);
createAppLogDirs(appId, logDirs, user);
// TODO: Why pick first app dir. The same in LCE why not random?
Path appStorageDir = getFirstApplicationDir(localDirs, user, appId);
String tokenFn = String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId);
Path tokenDst = new Path(appStorageDir, tokenFn);
lfs.util().copy(nmPrivateContainerTokensPath, tokenDst);
copyFile(nmPrivateContainerTokensPath, tokenDst, user);
LOG.info("Copying from " + nmPrivateContainerTokensPath + " to " + tokenDst);
lfs.setWorkingDirectory(appStorageDir);
LOG.info("CWD set to " + appStorageDir + " = " + lfs.getWorkingDirectory());
@ -129,30 +137,29 @@ public class DefaultContainerExecutor extends ContainerExecutor {
Path appCacheDir = new Path(userdir, ContainerLocalizer.APPCACHE);
Path appDir = new Path(appCacheDir, appIdStr);
Path containerDir = new Path(appDir, containerIdStr);
createDir(containerDir, dirPerm, true);
createDir(containerDir, dirPerm, true, userName);
}
// Create the container log-dirs on all disks
createContainerLogDirs(appIdStr, containerIdStr, logDirs);
createContainerLogDirs(appIdStr, containerIdStr, logDirs, userName);
Path tmpDir = new Path(containerWorkDir,
YarnConfiguration.DEFAULT_CONTAINER_TEMP_DIR);
createDir(tmpDir, dirPerm, false);
createDir(tmpDir, dirPerm, false, userName);
// copy launch script to work dir
Path launchDst =
new Path(containerWorkDir, ContainerLaunch.CONTAINER_SCRIPT);
lfs.util().copy(nmPrivateContainerScriptPath, launchDst);
copyFile(nmPrivateContainerScriptPath, launchDst, userName);
// copy container tokens to work dir
Path tokenDst =
new Path(containerWorkDir, ContainerLaunch.FINAL_CONTAINER_TOKENS_FILE);
lfs.util().copy(nmPrivateTokensPath, tokenDst);
copyFile(nmPrivateTokensPath, tokenDst, userName);
// Create new local launch wrapper script
LocalWrapperScriptBuilder sb = Shell.WINDOWS ?
new WindowsLocalWrapperScriptBuilder(containerIdStr, containerWorkDir) :
new UnixLocalWrapperScriptBuilder(containerWorkDir);
LocalWrapperScriptBuilder sb = getLocalWrapperScriptBuilder(
containerIdStr, containerWorkDir);
// Fail fast if attempting to launch the wrapper script would fail due to
// Windows path length limitation.
@ -178,14 +185,12 @@ public class DefaultContainerExecutor extends ContainerExecutor {
// fork script
ShellCommandExecutor shExec = null;
try {
lfs.setPermission(launchDst,
ContainerExecutor.TASK_LAUNCH_SCRIPT_PERMISSION);
lfs.setPermission(sb.getWrapperScriptPath(),
ContainerExecutor.TASK_LAUNCH_SCRIPT_PERMISSION);
setScriptExecutable(launchDst, userName);
setScriptExecutable(sb.getWrapperScriptPath(), userName);
// Setup command to run
String[] command = getRunCommand(sb.getWrapperScriptPath().toString(),
containerIdStr, this.getConf());
containerIdStr, userName, pidFile, this.getConf());
LOG.info("launchContainer: " + Arrays.toString(command));
shExec = new ShellCommandExecutor(
@ -241,7 +246,14 @@ public class DefaultContainerExecutor extends ContainerExecutor {
return 0;
}
private abstract class LocalWrapperScriptBuilder {
protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder(
String containerIdStr, Path containerWorkDir) {
return Shell.WINDOWS ?
new WindowsLocalWrapperScriptBuilder(containerIdStr, containerWorkDir) :
new UnixLocalWrapperScriptBuilder(containerWorkDir);
}
protected abstract class LocalWrapperScriptBuilder {
private final Path wrapperScriptPath;
@ -449,7 +461,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
* $logdir/$user/$appId */
static final short LOGDIR_PERM = (short)0710;
private Path getFirstApplicationDir(List<String> localDirs, String user,
protected Path getFirstApplicationDir(List<String> localDirs, String user,
String appId) {
return getApplicationDir(new Path(localDirs.get(0)), user, appId);
}
@ -472,8 +484,8 @@ public class DefaultContainerExecutor extends ContainerExecutor {
ContainerLocalizer.FILECACHE);
}
private void createDir(Path dirPath, FsPermission perms,
boolean createParent) throws IOException {
protected void createDir(Path dirPath, FsPermission perms,
boolean createParent, String user) throws IOException {
lfs.mkdir(dirPath, perms, createParent);
if (!perms.equals(perms.applyUMask(lfs.getUMask()))) {
lfs.setPermission(dirPath, perms);
@ -493,7 +505,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
for (String localDir : localDirs) {
// create $local.dir/usercache/$user and its immediate parent
try {
createDir(getUserCacheDir(new Path(localDir), user), userperms, true);
createDir(getUserCacheDir(new Path(localDir), user), userperms, true, user);
} catch (IOException e) {
LOG.warn("Unable to create the user directory : " + localDir, e);
continue;
@ -529,7 +541,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
Path localDirPath = new Path(localDir);
final Path appDir = getAppcacheDir(localDirPath, user);
try {
createDir(appDir, appCachePerms, true);
createDir(appDir, appCachePerms, true, user);
appcacheDirStatus = true;
} catch (IOException e) {
LOG.warn("Unable to create app cache directory : " + appDir, e);
@ -537,7 +549,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
// create $local.dir/usercache/$user/filecache
final Path distDir = getFileCacheDir(localDirPath, user);
try {
createDir(distDir, fileperms, true);
createDir(distDir, fileperms, true, user);
distributedCacheDirStatus = true;
} catch (IOException e) {
LOG.warn("Unable to create file cache directory : " + distDir, e);
@ -570,7 +582,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
Path fullAppDir = getApplicationDir(new Path(localDir), user, appId);
// create $local.dir/usercache/$user/appcache/$appId
try {
createDir(fullAppDir, appperms, true);
createDir(fullAppDir, appperms, true, user);
initAppDirStatus = true;
} catch (IOException e) {
LOG.warn("Unable to create app directory " + fullAppDir.toString(), e);
@ -586,7 +598,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
/**
* Create application log directories on all disks.
*/
void createAppLogDirs(String appId, List<String> logDirs)
void createAppLogDirs(String appId, List<String> logDirs, String user)
throws IOException {
boolean appLogDirStatus = false;
@ -595,7 +607,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
// create $log.dir/$appid
Path appLogDir = new Path(rootLogDir, appId);
try {
createDir(appLogDir, appLogDirPerms, true);
createDir(appLogDir, appLogDirPerms, true, user);
} catch (IOException e) {
LOG.warn("Unable to create the app-log directory : " + appLogDir, e);
continue;
@ -612,7 +624,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
* Create application log directories on all disks.
*/
void createContainerLogDirs(String appId, String containerId,
List<String> logDirs) throws IOException {
List<String> logDirs, String user) throws IOException {
boolean containerLogDirStatus = false;
FsPermission containerLogDirPerms = new FsPermission(LOGDIR_PERM);
@ -621,7 +633,7 @@ public class DefaultContainerExecutor extends ContainerExecutor {
Path appLogDir = new Path(rootLogDir, appId);
Path containerLogDir = new Path(appLogDir, containerId);
try {
createDir(containerLogDir, containerLogDirPerms, true);
createDir(containerLogDir, containerLogDirPerms, true, user);
} catch (IOException e) {
LOG.warn("Unable to create the container-log directory : "
+ appLogDir, e);

View File

@ -218,15 +218,7 @@ public class LinuxContainerExecutor extends ContainerExecutor {
if (javaLibPath != null) {
command.add("-Djava.library.path=" + javaLibPath);
}
command.add(ContainerLocalizer.class.getName());
command.add(user);
command.add(appId);
command.add(locId);
command.add(nmAddr.getHostName());
command.add(Integer.toString(nmAddr.getPort()));
for (String dir : localDirs) {
command.add(dir);
}
ContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr, localDirs);
String[] commandArray = command.toArray(new String[command.size()]);
ShellCommandExecutor shExec = new ShellCommandExecutor(commandArray);
if (LOG.isDebugEnabled()) {

View File

@ -0,0 +1,171 @@
/**
* 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;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.Shell.ShellCommandExecutor;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor.LocalWrapperScriptBuilder;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer;
/**
* Windows secure container executor. Uses winutils task createAsUser.
*
*/
public class WindowsSecureContainerExecutor extends DefaultContainerExecutor {
private static final Log LOG = LogFactory
.getLog(WindowsSecureContainerExecutor.class);
private class WindowsSecureWrapperScriptBuilder
extends LocalWrapperScriptBuilder {
public WindowsSecureWrapperScriptBuilder(Path containerWorkDir) {
super(containerWorkDir);
}
@Override
protected void writeLocalWrapperScript(Path launchDst, Path pidFile, PrintStream pout) {
pout.format("@call \"%s\"", launchDst);
}
}
private String nodeManagerGroup;
@Override
public void setConf(Configuration conf) {
super.setConf(conf);
nodeManagerGroup = conf.get(YarnConfiguration.NM_WINDOWS_SECURE_CONTAINER_GROUP);
}
@Override
protected String[] getRunCommand(String command, String groupId,
String userName, Path pidFile, Configuration conf) {
return new String[] { Shell.WINUTILS, "task", "createAsUser", groupId, userName,
pidFile.toString(), "cmd /c " + command };
}
@Override
protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder(
String containerIdStr, Path containerWorkDir) {
return new WindowsSecureWrapperScriptBuilder(containerWorkDir);
}
@Override
protected void copyFile(Path src, Path dst, String owner) throws IOException {
super.copyFile(src, dst, owner);
lfs.setOwner(dst, owner, nodeManagerGroup);
}
@Override
protected void createDir(Path dirPath, FsPermission perms,
boolean createParent, String owner) throws IOException {
super.createDir(dirPath, perms, createParent, owner);
lfs.setOwner(dirPath, owner, nodeManagerGroup);
}
@Override
protected void setScriptExecutable(Path script, String owner) throws IOException {
super.setScriptExecutable(script, null);
lfs.setOwner(script, owner, nodeManagerGroup);
}
@Override
public void localizeClasspathJar(Path classpathJar, String owner) throws IOException {
lfs.setOwner(classpathJar, owner, nodeManagerGroup);
}
@Override
public void startLocalizer(Path nmPrivateContainerTokens,
InetSocketAddress nmAddr, String user, String appId, String locId,
List<String> localDirs, List<String> logDirs) throws IOException,
InterruptedException {
createUserLocalDirs(localDirs, user);
createUserCacheDirs(localDirs, user);
createAppDirs(localDirs, user, appId);
createAppLogDirs(appId, logDirs, user);
// TODO: Why pick first app dir. The same in LCE why not random?
Path appStorageDir = getFirstApplicationDir(localDirs, user, appId);
String tokenFn = String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId);
Path tokenDst = new Path(appStorageDir, tokenFn);
LOG.info("Copying from " + nmPrivateContainerTokens + " to " + tokenDst);
copyFile(nmPrivateContainerTokens, tokenDst, user);
List<String> command ;
String[] commandArray;
ShellCommandExecutor shExec;
File cwdApp = new File(appStorageDir.toString());
LOG.info(String.format("cwdApp: %s", cwdApp));
command = new ArrayList<String>();
command.add(Shell.WINUTILS);
command.add("task");
command.add("createAsUser");
command.add("START_LOCALIZER_" + locId);
command.add(user);
command.add("nul:"); // PID file
//use same jvm as parent
File jvm = new File(new File(System.getProperty("java.home"), "bin"), "java.exe");
command.add(jvm.toString());
// Build a temp classpath jar. See ContainerLaunch.sanitizeEnv().
// Passing CLASSPATH explicitly is *way* too long for command line.
String classPath = System.getProperty("java.class.path");
Map<String, String> env = new HashMap<String, String>(System.getenv());
String classPathJar = FileUtil.createJarWithClassPath(classPath, appStorageDir, env);
localizeClasspathJar(new Path(classPathJar), user);
command.add("-classpath");
command.add(classPathJar);
String javaLibPath = System.getProperty("java.library.path");
if (javaLibPath != null) {
command.add("-Djava.library.path=" + javaLibPath);
}
ContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr, localDirs);
commandArray = command.toArray(new String[command.size()]);
shExec = new ShellCommandExecutor(
commandArray, cwdApp);
shExec.execute();
}
}

View File

@ -766,6 +766,8 @@ public class ContainerLaunch implements Callable<Integer> {
String classPathJar = FileUtil.createJarWithClassPath(
newClassPath.toString(), pwd, mergedEnv);
// In a secure cluster the classpath jar must be localized to grant access
this.exec.localizeClasspathJar(new Path(classPathJar), container.getUser());
environment.put(Environment.CLASSPATH.name(), classPathJar);
}
}

View File

@ -312,6 +312,31 @@ public class ContainerLocalizer {
status.addAllResources(currentResources);
return status;
}
/**
* Adds the ContainerLocalizer arguments for a @{link ShellCommandExecutor},
* as expected by ContainerLocalizer.main
* @param command the current ShellCommandExecutor command line
* @param user localization user
* @param appId localized app id
* @param locId localizer id
* @param nmAddr nodemanager address
* @param localDirs list of local dirs
*/
public static void buildMainArgs(List<String> command,
String user, String appId, String locId,
InetSocketAddress nmAddr, List<String> localDirs) {
command.add(ContainerLocalizer.class.getName());
command.add(user);
command.add(appId);
command.add(locId);
command.add(nmAddr.getHostName());
command.add(Integer.toString(nmAddr.getPort()));
for(String dir : localDirs) {
command.add(dir);
}
}
public static void main(String[] argv) throws Throwable {
Thread.setDefaultUncaughtExceptionHandler(new YarnUncaughtExceptionHandler());

View File

@ -0,0 +1,72 @@
/**
* 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;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.util.Shell;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.junit.Test;
import static org.junit.Assert.*;
public class TestContainerExecutor {
private ContainerExecutor containerExecutor = new DefaultContainerExecutor();
@Test (timeout = 5000)
public void testRunCommandNoPriority() throws Exception {
Configuration conf = new Configuration();
String[] command = containerExecutor.getRunCommand("echo", "group1", "user", null, conf);
assertTrue("first command should be the run command for the platform " + command[0],
command[0].equals(Shell.WINUTILS) || command[0].equals("bash"));
}
@Test (timeout = 5000)
public void testRunCommandwithPriority() throws Exception {
Configuration conf = new Configuration();
conf.setInt(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY, 2);
String[] command = containerExecutor.getRunCommand("echo", "group1", "user", null, conf);
if (Shell.WINDOWS) {
// windows doesn't currently support
assertEquals("first command should be the run command for the platform",
Shell.WINUTILS, command[0]);
} else {
assertEquals("first command should be nice", "nice", command[0]);
assertEquals("second command should be -n", "-n", command[1]);
assertEquals("third command should be the priority", Integer.toString(2),
command[2]);
}
// test with negative number
conf.setInt(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY, -5);
command = containerExecutor.getRunCommand("echo", "group1", "user", null, conf);
if (Shell.WINDOWS) {
// windows doesn't currently support
assertEquals("first command should be the run command for the platform",
Shell.WINUTILS, command[0]);
} else {
assertEquals("first command should be nice", "nice", command[0]);
assertEquals("second command should be -n", "-n", command[1]);
assertEquals("third command should be the priority", Integer.toString(-5),
command[2]);
}
}
}

View File

@ -199,7 +199,7 @@ public class TestDefaultContainerExecutor {
Assert.assertEquals(appDirPerm, stats.getPermission());
}
executor.createAppLogDirs(appId, logDirs);
executor.createAppLogDirs(appId, logDirs, user);
for (String dir : logDirs) {
FileStatus stats = lfs.getFileStatus(new Path(dir, appId));
@ -277,7 +277,7 @@ public class TestDefaultContainerExecutor {
mockExec.createUserLocalDirs(localDirs, appSubmitter);
mockExec.createUserCacheDirs(localDirs, appSubmitter);
mockExec.createAppDirs(localDirs, appSubmitter, appId);
mockExec.createAppLogDirs(appId, logDirs);
mockExec.createAppLogDirs(appId, logDirs, appSubmitter);
Path scriptPath = new Path("file:///bin/echo");
Path tokensPath = new Path("file:///dev/null");

View File

@ -0,0 +1,118 @@
~~ Licensed 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. See accompanying LICENSE file.
---
YARN Secure Containers
---
---
${maven.build.timestamp}
YARN Secure Containers
%{toc|section=1|fromDepth=0|toDepth=3}
* {Overview}
YARN containers in a secure cluster use the operating system facilities to offer
execution isolation for containers. Secure containers execute under the credentials
of the job user. The operating system enforces access restriction for the container.
The container must run as the use that submitted the application.
Secure Containers work only in the context of secured YARN clusters.
** Container isolation requirements
The container executor must access the local files and directories needed by the
container such as jars, configuration files, log files, shared objects etc. Although
it is launched by the NodeManager, the container should not have access to the
NodeManager private files and configuration. Container running applications
submitted by different users should be isolated and unable to access each other
files and directories. Similar requirements apply to other system non-file securable
objects like named pipes, critical sections, LPC queues, shared memory etc.
** Linux Secure Container Executor
On Linux environment the secure container executor is the <<<LinuxContainerExecutor>>>.
It uses an external program called the <<container-executor>>> to launch the container.
This program has the <<<setuid>>> access right flag set which allows it to launch
the container with the permissions of the YARN application user.
*** Configuration
The configured directories for <<<yarn.nodemanager.local-dirs>>> and
<<<yarn.nodemanager.log-dirs>>> must be owned by the configured NodeManager user
(<<<yarn>>>) and group (<<<hadoop>>>). The permission set on these directories must
be <<<drwxr-xr-x>>>.
The <<<container-executor>>> program must be owned by <<<root>>> and have the
permission set <<<---sr-s--->>>.
To configure the <<<NodeManager>>> to use the <<<LinuxContainerExecutor>>> set the following
in the <<conf/yarn-site.xml>>:
+---+
<property>
<name>yarn.nodemanager.container-executor.class</name>
<value>org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor</value>
</property>
<property>
<name>yarn.nodemanager.linux-container-executor.group</name>
<value>hadoop</value>
</property>
+---+
Additionally the LCE requires the <<<container-executor.cfg>>> file, which is read by the
<<<container-executor>>> program.
+---+
yarn.nodemanager.linux-container-executor.group=#configured value of yarn.nodemanager.linux-container-executor.group
banned.users=#comma separated list of users who can not run applications
allowed.system.users=#comma separated list of allowed system users
min.user.id=1000#Prevent other super-users
+---+
** Windows Secure Container Executor
The Windows environment secure container executor is the <<<WindowsSecureContainerExecutor>>>.
It uses the Windows S4U infrastructure to launch the container as the
YARN application user.
*** Configuration
To configure the <<<NodeManager>>> to use the <<<WindowsSecureContainerExecutor>>>
set the following in the <<conf/yarn-site.xml>>:
+---+
<property>
<name>yarn.nodemanager.container-executor.class</name>
<value>org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor</value>
</property>
<property>
<name>yarn.nodemanager.windows-secure-container-executor.group</name>
<value>hadoop</value>
</property>
+---+
The NodeManager must run as a member of the local <<<Administrators>>> group or as
<<<LocalSystem>>>. It is not enough for the NodeManager to simply impersonate such an user.
*** Useful Links
* {{{http://msdn.microsoft.com/en-us/magazine/cc188757.aspx}Exploring S4U Kerberos Extensions in Windows Server 2003}}
* {{{https://issues.apache.org/jira/browse/YARN-1063}Winutils needs ability to create task as domain user}}
* {{{https://issues.apache.org/jira/browse/YARN-1972}Implement secure Windows Container Executor}}

View File

@ -52,6 +52,8 @@ MapReduce NextGen aka YARN aka MRv2
* {{{./WebApplicationProxy.html}Web Application Proxy}}
* {{{./TimelineServer.html}YARN Timeline Server}}
* {{{./SecureContainer.html}YARN Secure Containers}}
* {{{../../hadoop-project-dist/hadoop-common/CLIMiniCluster.html}CLI MiniCluster}}