From 4de1616b820d6bbea03d17e93ea822cbc74157df Mon Sep 17 00:00:00 2001 From: Alejandro Abdelnur Date: Fri, 4 Oct 2013 22:00:01 +0000 Subject: [PATCH] YARN-1253. Changes to LinuxContainerExecutor to run containers as a single dedicated user in non-secure mode. (rvs via tucu) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1529326 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/site/apt/ClusterSetup.apt.vm | 6 +- hadoop-yarn-project/CHANGES.txt | 3 + .../hadoop/yarn/conf/YarnConfiguration.java | 22 ++++- .../src/main/resources/yarn-default.xml | 15 ++++ .../nodemanager/LinuxContainerExecutor.java | 38 +++++++- .../impl/container-executor.c | 4 +- .../native/container-executor/impl/main.c | 33 ++++--- .../test/test-container-executor.c | 67 ++++++++------ .../TestLinuxContainerExecutor.java | 90 +++++++++++++++++++ .../TestLinuxContainerExecutorWithMocks.java | 37 ++++---- 10 files changed, 256 insertions(+), 59 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/site/apt/ClusterSetup.apt.vm b/hadoop-common-project/hadoop-common/src/site/apt/ClusterSetup.apt.vm index 1e2a2c0b994..01044fdccf6 100644 --- a/hadoop-common-project/hadoop-common/src/site/apt/ClusterSetup.apt.vm +++ b/hadoop-common-project/hadoop-common/src/site/apt/ClusterSetup.apt.vm @@ -854,8 +854,10 @@ KVNO Timestamp Principal | | The container process has the same Unix user as the NodeManager. | *--------------------------------------+--------------------------------------+ | <<>> | | -| | Supported only on GNU/Linux, this executor runs the containers as the | -| | user who submitted the application. It requires all user accounts to be | +| | Supported only on GNU/Linux, this executor runs the containers as either the | +| | YARN user who submitted the application (when full security is enabled) or | +| | as a dedicated user (defaults to nobody) when full security is not enabled. | +| | When full security is enabled, this executor requires all user accounts to be | | | created on the cluster nodes where the containers are launched. It uses | | | a executable that is included in the Hadoop distribution. | | | The NodeManager uses this executable to launch and kill containers. | diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt index 66c62517684..2f7156b8749 100644 --- a/hadoop-yarn-project/CHANGES.txt +++ b/hadoop-yarn-project/CHANGES.txt @@ -14,6 +14,9 @@ Release 2.3.0 - UNRELEASED YARN-1010. FairScheduler: decouple container scheduling from nodemanager heartbeats. (Wei Yan via Sandy Ryza) + YARN-1253. Changes to LinuxContainerExecutor to run containers as a single + dedicated user in non-secure mode. (rvs via tucu) + IMPROVEMENTS YARN-905. Add state filters to nodes CLI (Wei Yan via Sandy Ryza) diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java index e1327dee5a6..31f344293d5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java @@ -635,7 +635,27 @@ public class YarnConfiguration extends Configuration { */ public static final String NM_LINUX_CONTAINER_GROUP = NM_PREFIX + "linux-container-executor.group"; - + + /** + * The UNIX user that containers will run as when Linux-container-executor + * is used in nonsecure mode (a use case for this is using cgroups). + */ + public static final String NM_NONSECURE_MODE_LOCAL_USER_KEY = NM_PREFIX + + "linux-container-executor.nonsecure-mode.local-user"; + + public static final String DEFAULT_NM_NONSECURE_MODE_LOCAL_USER = "nobody"; + + /** + * The allowed pattern for UNIX user names enforced by + * Linux-container-executor when used in nonsecure mode (use case for this + * is using cgroups). The default value is taken from /usr/sbin/adduser + */ + public static final String NM_NONSECURE_MODE_USER_PATTERN_KEY = NM_PREFIX + + "linux-container-executor.nonsecure-mode.user-pattern"; + + public static final String DEFAULT_NM_NONSECURE_MODE_USER_PATTERN = + "^[_.A-Za-z0-9][-@_.A-Za-z0-9]{0,255}?[$]?$"; + /** The type of resource enforcement to use with the * linux container executor. */ diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml index 6dfeb6662e0..0127fcc579f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml @@ -727,6 +727,21 @@ yarn.nodemanager.linux-container-executor.cgroups.mount-path + + The UNIX user that containers will run as when Linux-container-executor + is used in nonsecure mode (a use case for this is using cgroups). + yarn.nodemanager.linux-container-executor.nonsecure-mode.local-user + nobody + + + + The allowed pattern for UNIX user names enforced by + Linux-container-executor when used in nonsecure mode (use case for this + is using cgroups). The default value is taken from /usr/sbin/adduser + yarn.nodemanager.linux-container-executor.nonsecure-mode.user-pattern + ^[_.A-Za-z0-9][-@_.A-Za-z0-9]{0,255}?[$]?$ + + T-file compression types used to compress aggregated logs. yarn.nodemanager.log-aggregation.compression-type diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java index 9381268b776..3e097815d10 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java @@ -24,11 +24,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.ReflectionUtils; import org.apache.hadoop.util.Shell.ExitCodeException; import org.apache.hadoop.util.Shell.ShellCommandExecutor; @@ -48,6 +50,8 @@ public class LinuxContainerExecutor extends ContainerExecutor { private static final Log LOG = LogFactory .getLog(LinuxContainerExecutor.class); + private String nonsecureLocalUser; + private Pattern nonsecureLocalUserPattern; private String containerExecutorExe; private LCEResourcesHandler resourcesHandler; private boolean containerSchedPriorityIsSet = false; @@ -69,6 +73,24 @@ public void setConf(Configuration conf) { .getInt(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY, YarnConfiguration.DEFAULT_NM_CONTAINER_EXECUTOR_SCHED_PRIORITY); } + nonsecureLocalUser = conf.get( + YarnConfiguration.NM_NONSECURE_MODE_LOCAL_USER_KEY, + YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER); + nonsecureLocalUserPattern = Pattern.compile( + conf.get(YarnConfiguration.NM_NONSECURE_MODE_USER_PATTERN_KEY, + YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_USER_PATTERN)); + } + + void verifyUsernamePattern(String user) { + if (!UserGroupInformation.isSecurityEnabled() && + !nonsecureLocalUserPattern.matcher(user).matches()) { + throw new IllegalArgumentException("Invalid user name '" + user + "'," + + " it must match '" + nonsecureLocalUserPattern.pattern() + "'"); + } + } + + String getRunAsUser(String user) { + return UserGroupInformation.isSecurityEnabled() ? user : nonsecureLocalUser; } @@ -163,9 +185,12 @@ public void startLocalizer(Path nmPrivateContainerTokensPath, List localDirs, List logDirs) throws IOException, InterruptedException { + verifyUsernamePattern(user); + String runAsUser = getRunAsUser(user); List command = new ArrayList(); addSchedPriorityCommand(command); command.addAll(Arrays.asList(containerExecutorExe, + runAsUser, user, Integer.toString(Commands.INITIALIZE_CONTAINER.getValue()), appId, @@ -219,6 +244,9 @@ public int launchContainer(Container container, String user, String appId, Path containerWorkDir, List localDirs, List logDirs) throws IOException { + verifyUsernamePattern(user); + String runAsUser = getRunAsUser(user); + ContainerId containerId = container.getContainerId(); String containerIdStr = ConverterUtils.toString(containerId); @@ -235,7 +263,7 @@ public int launchContainer(Container container, List command = new ArrayList(); addSchedPriorityCommand(command); command.addAll(Arrays.asList( - containerExecutorExe, user, Integer + containerExecutorExe, runAsUser, user, Integer .toString(Commands.LAUNCH_CONTAINER.getValue()), appId, containerIdStr, containerWorkDir.toString(), nmPrivateCotainerScriptPath.toUri().getPath().toString(), @@ -294,8 +322,12 @@ public int launchContainer(Container container, public boolean signalContainer(String user, String pid, Signal signal) throws IOException { + verifyUsernamePattern(user); + String runAsUser = getRunAsUser(user); + String[] command = new String[] { containerExecutorExe, + runAsUser, user, Integer.toString(Commands.SIGNAL_CONTAINER.getValue()), pid, @@ -323,8 +355,12 @@ public boolean signalContainer(String user, String pid, Signal signal) @Override public void deleteAsUser(String user, Path dir, Path... baseDirs) { + verifyUsernamePattern(user); + String runAsUser = getRunAsUser(user); + List command = new ArrayList( Arrays.asList(containerExecutorExe, + runAsUser, user, Integer.toString(Commands.DELETE_AS_USER.getValue()), dir == null ? "" : dir.toUri().getPath())); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c index 307e0fafda7..d5d894d42da 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c @@ -407,7 +407,7 @@ static int create_container_directories(const char* user, const char *app_id, const char *container_id, char* const* local_dir, char* const* log_dir, const char *work_dir) { // create dirs as 0750 const mode_t perms = S_IRWXU | S_IRGRP | S_IXGRP; - if (app_id == NULL || container_id == NULL || user == NULL) { + if (app_id == NULL || container_id == NULL || user == NULL || user_detail == NULL || user_detail->pw_name == NULL) { fprintf(LOGFILE, "Either app_id, container_id or the user passed is null.\n"); return -1; @@ -758,7 +758,7 @@ int initialize_app(const char *user, const char *app_id, const char* nmPrivate_credentials_file, char* const* local_dirs, char* const* log_roots, char* const* args) { - if (app_id == NULL || user == NULL) { + if (app_id == NULL || user == NULL || user_detail == NULL || user_detail->pw_name == NULL) { fprintf(LOGFILE, "Either app_id is null or the user passed is null.\n"); return INVALID_ARGUMENT_NUMBER; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c index f0245d81dc1..9b5e784d520 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c @@ -49,7 +49,7 @@ void display_usage(FILE *stream) { "Usage: container-executor --mount-cgroups "\ "hierarchy controller=path...\n"); fprintf(stream, - "Usage: container-executor user command command-args\n"); + "Usage: container-executor user yarn-user command command-args\n"); fprintf(stream, "Commands:\n"); fprintf(stream, " initialize container: %2d appid tokens " \ "nm-local-dirs nm-log-dirs cmd app...\n", INITIALIZE_CONTAINER); @@ -178,18 +178,29 @@ int main(int argc, char **argv) { if (ret != 0) { return ret; } + + // this string is used for building pathnames, the + // process management is done based on the 'user_detail' + // global, which was set by 'set_user()' above + optind = optind + 1; + char *yarn_user_name = argv[optind]; + if (yarn_user_name == NULL) { + fprintf(ERRORFILE, "Invalid yarn user name.\n"); + return INVALID_USER_NAME; + } optind = optind + 1; command = atoi(argv[optind++]); fprintf(LOGFILE, "main : command provided %d\n",command); fprintf(LOGFILE, "main : user is %s\n", user_detail->pw_name); + fprintf(LOGFILE, "main : requested yarn user is %s\n", yarn_user_name); fflush(LOGFILE); switch (command) { case INITIALIZE_CONTAINER: - if (argc < 8) { - fprintf(ERRORFILE, "Too few arguments (%d vs 8) for initialize container\n", + if (argc < 9) { + fprintf(ERRORFILE, "Too few arguments (%d vs 9) for initialize container\n", argc); fflush(ERRORFILE); return INVALID_ARGUMENT_NUMBER; @@ -198,13 +209,13 @@ int main(int argc, char **argv) { cred_file = argv[optind++]; local_dirs = argv[optind++];// good local dirs as a comma separated list log_dirs = argv[optind++];// good log dirs as a comma separated list - exit_code = initialize_app(user_detail->pw_name, app_id, cred_file, + exit_code = initialize_app(yarn_user_name, app_id, cred_file, extract_values(local_dirs), extract_values(log_dirs), argv + optind); break; case LAUNCH_CONTAINER: - if (argc != 12) { - fprintf(ERRORFILE, "Wrong number of arguments (%d vs 12) for launch container\n", + if (argc != 13) { + fprintf(ERRORFILE, "Wrong number of arguments (%d vs 13) for launch container\n", argc); fflush(ERRORFILE); return INVALID_ARGUMENT_NUMBER; @@ -230,7 +241,7 @@ int main(int argc, char **argv) { return INVALID_ARGUMENT_NUMBER; } char** resources_values = extract_values(resources_value); - exit_code = launch_container_as_user(user_detail->pw_name, app_id, + exit_code = launch_container_as_user(yarn_user_name, app_id, container_id, current_dir, script_file, cred_file, pid_file, extract_values(local_dirs), extract_values(log_dirs), resources_key, @@ -239,8 +250,8 @@ int main(int argc, char **argv) { free(resources_value); break; case SIGNAL_CONTAINER: - if (argc != 5) { - fprintf(ERRORFILE, "Wrong number of arguments (%d vs 5) for " \ + if (argc != 6) { + fprintf(ERRORFILE, "Wrong number of arguments (%d vs 6) for " \ "signal container\n", argc); fflush(ERRORFILE); return INVALID_ARGUMENT_NUMBER; @@ -260,12 +271,12 @@ int main(int argc, char **argv) { fflush(ERRORFILE); return INVALID_ARGUMENT_NUMBER; } - exit_code = signal_container_as_user(user_detail->pw_name, container_pid, signal); + exit_code = signal_container_as_user(yarn_user_name, container_pid, signal); } break; case DELETE_AS_USER: dir_to_be_deleted = argv[optind++]; - exit_code= delete_as_user(user_detail->pw_name, dir_to_be_deleted, + exit_code= delete_as_user(yarn_user_name, dir_to_be_deleted, argv + optind); break; default: diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c index e995bf24132..e9a47b1cbe1 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c @@ -36,6 +36,7 @@ #define ARRAY_SIZE 1000 static char* username = NULL; +static char* yarn_username = NULL; static char** local_dirs = NULL; static char** log_dirs = NULL; @@ -252,15 +253,15 @@ void test_check_configuration_permissions() { } void test_delete_container() { - if (initialize_user(username, local_dirs)) { - printf("FAIL: failed to initialize user %s\n", username); + if (initialize_user(yarn_username, local_dirs)) { + printf("FAIL: failed to initialize user %s\n", yarn_username); exit(1); } - char* app_dir = get_app_directory(TEST_ROOT "/local-2", username, "app_1"); - char* dont_touch = get_app_directory(TEST_ROOT "/local-2", username, + char* app_dir = get_app_directory(TEST_ROOT "/local-2", yarn_username, "app_1"); + char* dont_touch = get_app_directory(TEST_ROOT "/local-2", yarn_username, DONT_TOUCH_FILE); char* container_dir = get_container_work_directory(TEST_ROOT "/local-2", - username, "app_1", "container_1"); + yarn_username, "app_1", "container_1"); char buffer[100000]; sprintf(buffer, "mkdir -p %s/who/let/the/dogs/out/who/who", container_dir); run(buffer); @@ -287,7 +288,7 @@ void test_delete_container() { // delete container directory char * dirs[] = {app_dir, 0}; - int ret = delete_as_user(username, "container_1" , dirs); + int ret = delete_as_user(yarn_username, "container_1" , dirs); if (ret != 0) { printf("FAIL: return code from delete_as_user is %d\n", ret); exit(1); @@ -318,11 +319,11 @@ void test_delete_container() { } void test_delete_app() { - char* app_dir = get_app_directory(TEST_ROOT "/local-2", username, "app_2"); - char* dont_touch = get_app_directory(TEST_ROOT "/local-2", username, + char* app_dir = get_app_directory(TEST_ROOT "/local-2", yarn_username, "app_2"); + char* dont_touch = get_app_directory(TEST_ROOT "/local-2", yarn_username, DONT_TOUCH_FILE); char* container_dir = get_container_work_directory(TEST_ROOT "/local-2", - username, "app_2", "container_1"); + yarn_username, "app_2", "container_1"); char buffer[100000]; sprintf(buffer, "mkdir -p %s/who/let/the/dogs/out/who/who", container_dir); run(buffer); @@ -348,7 +349,7 @@ void test_delete_app() { run(buffer); // delete container directory - int ret = delete_as_user(username, app_dir, NULL); + int ret = delete_as_user(yarn_username, app_dir, NULL); if (ret != 0) { printf("FAIL: return code from delete_as_user is %d\n", ret); exit(1); @@ -377,17 +378,17 @@ void test_delete_app() { void test_delete_user() { printf("\nTesting delete_user\n"); - char* app_dir = get_app_directory(TEST_ROOT "/local-1", username, "app_3"); + char* app_dir = get_app_directory(TEST_ROOT "/local-1", yarn_username, "app_3"); if (mkdirs(app_dir, 0700) != 0) { exit(1); } char buffer[100000]; - sprintf(buffer, "%s/local-1/usercache/%s", TEST_ROOT, username); + sprintf(buffer, "%s/local-1/usercache/%s", TEST_ROOT, yarn_username); if (access(buffer, R_OK) != 0) { printf("FAIL: directory missing before test\n"); exit(1); } - if (delete_as_user(username, buffer, NULL) != 0) { + if (delete_as_user(yarn_username, buffer, NULL) != 0) { exit(1); } if (access(buffer, R_OK) == 0) { @@ -446,7 +447,7 @@ void test_signal_container() { exit(0); } else { printf("Child container launched as %d\n", child); - if (signal_container_as_user(username, child, SIGQUIT) != 0) { + if (signal_container_as_user(yarn_username, child, SIGQUIT) != 0) { exit(1); } int status = 0; @@ -486,7 +487,7 @@ void test_signal_container_group() { // there's a race condition for child calling change_user and us // calling signal_container_as_user, hence sleeping sleep(3); - if (signal_container_as_user(username, child, SIGKILL) != 0) { + if (signal_container_as_user(yarn_username, child, SIGKILL) != 0) { exit(1); } int status = 0; @@ -550,7 +551,7 @@ void test_init_app() { exit(1); } else if (child == 0) { char *final_pgm[] = {"touch", "my-touch-file", 0}; - if (initialize_app(username, "app_4", TEST_ROOT "/creds.txt", + if (initialize_app(yarn_username, "app_4", TEST_ROOT "/creds.txt", local_dirs, log_dirs, final_pgm) != 0) { printf("FAIL: failed in child\n"); exit(42); @@ -568,7 +569,7 @@ void test_init_app() { printf("FAIL: failed to create app log directory\n"); exit(1); } - char* app_dir = get_app_directory(TEST_ROOT "/local-1", username, "app_4"); + char* app_dir = get_app_directory(TEST_ROOT "/local-1", yarn_username, "app_4"); if (access(app_dir, R_OK) != 0) { printf("FAIL: failed to create app directory %s\n", app_dir); exit(1); @@ -640,7 +641,7 @@ void test_run_container() { fflush(stdout); fflush(stderr); char* container_dir = get_container_work_directory(TEST_ROOT "/local-1", - username, "app_4", "container_1"); + yarn_username, "app_4", "container_1"); const char * pid_file = TEST_ROOT "/pid.txt"; pid_t child = fork(); @@ -649,7 +650,7 @@ void test_run_container() { strerror(errno)); exit(1); } else if (child == 0) { - if (launch_container_as_user(username, "app_4", "container_1", + 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) { @@ -697,10 +698,22 @@ void test_run_container() { check_pid_file(cgroups_pids[1], child); } +// This test is expected to be executed either by a regular +// user or by root. If executed by a regular user it doesn't +// test all the functions that would depend on changing the +// effective user id. If executed by a super-user everything +// gets tested. Here are different ways of execing the test binary: +// 1. regular user assuming user == yarn user +// $ test-container-executor +// 2. regular user with a given yarn user +// $ test-container-executor yarn_user +// 3. super user with a given user and assuming user == yarn user +// # test-container-executor user +// 4. super user with a given user and a given yarn user +// # test-container-executor user yarn_user int main(int argc, char **argv) { LOGFILE = stdout; ERRORFILE = stderr; - int my_username = 0; // clean up any junk from previous run if (system("chmod -R u=rwx " TEST_ROOT "; rm -fr " TEST_ROOT)) { @@ -721,11 +734,15 @@ int main(int argc, char **argv) { create_nm_roots(local_dirs); - if (getuid() == 0 && argc == 2) { + // See the description above of various ways this test + // can be executed in order to understand the following logic + char* current_username = strdup(getpwuid(getuid())->pw_name); + if (getuid() == 0 && (argc == 2 || argc == 3)) { username = argv[1]; + yarn_username = (argc == 3) ? argv[2] : argv[1]; } else { - username = strdup(getpwuid(getuid())->pw_name); - my_username = 1; + username = current_username; + yarn_username = (argc == 2) ? argv[1] : current_username; } set_nm_uid(geteuid(), getegid()); @@ -783,9 +800,7 @@ int main(int argc, char **argv) { run("rm -fr " TEST_ROOT); printf("\nFinished tests\n"); - if (my_username) { - free(username); - } + free(current_username); free_configurations(); return 0; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java index f09f5a88e69..74452c92b5c 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java @@ -31,14 +31,17 @@ import java.io.PrintWriter; import java.util.HashMap; +import junit.framework.Assert; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsPermission; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; import org.apache.hadoop.yarn.conf.YarnConfiguration; @@ -256,4 +259,91 @@ public void run() { assertFalse(t.isAlive()); } + + @Test + public void testLocalUser() throws Exception { + try { + //nonsecure default + Configuration conf = new YarnConfiguration(); + conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, + "simple"); + UserGroupInformation.setConfiguration(conf); + LinuxContainerExecutor lce = new LinuxContainerExecutor(); + lce.setConf(conf); + Assert.assertEquals(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER, + lce.getRunAsUser("foo")); + + //nonsecure custom setting + conf.set(YarnConfiguration.NM_NONSECURE_MODE_LOCAL_USER_KEY, "bar"); + lce = new LinuxContainerExecutor(); + lce.setConf(conf); + Assert.assertEquals("bar", lce.getRunAsUser("foo")); + + //secure + conf = new YarnConfiguration(); + conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, + "kerberos"); + UserGroupInformation.setConfiguration(conf); + lce = new LinuxContainerExecutor(); + lce.setConf(conf); + Assert.assertEquals("foo", lce.getRunAsUser("foo")); + } finally { + Configuration conf = new YarnConfiguration(); + conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, + "simple"); + UserGroupInformation.setConfiguration(conf); + } + } + + @Test + public void testNonsecureUsernamePattern() throws Exception { + try { + //nonsecure default + Configuration conf = new YarnConfiguration(); + conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, + "simple"); + UserGroupInformation.setConfiguration(conf); + LinuxContainerExecutor lce = new LinuxContainerExecutor(); + lce.setConf(conf); + lce.verifyUsernamePattern("foo"); + try { + lce.verifyUsernamePattern("foo/x"); + Assert.fail(); + } catch (IllegalArgumentException ex) { + //NOP + } catch (Throwable ex) { + Assert.fail(ex.toString()); + } + + //nonsecure custom setting + conf.set(YarnConfiguration.NM_NONSECURE_MODE_USER_PATTERN_KEY, "foo"); + lce = new LinuxContainerExecutor(); + lce.setConf(conf); + lce.verifyUsernamePattern("foo"); + try { + lce.verifyUsernamePattern("bar"); + Assert.fail(); + } catch (IllegalArgumentException ex) { + //NOP + } catch (Throwable ex) { + Assert.fail(ex.toString()); + } + + //secure, pattern matching does not kick in. + conf = new YarnConfiguration(); + conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, + "kerberos"); + UserGroupInformation.setConfiguration(conf); + lce = new LinuxContainerExecutor(); + lce.setConf(conf); + lce.verifyUsernamePattern("foo"); + lce.verifyUsernamePattern("foo/w"); + } finally { + Configuration conf = new YarnConfiguration(); + conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, + "simple"); + UserGroupInformation.setConfiguration(conf); + } + } + } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java index fe81ef13493..d193f3e74a8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java @@ -128,7 +128,8 @@ public void testContainerLaunch() throws IOException { appSubmitter, appId, workDir, dirsHandler.getLocalDirs(), dirsHandler.getLogDirs()); assertEquals(0, ret); - assertEquals(Arrays.asList(appSubmitter, cmd, appId, containerId, + assertEquals(Arrays.asList(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER, + appSubmitter, cmd, appId, containerId, workDir.toString(), "/bin/echo", "/dev/null", pidFile.toString(), StringUtils.join(",", dirsHandler.getLocalDirs()), StringUtils.join(",", dirsHandler.getLogDirs()), "cgroups=none"), @@ -179,18 +180,19 @@ public void testStartLocalizer() throws IOException { try { mockExec.startLocalizer(nmPrivateCTokensPath, address, "test", "application_0", "12345", dirsHandler.getLocalDirs(), dirsHandler.getLogDirs()); List result=readMockParams(); - Assert.assertEquals(result.size(), 16); - Assert.assertEquals(result.get(0), "test"); - Assert.assertEquals(result.get(1), "0" ); - Assert.assertEquals(result.get(2),"application_0" ); - Assert.assertEquals(result.get(3), "/bin/nmPrivateCTokensPath"); - Assert.assertEquals(result.get(7), "-classpath" ); - Assert.assertEquals(result.get(10),"org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer" ); - Assert.assertEquals(result.get(11), "test"); - Assert.assertEquals(result.get(12), "application_0"); - Assert.assertEquals(result.get(13),"12345" ); - Assert.assertEquals(result.get(14),"localhost" ); - Assert.assertEquals(result.get(15),"8040" ); + Assert.assertEquals(result.size(), 17); + Assert.assertEquals(result.get(0), YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER); + Assert.assertEquals(result.get(1), "test"); + Assert.assertEquals(result.get(2), "0" ); + Assert.assertEquals(result.get(3),"application_0" ); + Assert.assertEquals(result.get(4), "/bin/nmPrivateCTokensPath"); + Assert.assertEquals(result.get(8), "-classpath" ); + Assert.assertEquals(result.get(11),"org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer" ); + Assert.assertEquals(result.get(12), "test"); + Assert.assertEquals(result.get(13), "application_0"); + Assert.assertEquals(result.get(14),"12345" ); + Assert.assertEquals(result.get(15),"localhost" ); + Assert.assertEquals(result.get(16),"8040" ); } catch (InterruptedException e) { LOG.error("Error:"+e.getMessage(),e); @@ -245,7 +247,8 @@ public void testContainerLaunchError() throws IOException { appSubmitter, appId, workDir, dirsHandler.getLocalDirs(), dirsHandler.getLogDirs()); Assert.assertNotSame(0, ret); - assertEquals(Arrays.asList(appSubmitter, cmd, appId, containerId, + assertEquals(Arrays.asList(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER, + appSubmitter, cmd, appId, containerId, workDir.toString(), "/bin/echo", "/dev/null", pidFile.toString(), StringUtils.join(",", dirsHandler.getLocalDirs()), StringUtils.join(",", dirsHandler.getLogDirs()), @@ -271,7 +274,8 @@ public void testContainerKill() throws IOException { String sigVal = String.valueOf(signal.getValue()); mockExec.signalContainer(appSubmitter, "1000", signal); - assertEquals(Arrays.asList(appSubmitter, cmd, "1000", sigVal), + assertEquals(Arrays.asList(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER, + appSubmitter, cmd, "1000", sigVal), readMockParams()); } @@ -283,7 +287,8 @@ public void testDeleteAsUser() throws IOException { Path dir = new Path("/tmp/testdir"); mockExec.deleteAsUser(appSubmitter, dir); - assertEquals(Arrays.asList(appSubmitter, cmd, "/tmp/testdir"), + assertEquals(Arrays.asList(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER, + appSubmitter, cmd, "/tmp/testdir"), readMockParams()); } }