commit 3117978938243ac93ea04fe11c11f82d701ae43c Author: Eric Yang Date: Fri Feb 16 14:02:52 2018 -0500 YARN-7221. Added sudoers check for starting privileged docker container. (Contributed by Eric Yang) 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/DockerLinuxContainerRuntime.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/DockerLinuxContainerRuntime.java index 601c32c..f6eb8db 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/DockerLinuxContainerRuntime.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/DockerLinuxContainerRuntime.java @@ -761,7 +761,9 @@ public void launchContainer(ContainerRuntimeContext ctx) throw new ContainerExecutionException(message); } } - dockerRunAsUser = uid + ":" + gid; + if (!allowPrivilegedContainerExecution(container)) { + dockerRunAsUser = uid + ":" + gid; + } } //List -> stored as List -> fetched/converted to List diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c index 4a0428d..dd9da60 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c @@ -16,6 +16,9 @@ * limitations under the License. */ +#include +#include +#include #include #include #include @@ -25,6 +28,8 @@ #include "docker-util.h" #include "string-utils.h" #include "util.h" +#include +#include static int read_and_verify_command_file(const char *command_file, const char *docker_command, struct configuration *command_config) { @@ -1168,14 +1173,83 @@ static int add_rw_mounts(const struct configuration *command_config, const stru return add_mounts(command_config, conf, "rw-mounts", 0, out, outlen); } +static int check_privileges(const char *user) { + int ngroups; + gid_t *groups; + struct passwd *pw; + struct group *gr; + int ret = 1; + + int statval = 0; + int MAX_GROUPS = 100; + + groups = (gid_t *) alloc_and_clear_memory(MAX_GROUPS, sizeof(gid_t)); + if (groups == NULL) { + fprintf(ERRORFILE, "Failed to allocate buffer for group lookup for user %s.\n", user); + exit(OUT_OF_MEMORY); + } + + pw = getpwnam(user); + if (pw == NULL) { + fprintf(ERRORFILE, "User %s does not exist in host OS.\n", user); + free(groups); + exit(INITIALIZE_USER_FAILED); + } + + if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) { + fprintf(ERRORFILE, "Fail to lookup groups for user %s.\n", user); + ret = 2; + } + + if (ret != 2) { + for (int j = 0; j < ngroups; j++) { + gr = getgrgid(groups[j]); + if (gr != NULL) { + if (strcmp(gr->gr_name, "root")==0 || strcmp(gr->gr_name, "docker")==0) { + ret = 0; + break; + } + } + } + } + + if (ret != 0) { + if (fork()==0) { + execl("/bin/sudo", "sudo", "-U", user, "-n", "-l", "docker", NULL); + exit(INITIALIZE_USER_FAILED); + } else { + wait(&statval); + if (WIFEXITED(statval)) { + if (WEXITSTATUS(statval)==0) { + ret = 0; + } + } + } + } + free(groups); + if (ret == 0) { + fprintf(ERRORFILE, "check privileges passed for user: %s\n", user); + } else { + fprintf(ERRORFILE, "check privileges failed for user: %s, error code: %d\n", user, ret); + } + return ret; +} + static int set_privileged(const struct configuration *command_config, const struct configuration *conf, char *out, const size_t outlen) { size_t tmp_buffer_size = 1024; + char *user = NULL; char *tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); char *value = get_configuration_value("privileged", DOCKER_COMMAND_FILE_SECTION, command_config); char *privileged_container_enabled = get_configuration_value("docker.privileged-containers.enabled", CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf); int ret = 0; + int allowed = 0; + + user = get_configuration_value("user", DOCKER_COMMAND_FILE_SECTION, command_config); + if (user == NULL) { + return INVALID_DOCKER_USER_NAME; + } if (value != NULL && strcasecmp(value, "true") == 0 ) { if (privileged_container_enabled != NULL) { @@ -1187,9 +1261,16 @@ static int set_privileged(const struct configuration *command_config, const stru ret = PRIVILEGED_CONTAINERS_DISABLED; goto free_and_exit; } - ret = add_to_buffer(out, outlen, "--privileged "); - if (ret != 0) { - ret = BUFFER_TOO_SMALL; + allowed = check_privileges(user); + if (allowed == 0) { + ret = add_to_buffer(out, outlen, "--privileged "); + if (ret != 0) { + ret = BUFFER_TOO_SMALL; + } + } else { + fprintf(ERRORFILE, "Privileged containers are disabled for user: %s\n", user); + ret = PRIVILEGED_CONTAINERS_DISABLED; + goto free_and_exit; } } else { fprintf(ERRORFILE, "Privileged containers are disabled\n"); @@ -1207,6 +1288,7 @@ static int set_privileged(const struct configuration *command_config, const stru free(tmp_buffer); free(value); free(privileged_container_enabled); + free(user); if (ret != 0) { memset(out, 0, outlen); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc index 81823a8..ef2bfca 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc @@ -623,10 +623,10 @@ namespace ContainerExecutor { FAIL(); } ret = set_privileged(&cmd_cfg, &container_cfg, buff, buff_len); - ASSERT_EQ(0, ret); - ASSERT_STREQ(itr->second.c_str(), buff); + ASSERT_EQ(6, ret); + ASSERT_EQ(0, strlen(buff)); } - write_command_file("[docker-command-execution]\n docker-command=run\n privileged=true\n image=nothadoop/image"); + write_command_file("[docker-command-execution]\n docker-command=run\n user=test\n privileged=true\n image=nothadoop/image"); ret = read_config(docker_command_file.c_str(), &cmd_cfg); if (ret != 0) { FAIL(); @@ -646,9 +646,7 @@ namespace ContainerExecutor { } file_cmd_vec.clear(); file_cmd_vec.push_back(std::make_pair( - "[docker-command-execution]\n docker-command=run\n privileged=false", "")); - file_cmd_vec.push_back(std::make_pair( - "[docker-command-execution]\n docker-command=run", "")); + "[docker-command-execution]\n docker-command=run\n user=root\n privileged=false", "")); for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { memset(buff, 0, buff_len); write_command_file(itr->first); @@ -660,7 +658,7 @@ namespace ContainerExecutor { ASSERT_EQ(0, ret); ASSERT_STREQ(itr->second.c_str(), buff); } - write_command_file("[docker-command-execution]\n docker-command=run\n privileged=true"); + write_command_file("[docker-command-execution]\n docker-command=run\n user=root\n privileged=true"); ret = read_config(docker_command_file.c_str(), &cmd_cfg); if (ret != 0) { FAIL(); @@ -1110,7 +1108,7 @@ namespace ContainerExecutor { // Test privileged container file_cmd_vec.push_back(std::make_pair( "[docker-command-execution]\n" - " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=hadoop/docker-image\n user=test\n hostname=host-id\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=hadoop/docker-image\n user=root\n hostname=host-id\n" " ro-mounts=/var/log:/var/log,/var/lib:/lib,/usr/bin/cut:/usr/bin/cut\n rw-mounts=/tmp:/tmp\n" " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n privileged=true\n" " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" @@ -1120,10 +1118,9 @@ namespace ContainerExecutor { "--cap-add='CHOWN' --cap-add='SETUID' --hostname='host-id' --device='/dev/test:/dev/test' 'hadoop/docker-image' " "'bash' 'test_script.sh' 'arg1' 'arg2' ")); - file_cmd_vec.push_back(std::make_pair( "[docker-command-execution]\n" - " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=hadoop/docker-image\n user=test\n hostname=host-id\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=hadoop/docker-image\n user=root\n hostname=host-id\n" " ro-mounts=/var/log:/var/log,/var/lib:/lib,/usr/bin/cut:/usr/bin/cut\n rw-mounts=/tmp:/tmp\n" " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n privileged=true\n" " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n group-add=1000,1001\n" @@ -1201,7 +1198,7 @@ namespace ContainerExecutor { " network=bridge\n devices=/dev/dev1:/dev/dev1\n privileged=true\n" " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" " launch-command=bash,test_script.sh,arg1,arg2", - static_cast(INVALID_DOCKER_DEVICE))); + static_cast(PRIVILEGED_CONTAINERS_DISABLED))); // invalid network bad_file_cmd_vec.push_back(std::make_pair( 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/TestDockerContainerRuntime.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/TestDockerContainerRuntime.java index fe4e238..2ee1502 100644 --- 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/TestDockerContainerRuntime.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/TestDockerContainerRuntime.java @@ -933,7 +933,7 @@ public void testLaunchPrivilegedContainersWithEnabledSettingAndDefaultACL() + "/test_container_log_dir:/test_container_log_dir," + "/test_user_local_dir:/test_user_local_dir", dockerCommands.get(counter++)); - Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++)); + Assert.assertEquals(" user=" + runAsUser, dockerCommands.get(counter++)); Assert.assertEquals(" workdir=/test_container_work_dir", dockerCommands.get(counter++)); }