Remove parent's env vars from child processes

This commit is contained in:
Robert Kanter 2016-05-06 09:33:10 -07:00
parent dd8aba3fad
commit b0bcb4e728
5 changed files with 81 additions and 7 deletions

View File

@ -33,6 +33,7 @@
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.security.alias.AbstractJavaKeyStoreProvider;
/**
* A base class for running a Unix command.
@ -280,6 +281,8 @@ public static String[] getRunScriptCommand(File script) {
/** If or not script timed out*/
private AtomicBoolean timedOut;
/** Indicates if the parent env vars should be inherited or not*/
protected boolean inheritParentEnv = true;
/** Centralized logic to discover and validate the sanity of the Hadoop
* home directory. Returns either NULL or a directory that exists and
@ -467,6 +470,20 @@ private void runCommand() throws IOException {
if (environment != null) {
builder.environment().putAll(this.environment);
}
// Remove all env vars from the Builder to prevent leaking of env vars from
// the parent process.
if (!inheritParentEnv) {
// branch-2: Only do this for HADOOP_CREDSTORE_PASSWORD
// Sometimes daemons are configured to use the CredentialProvider feature
// and given their jceks password via an environment variable. We need to
// make sure to remove it so it doesn't leak to child processes, which
// might be owned by a different user. For example, the NodeManager
// running a User's container.
builder.environment().remove(
AbstractJavaKeyStoreProvider.CREDENTIAL_PASSWORD_NAME);
}
if (dir != null) {
builder.directory(this.dir);
}
@ -690,6 +707,11 @@ public ShellCommandExecutor(String[] execString, File dir,
this(execString, dir, env , 0L);
}
public ShellCommandExecutor(String[] execString, File dir,
Map<String, String> env, long timeout) {
this(execString, dir, env , timeout, true);
}
/**
* Create a new instance of the ShellCommandExecutor to execute a command.
*
@ -702,10 +724,12 @@ public ShellCommandExecutor(String[] execString, File dir,
* environment is not modified.
* @param timeout Specifies the time in milliseconds, after which the
* command will be killed and the status marked as timedout.
* If 0, the command will not be timed out.
* If 0, the command will not be timed out.
* @param inheritParentEnv Indicates if the process should inherit the env
* vars from the parent process or not.
*/
public ShellCommandExecutor(String[] execString, File dir,
Map<String, String> env, long timeout) {
Map<String, String> env, long timeout, boolean inheritParentEnv) {
command = execString.clone();
if (dir != null) {
setWorkingDirectory(dir);
@ -714,6 +738,7 @@ public ShellCommandExecutor(String[] execString, File dir,
setEnvironment(env);
}
timeOutInterval = timeout;
this.inheritParentEnv = inheritParentEnv;
}

View File

@ -18,6 +18,7 @@
package org.apache.hadoop.util;
import junit.framework.TestCase;
import org.apache.hadoop.security.alias.AbstractJavaKeyStoreProvider;
import java.io.BufferedReader;
import java.io.File;
@ -27,8 +28,13 @@
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.fs.FileUtil;
import org.junit.Assume;
import org.junit.Test;
public class TestShell extends TestCase {
@ -111,6 +117,44 @@ public void testShellCommandTimeout() throws Throwable {
shellFile.delete();
assertTrue("Script didnt not timeout" , shexc.isTimedOut());
}
@Test
public void testEnvVarsWithInheritance() throws Exception {
Assume.assumeFalse(Shell.WINDOWS);
testEnvHelper(true);
}
@Test
public void testEnvVarsWithoutInheritance() throws Exception {
Assume.assumeFalse(Shell.WINDOWS);
testEnvHelper(false);
}
private void testEnvHelper(boolean inheritParentEnv) throws Exception {
Map<String, String> customEnv = Collections.singletonMap(
AbstractJavaKeyStoreProvider.CREDENTIAL_PASSWORD_NAME, "foo");
Shell.ShellCommandExecutor command = new Shell.ShellCommandExecutor(
new String[]{"env"}, null, customEnv, 0L,
inheritParentEnv);
command.execute();
String[] varsArr = command.getOutput().split("\n");
Map<String, String> vars = new HashMap<>();
for (String var : varsArr) {
int eqIndex = var.indexOf('=');
vars.put(var.substring(0, eqIndex), var.substring(eqIndex + 1));
}
Map<String, String> expectedEnv = new HashMap<>();
expectedEnv.putAll(System.getenv());
if (inheritParentEnv) {
expectedEnv.putAll(customEnv);
} else {
assertFalse("child process environment should not have contained "
+ AbstractJavaKeyStoreProvider.CREDENTIAL_PASSWORD_NAME,
vars.containsKey(
AbstractJavaKeyStoreProvider.CREDENTIAL_PASSWORD_NAME));
}
assertEquals(expectedEnv, vars);
}
private static int countTimerThreads() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

View File

@ -269,7 +269,9 @@ protected CommandExecutor buildCommandExecutor(String wrapperScriptPath,
return new ShellCommandExecutor(
command,
wordDir,
environment);
environment,
0L,
false);
}
protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder(

View File

@ -238,9 +238,11 @@ public int launchContainer(Container container,
LOG.debug("launchContainer: " + commandStr + " " + Joiner.on(" ").join(command));
}
shExec = new ShellCommandExecutor(
command,
new File(containerWorkDir.toUri().getPath()),
container.getLaunchContext().getEnvironment()); // sanitized env
command,
new File(containerWorkDir.toUri().getPath()),
container.getLaunchContext().getEnvironment(), // sanitized env
0L,
false);
if (isContainerActive(containerId)) {
shExec.execute();
} else {

View File

@ -228,7 +228,8 @@ public void startLocalizer(Path nmPrivateContainerTokensPath,
}
buildMainArgs(command, user, appId, locId, nmAddr, localDirs);
String[] commandArray = command.toArray(new String[command.size()]);
ShellCommandExecutor shExec = new ShellCommandExecutor(commandArray);
ShellCommandExecutor shExec = new ShellCommandExecutor(commandArray,
null, null, 0L, true);
if (LOG.isDebugEnabled()) {
LOG.debug("initApplication: " + Arrays.toString(commandArray));
}