This is an automated email from the ASF dual-hosted git repository. gilbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 9c44b31e73783a220e43098a1b117e83e6074fdc Author: Qian Zhang <[email protected]> AuthorDate: Wed Feb 27 22:22:34 2019 -0800 Added a test `ROOT_UNPRIVILEGED_USER_ParentTypeDifferentUser`. Review: https://reviews.apache.org/r/67997/ --- .../volume_sandbox_path_isolator_tests.cpp | 128 +++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/src/tests/containerizer/volume_sandbox_path_isolator_tests.cpp b/src/tests/containerizer/volume_sandbox_path_isolator_tests.cpp index cbe6778..9238d58 100644 --- a/src/tests/containerizer/volume_sandbox_path_isolator_tests.cpp +++ b/src/tests/containerizer/volume_sandbox_path_isolator_tests.cpp @@ -28,6 +28,8 @@ #include "slave/containerizer/mesos/containerizer.hpp" +#include "slave/volume_gid_manager/volume_gid_manager.hpp" + #ifdef __linux__ #include "tests/containerizer/docker_archive.hpp" #endif @@ -41,6 +43,7 @@ using process::Owned; using mesos::internal::slave::Containerizer; using mesos::internal::slave::Fetcher; using mesos::internal::slave::MesosContainerizer; +using mesos::internal::slave::VolumeGidManager; using mesos::internal::slave::state::SlaveState; @@ -429,6 +432,131 @@ TEST_F(VolumeSandboxPathIsolatorTest, EXPECT_WTERMSIG_EQ(SIGKILL, termination.get()->status()); } + +#ifdef __linux__ +// This test verifies that a nested container launched with a +// non-root user has the permission to write to a PARENT type +// SANDBOX_PATH volume while its parent container (i.e., the +// executor container) is launched with a different user (root). +TEST_F(VolumeSandboxPathIsolatorTest, + ROOT_UNPRIVILEGED_USER_ParentTypeDifferentUser) +{ + slave::Flags flags = CreateSlaveFlags(); + flags.isolation = "filesystem/linux,volume/sandbox_path"; + flags.volume_gid_range = "[10000-20000]"; + + Fetcher fetcher(flags); + + Try<VolumeGidManager*> volumeGidManager = VolumeGidManager::create(flags); + ASSERT_SOME(volumeGidManager); + + Try<MesosContainerizer*> create = MesosContainerizer::create( + flags, + true, + &fetcher, + nullptr, + nullptr, + None(), + volumeGidManager.get()); + + ASSERT_SOME(create); + + Owned<MesosContainerizer> containerizer(create.get()); + + SlaveState state; + state.id = SlaveID(); + + AWAIT_READY(containerizer->recover(state)); + + ContainerID containerId; + containerId.set_value(id::UUID::random().toString()); + + ExecutorInfo executor = createExecutorInfo("executor", "sleep 99", "cpus:1"); + + Try<string> directory = environment->mkdtemp(); + ASSERT_SOME(directory); + + // By default this directory is created with the mode 0700, + // here we change it to 0711 to make sure the non-root user + // used to launch the nested container can enter it. + ASSERT_SOME(os::chmod(directory.get(), 0711)); + + // Launch the executor. Since this is a ROOT test, the agent will + // run as root, and by default executor will run as the same user + // as the agent, so it will run as root as well. + Future<Containerizer::LaunchResult> launch = containerizer->launch( + containerId, + createContainerConfig(None(), executor, directory.get()), + map<string, string>(), + None()); + + AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); + + ContainerID nestedContainerId; + nestedContainerId.mutable_parent()->CopyFrom(containerId); + nestedContainerId.set_value(id::UUID::random().toString()); + + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::MESOS); + + Volume* volume = containerInfo.add_volumes(); + volume->set_mode(Volume::RW); + volume->set_container_path("parent"); + + Volume::Source* source = volume->mutable_source(); + source->set_type(Volume::Source::SANDBOX_PATH); + + Volume::Source::SandboxPath* sandboxPath = source->mutable_sandbox_path(); + sandboxPath->set_type(Volume::Source::SandboxPath::PARENT); + sandboxPath->set_path("shared"); + + Option<string> user = os::getenv("SUDO_USER"); + ASSERT_SOME(user); + + // Launch the nested container with a non-root user. + launch = containerizer->launch( + nestedContainerId, + createContainerConfig( + createCommandInfo("echo 'hello' > parent/file"), + containerInfo, + None(), + user.get()), + map<string, string>(), + None()); + + AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); + + Future<Option<ContainerTermination>> wait = + containerizer->wait(nestedContainerId); + + AWAIT_READY(wait); + ASSERT_SOME(wait.get()); + ASSERT_TRUE(wait.get()->has_status()); + EXPECT_WEXITSTATUS_EQ(0, wait.get()->status()); + + string volumePath = path::join(directory.get(), "shared"); + + // The owner group of the volume should be changed to the gid allocated + // to it, i.e., the first gid in the agent flag `--volume_gid_range`. + struct stat s; + EXPECT_EQ(0, ::stat(volumePath.c_str(), &s)); + EXPECT_EQ(10000u, s.st_gid); + + Future<Option<ContainerTermination>> termination = + containerizer->destroy(containerId); + + AWAIT_READY(termination); + ASSERT_SOME(termination.get()); + ASSERT_TRUE(termination.get()->has_status()); + EXPECT_WTERMSIG_EQ(SIGKILL, termination.get()->status()); + + // The owner group of the volume should be changed back to + // the original one, i.e., root. + EXPECT_EQ(0, ::stat(volumePath.c_str(), &s)); + EXPECT_EQ(0u, s.st_gid); +} +#endif // __linux__ + } // namespace tests { } // namespace internal { } // namespace mesos {
