You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by jp...@apache.org on 2019/07/21 08:06:47 UTC
[mesos] 02/03: Added a `linux/nnp` isolator.
This is an automated email from the ASF dual-hosted git repository.
jpeach pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 2f9494dc808b948720d318fdfc35dbf16ecd65ce
Author: Jacob Janco <jj...@gmail.com>
AuthorDate: Sat Jul 20 23:34:26 2019 -0700
Added a `linux/nnp` isolator.
This adds a `linux/nnp` isolator to set the NO_NEW_PRIVILEGES flag
on containerized processes.
Review: https://reviews.apache.org/r/70757/
---
include/mesos/slave/containerizer.proto | 3 +
src/CMakeLists.txt | 1 +
src/Makefile.am | 3 +
src/slave/containerizer/mesos/containerizer.cpp | 2 +
.../containerizer/mesos/isolators/linux/nnp.cpp | 78 ++++++++++++
.../containerizer/mesos/isolators/linux/nnp.hpp | 45 +++++++
src/slave/containerizer/mesos/launch.cpp | 9 ++
src/tests/CMakeLists.txt | 1 +
.../containerizer/linux_nnp_isolator_tests.cpp | 141 +++++++++++++++++++++
9 files changed, 283 insertions(+)
diff --git a/include/mesos/slave/containerizer.proto b/include/mesos/slave/containerizer.proto
index 2d04f3c..a60c963 100644
--- a/include/mesos/slave/containerizer.proto
+++ b/include/mesos/slave/containerizer.proto
@@ -327,6 +327,9 @@ message ContainerLaunchInfo {
// (POSIX only) The supplementary group IDs specific for command task
// with its own rootfs.
repeated uint32 task_supplementary_groups = 20;
+
+ // (Linux only) Set the NO_NEW_PRIVILEGES flag.
+ optional bool no_new_privileges = 23;
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4a5eeb0..c455ed6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -324,6 +324,7 @@ set(LINUX_SRC
slave/containerizer/mesos/isolators/gpu/volume.cpp
slave/containerizer/mesos/isolators/linux/capabilities.cpp
slave/containerizer/mesos/isolators/linux/devices.cpp
+ slave/containerizer/mesos/isolators/linux/nnp.cpp
slave/containerizer/mesos/isolators/namespaces/ipc.cpp
slave/containerizer/mesos/isolators/namespaces/pid.cpp
slave/containerizer/mesos/isolators/network/cni/cni.cpp
diff --git a/src/Makefile.am b/src/Makefile.am
index 7851aba..46c66f1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1435,6 +1435,8 @@ MESOS_LINUX_FILES = \
slave/containerizer/mesos/isolators/linux/capabilities.hpp \
slave/containerizer/mesos/isolators/linux/devices.cpp \
slave/containerizer/mesos/isolators/linux/devices.hpp \
+ slave/containerizer/mesos/isolators/linux/nnp.hpp \
+ slave/containerizer/mesos/isolators/linux/nnp.cpp \
slave/containerizer/mesos/isolators/namespaces/ipc.cpp \
slave/containerizer/mesos/isolators/namespaces/ipc.hpp \
slave/containerizer/mesos/isolators/namespaces/pid.cpp \
@@ -2764,6 +2766,7 @@ mesos_tests_SOURCES += \
tests/containerizer/docker_volume_isolator_tests.cpp \
tests/containerizer/linux_devices_isolator_tests.cpp \
tests/containerizer/linux_filesystem_isolator_tests.cpp \
+ tests/containerizer/linux_nnp_isolator_tests.cpp \
tests/containerizer/fs_tests.cpp \
tests/containerizer/memory_pressure_tests.cpp \
tests/containerizer/nested_mesos_containerizer_tests.cpp \
diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp
index 6f76527..a01edc8 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -108,6 +108,7 @@
#include "slave/containerizer/mesos/isolators/gpu/nvidia.hpp"
#include "slave/containerizer/mesos/isolators/linux/capabilities.hpp"
#include "slave/containerizer/mesos/isolators/linux/devices.hpp"
+#include "slave/containerizer/mesos/isolators/linux/nnp.hpp"
#include "slave/containerizer/mesos/isolators/namespaces/ipc.hpp"
#include "slave/containerizer/mesos/isolators/namespaces/pid.hpp"
#include "slave/containerizer/mesos/isolators/network/cni/cni.hpp"
@@ -438,6 +439,7 @@ Try<MesosContainerizer*> MesosContainerizer::create(
{"linux/devices", &LinuxDevicesIsolatorProcess::create},
{"linux/capabilities", &LinuxCapabilitiesIsolatorProcess::create},
+ {"linux/nnp", &LinuxNNPIsolatorProcess::create},
{"namespaces/ipc", &NamespacesIPCIsolatorProcess::create},
{"namespaces/pid", &NamespacesPidIsolatorProcess::create},
diff --git a/src/slave/containerizer/mesos/isolators/linux/nnp.cpp b/src/slave/containerizer/mesos/isolators/linux/nnp.cpp
new file mode 100644
index 0000000..bd214b1
--- /dev/null
+++ b/src/slave/containerizer/mesos/isolators/linux/nnp.cpp
@@ -0,0 +1,78 @@
+// 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.
+
+#include <stout/try.hpp>
+
+#include "common/kernel_version.hpp"
+
+#include "slave/containerizer/mesos/isolators/linux/nnp.hpp"
+
+using process::Future;
+using process::Owned;
+
+using mesos::slave::ContainerConfig;
+using mesos::slave::ContainerLaunchInfo;
+using mesos::slave::Isolator;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+Try<Isolator*> LinuxNNPIsolatorProcess::create(const Flags& flags)
+{
+ // PR_SET_NO_NEW_PRIVS requires Linux kernel version greater than or
+ // equal to 3.5.
+ Try<Version> version = mesos::kernelVersion();
+
+ if (version.isError()) {
+ return Error("Could not determine kernel version");
+ }
+
+ if (version.get() < Version(3, 5, 0)) {
+ return Error("Linux kernel version greater than or equal to 3.5 required");
+ }
+
+ return new MesosIsolator(
+ Owned<MesosIsolatorProcess>(new LinuxNNPIsolatorProcess()));
+}
+
+
+bool LinuxNNPIsolatorProcess::supportsNesting()
+{
+ return true;
+}
+
+
+bool LinuxNNPIsolatorProcess::supportsStandalone()
+{
+ return true;
+}
+
+
+Future<Option<ContainerLaunchInfo>> LinuxNNPIsolatorProcess::prepare(
+ const ContainerID& containerId,
+ const ContainerConfig& containerConfig)
+{
+ ContainerLaunchInfo launchInfo;
+
+ launchInfo.set_no_new_privileges(true);
+
+ return launchInfo;
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
diff --git a/src/slave/containerizer/mesos/isolators/linux/nnp.hpp b/src/slave/containerizer/mesos/isolators/linux/nnp.hpp
new file mode 100644
index 0000000..5251f61
--- /dev/null
+++ b/src/slave/containerizer/mesos/isolators/linux/nnp.hpp
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef __LINUX_NNP_ISOLATOR_HPP__
+#define __LINUX_NNP_ISOLATOR_HPP__
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/mesos/isolator.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+class LinuxNNPIsolatorProcess : public MesosIsolatorProcess
+{
+public:
+ static Try<mesos::slave::Isolator*> create(const Flags& flags);
+
+ bool supportsNesting() override;
+ bool supportsStandalone() override;
+
+ process::Future<Option<mesos::slave::ContainerLaunchInfo>> prepare(
+ const ContainerID& containerId,
+ const mesos::slave::ContainerConfig& containerConfig) override;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __LINUX_NNP_ISOLATOR_HPP__
diff --git a/src/slave/containerizer/mesos/launch.cpp b/src/slave/containerizer/mesos/launch.cpp
index 0419e8e..1f8dc1a 100644
--- a/src/slave/containerizer/mesos/launch.cpp
+++ b/src/slave/containerizer/mesos/launch.cpp
@@ -18,6 +18,7 @@
#ifdef __linux__
#include <sched.h>
#include <signal.h>
+#include <sys/prctl.h>
#endif // __linux__
#include <string.h>
@@ -1156,6 +1157,14 @@ int MesosContainerizerLaunch::execute()
exitWithStatus(EXIT_FAILURE);
}
}
+
+ if (launchInfo.has_no_new_privileges()) {
+ const int val = launchInfo.no_new_privileges() ? 1 : 0;
+ if (prctl(PR_SET_NO_NEW_PRIVS, val, 0, 0, 0) == -1) {
+ cerr << "Failed to set NO_NEW_PRIVS: " << os::strerror(errno) << endl;
+ exitWithStatus(EXIT_FAILURE);
+ }
+ }
#endif // __linux__
// Prepare the executable and the argument list for the child.
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 57344a1..faa0058 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -232,6 +232,7 @@ if (LINUX)
containerizer/docker_volume_isolator_tests.cpp
containerizer/fs_tests.cpp
containerizer/linux_capabilities_isolator_tests.cpp
+ containerizer/linux_nnp_isolator_tests.cpp
containerizer/linux_devices_isolator_tests.cpp
containerizer/linux_filesystem_isolator_tests.cpp
containerizer/memory_pressure_tests.cpp
diff --git a/src/tests/containerizer/linux_nnp_isolator_tests.cpp b/src/tests/containerizer/linux_nnp_isolator_tests.cpp
new file mode 100644
index 0000000..8489daf
--- /dev/null
+++ b/src/tests/containerizer/linux_nnp_isolator_tests.cpp
@@ -0,0 +1,141 @@
+// 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.
+
+#include <map>
+#include <string>
+
+#include <mesos/mesos.hpp>
+
+#include <process/gtest.hpp>
+#include <process/owned.hpp>
+
+#include <stout/try.hpp>
+
+#include "common/kernel_version.hpp"
+
+#include "slave/containerizer/mesos/containerizer.hpp"
+
+#include "tests/mesos.hpp"
+
+#include "tests/containerizer/docker_archive.hpp"
+
+using process::Future;
+using process::Owned;
+
+using std::map;
+using std::string;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Containerizer;
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::MesosContainerizer;
+
+using mesos::slave::ContainerTermination;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class LinuxNNPIsolatorTest : public MesosTest
+{
+protected:
+ slave::Flags CreateSlaveFlags() override
+ {
+ slave::Flags flags = MesosTest::CreateSlaveFlags();
+ flags.isolation = "filesystem/linux,docker/runtime,linux/nnp";
+ flags.docker_registry = GetRegistryPath();
+ flags.docker_store_dir = path::join(sandbox.get(), "store");
+ flags.image_providers = "docker";
+
+ return flags;
+ }
+
+ string GetRegistryPath() const
+ {
+ return path::join(sandbox.get(), "registry");
+ }
+};
+
+
+// Check that the PR_NO_NEW_PRIVILEGES flag is set.
+TEST_F(LinuxNNPIsolatorTest, CheckNoNewPrivileges)
+{
+ // This tests requires the NoNewPrivs field present in process
+ // status fields which requires Linux kernel version greater than
+ // or equal to 4.10.
+ Try<Version> version = mesos::kernelVersion();
+ ASSERT_SOME(version);
+ if (version.get() < Version(4, 10, 0)) {
+ LOG(INFO) << "Linux kernel version greater than or equal to 4.10 required";
+ return;
+ }
+
+ AWAIT_READY(DockerArchive::create(GetRegistryPath(), "test_image"));
+
+ slave::Flags flags = CreateSlaveFlags();
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, true, &fetcher);
+
+ ASSERT_SOME(create);
+
+ Owned<Containerizer> containerizer(create.get());
+
+ ContainerID containerId;
+ containerId.set_value(id::UUID::random().toString());
+
+ // Test that the child process inherits the PR_NO_NEW_PRIVS flag.
+ // Using parameter expansion to parse the process status file
+ // due to minimal docker image. The child process should inherit
+ // the PR_NO_NEW_PRIVS flag. Parse the process status file and
+ // determine if "NoNewPrivs: 1" is found.
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ R"~(
+ #!/bin/bash
+ x=$(cat /proc/self/status);
+ y=${x##*NoNewPrivs:};
+ read -a a <<< $y;
+ if [ ${a[0]} == "1" ]; then exit 0; else exit 1; fi
+ )~");
+
+ executor.mutable_container()->CopyFrom(createContainerInfo("test_image"));
+
+ string directory = path::join(flags.work_dir, "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
+
+ Future<Containerizer::LaunchResult> launch = containerizer->launch(
+ containerId,
+ createContainerConfig(None(), executor, directory),
+ map<string, string>(),
+ None());
+
+ AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+ Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);
+
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
+ ASSERT_TRUE(wait->get().has_status());
+ EXPECT_WEXITSTATUS_EQ(0, wait->get().status());
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {