Merge branch 'master' of github.com:google/nsjail

This commit is contained in:
Robert Swiecki 2019-08-19 11:35:17 +02:00
commit ac6e19d4ec
10 changed files with 225 additions and 8 deletions

View File

@ -37,7 +37,7 @@ LDFLAGS += -pie -Wl,-z,noexecstack -lpthread $(shell pkg-config --libs protobuf)
BIN = nsjail
LIBS = kafel/libkafel.a
SRCS_CXX = caps.cc cgroup.cc cmdline.cc config.cc contain.cc cpu.cc logs.cc mnt.cc net.cc nsjail.cc pid.cc sandbox.cc subproc.cc uts.cc user.cc util.cc
SRCS_CXX = caps.cc cgroup.cc cgroup2.cc cmdline.cc config.cc contain.cc cpu.cc logs.cc mnt.cc net.cc nsjail.cc pid.cc sandbox.cc subproc.cc uts.cc user.cc util.cc
SRCS_PROTO = config.proto
SRCS_PB_CXX = $(SRCS_PROTO:.proto=.pb.cc)
SRCS_PB_H = $(SRCS_PROTO:.proto=.pb.h)
@ -101,6 +101,7 @@ indent:
caps.o: caps.h nsjail.h logs.h macros.h util.h
cgroup.o: cgroup.h nsjail.h logs.h util.h
cgroup2.o: cgroup2.h nsjail.h logs.h util.h
cmdline.o: cmdline.h nsjail.h caps.h config.h logs.h macros.h mnt.h user.h
cmdline.o: util.h
config.o: config.h nsjail.h caps.h cmdline.h config.pb.h logs.h macros.h
@ -114,8 +115,8 @@ net.o: net.h nsjail.h logs.h subproc.h
nsjail.o: nsjail.h cmdline.h logs.h macros.h net.h sandbox.h subproc.h util.h
pid.o: pid.h nsjail.h logs.h subproc.h
sandbox.o: sandbox.h nsjail.h kafel/include/kafel.h logs.h util.h
subproc.o: subproc.h nsjail.h cgroup.h contain.h logs.h macros.h net.h
subproc.o: sandbox.h user.h util.h
subproc.o: subproc.h nsjail.h cgroup.h cgroup2.h contain.h logs.h macros.h
subproc.o: net.h sandbox.h user.h util.h
uts.o: uts.h nsjail.h logs.h
user.o: user.h nsjail.h logs.h macros.h subproc.h util.h
util.o: util.h nsjail.h logs.h macros.h

View File

@ -43,7 +43,6 @@ static bool createCgroup(const std::string& cgroup_path, pid_t pid) {
PLOG_W("mkdir('%s', 0700) failed", cgroup_path.c_str());
return false;
}
return true;
}
@ -55,7 +54,6 @@ static bool writeToCgroup(
LOG_W("Could not update %s", what.c_str());
return false;
}
return true;
}

137
cgroup2.cc Normal file
View File

@ -0,0 +1,137 @@
/*
nsjail - cgroup2 namespacing
-----------------------------------------
Copyright 2014 Google Inc. All Rights Reserved.
Licensed 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 "cgroup2.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include "logs.h"
#include "util.h"
namespace cgroup2 {
static std::string getCgroupPath(nsjconf_t *nsjconf, pid_t pid) {
return nsjconf->cgroupv2_mount + "/NSJAIL." + std::to_string(pid);
}
static bool createCgroup(const std::string &cgroup_path, pid_t pid) {
LOG_D("Create '%s' for pid=%d", cgroup_path.c_str(), (int)pid);
if (mkdir(cgroup_path.c_str(), 0700) == -1 && errno != EEXIST) {
PLOG_W("mkdir('%s', 0700) failed", cgroup_path.c_str());
return false;
}
return true;
}
static bool writeToCgroup(
const std::string &cgroup_path, const std::string &resource, const std::string &value) {
LOG_I("Setting '%s' to '%s'", resource.c_str(), value.c_str());
if (!util::writeBufToFile(
(cgroup_path + "/" + resource).c_str(), value.c_str(), value.length(), O_WRONLY)) {
LOG_W("Could not update %s", resource.c_str());
return false;
}
return true;
}
static bool addPidToProcList(const std::string &cgroup_path, pid_t pid) {
std::string pid_str = std::to_string(pid);
LOG_D("Adding pid='%s' to cgroup.procs", pid_str.c_str());
if (!util::writeBufToFile((cgroup_path + "/cgroup.procs").c_str(), pid_str.c_str(),
pid_str.length(), O_WRONLY)) {
LOG_W("Could not update cgroup.procs");
return false;
}
return true;
}
static void removeCgroup(const std::string &cgroup_path) {
LOG_D("Remove '%s'", cgroup_path.c_str());
if (rmdir(cgroup_path.c_str()) == -1) {
PLOG_W("rmdir('%s') failed", cgroup_path.c_str());
}
}
static bool initNsFromParentMem(nsjconf_t *nsjconf, pid_t pid) {
if (nsjconf->cgroup_mem_max == (size_t)0) {
return true;
}
std::string cgroup_path = getCgroupPath(nsjconf, pid);
RETURN_ON_FAILURE(createCgroup(cgroup_path, pid));
RETURN_ON_FAILURE(addPidToProcList(cgroup_path, pid));
return writeToCgroup(cgroup_path, "memory.max", std::to_string(nsjconf->cgroup_mem_max));
}
static bool initNsFromParentPids(nsjconf_t *nsjconf, pid_t pid) {
if (nsjconf->cgroup_pids_max == 0U) {
return true;
}
std::string cgroup_path = getCgroupPath(nsjconf, pid);
RETURN_ON_FAILURE(createCgroup(cgroup_path, pid));
RETURN_ON_FAILURE(addPidToProcList(cgroup_path, pid));
return writeToCgroup(cgroup_path, "pids.max", std::to_string(nsjconf->cgroup_pids_max));
}
static bool initNsFromParentCpu(nsjconf_t *nsjconf, pid_t pid) {
if (nsjconf->cgroup_cpu_ms_per_sec == 0U) {
return true;
}
std::string cgroup_path = getCgroupPath(nsjconf, pid);
RETURN_ON_FAILURE(createCgroup(cgroup_path, pid));
RETURN_ON_FAILURE(addPidToProcList(cgroup_path, pid));
// The maximum bandwidth limit in the format: `$MAX $PERIOD`.
// This indicates that the group may consume up to $MAX in each $PERIOD
// duration.
std::string cpu_ms_per_sec_str = std::to_string(nsjconf->cgroup_cpu_ms_per_sec * 1000U);
cpu_ms_per_sec_str += " 1000000";
return writeToCgroup(cgroup_path, "cpu.max", cpu_ms_per_sec_str);
}
bool initNsFromParent(nsjconf_t *nsjconf, pid_t pid) {
RETURN_ON_FAILURE(initNsFromParentMem(nsjconf, pid));
RETURN_ON_FAILURE(initNsFromParentPids(nsjconf, pid));
return initNsFromParentCpu(nsjconf, pid);
}
void finishFromParent(nsjconf_t *nsjconf, pid_t pid) {
if (nsjconf->cgroup_mem_max != (size_t)0 || nsjconf->cgroup_pids_max != 0U ||
nsjconf->cgroup_cpu_ms_per_sec != 0U) {
removeCgroup(getCgroupPath(nsjconf, pid));
}
}
} // namespace cgroup2

38
cgroup2.h Normal file
View File

@ -0,0 +1,38 @@
/*
nsjail - cgroup2 namespacing
-----------------------------------------
Copyright 2014 Google Inc. All Rights Reserved.
Licensed 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 NS_CGROUP2_H
#define NS_CGROUP2_H
#include <stdbool.h>
#include <stddef.h>
#include "nsjail.h"
namespace cgroup2 {
bool initNsFromParent(nsjconf_t* nsjconf, pid_t pid);
bool initNs(void);
void finishFromParent(nsjconf_t* nsjconf, pid_t pid);
} // namespace cgroup2
#endif /* _CGROUP2_H */

View File

@ -146,6 +146,8 @@ struct custom_option custom_opts[] = {
{ { "cgroup_cpu_ms_per_sec", required_argument, NULL, 0x0831 }, "Number of milliseconds of CPU time per second that the process group can use (default: '0' - no limit)" },
{ { "cgroup_cpu_mount", required_argument, NULL, 0x0832 }, "Location of cpu cgroup FS (default: '/sys/fs/cgroup/net_cls')" },
{ { "cgroup_cpu_parent", required_argument, NULL, 0x0833 }, "Which pre-existing cpu cgroup to use as a parent (default: 'NSJAIL')" },
{ { "cgroupv2_mount", required_argument, NULL, 0x0834}, "Location of cgroupv2 directory (default: '/sys/fs/cgroup')"},
{ { "use_cgroupv2", no_argument, NULL, 0x0835}, "Use cgroup v2"},
{ { "iface_no_lo", no_argument, NULL, 0x700 }, "Don't bring the 'lo' interface up" },
{ { "iface_own", required_argument, NULL, 0x704 }, "Move this existing network interface into the new NET namespace. Can be specified multiple times" },
{ { "macvlan_iface", required_argument, NULL, 'I' }, "Interface which will be cloned (MACVLAN) and put inside the subprocess' namespace as 'vs'" },
@ -434,6 +436,8 @@ std::unique_ptr<nsjconf_t> parseArgs(int argc, char* argv[]) {
nsjconf->cgroup_cpu_mount = "/sys/fs/cgroup/cpu";
nsjconf->cgroup_cpu_parent = "NSJAIL";
nsjconf->cgroup_cpu_ms_per_sec = 0U;
nsjconf->cgroupv2_mount = "/sys/fs/cgroup";
nsjconf->use_cgroupv2 = false;
nsjconf->iface_lo = true;
nsjconf->iface_vs_ip = "0.0.0.0";
nsjconf->iface_vs_nm = "255.255.255.0";
@ -827,6 +831,12 @@ std::unique_ptr<nsjconf_t> parseArgs(int argc, char* argv[]) {
case 0x833:
nsjconf->cgroup_cpu_parent = optarg;
break;
case 0x834:
nsjconf->cgroupv2_mount = optarg;
break;
case 0x835:
nsjconf->use_cgroupv2 = true;
break;
case 'P':
nsjconf->kafel_file_path = optarg;
break;

View File

@ -253,6 +253,8 @@ static bool configParseInternal(nsjconf_t* nsjconf, const nsjail::NsJailConfig&
nsjconf->cgroup_cpu_ms_per_sec = njc.cgroup_cpu_ms_per_sec();
nsjconf->cgroup_cpu_mount = njc.cgroup_cpu_mount();
nsjconf->cgroup_cpu_parent = njc.cgroup_cpu_parent();
nsjconf->cgroupv2_mount = njc.cgroupv2_mount();
nsjconf->use_cgroupv2 = njc.use_cgroupv2();
nsjconf->iface_lo = !(njc.iface_no_lo());
for (ssize_t i = 0; i < njc.iface_own().size(); i++) {

View File

@ -243,4 +243,10 @@ message NsJailConfig {
/* Binary path (with arguments) to be executed. If not specified here, it
can be specified with cmd-line as "-- /path/to/command arg1 arg2" */
optional Exe exec_bin = 81;
/* Mount point for cgroup v2 in your system */
optional string cgroupv2_mount = 82 [default = "/sys/fs/cgroup"];
/* Use cgroup v2 */
optional bool use_cgroupv2 = 83 [default = false];
}

View File

@ -247,6 +247,12 @@ Location of cpu cgroup FS (default: '/sys/fs/cgroup/net_cls')
\fB\-\-cgroup_cpu_parent\fR VALUE
Which pre-existing cpu cgroup to use as a parent (default: 'NSJAIL')
.TP
\fB\-\-cgroupv2_mount\fR VALUE
Location of cgroup v2 directory (default: '/sys/fs/cgroup')
.TP
\fB\-\-use_cgroupv2\fR
Use cgroup v2
.TP
\fB\-\-iface_no_lo\fR
Don't bring the 'lo' interface up
.TP

View File

@ -138,6 +138,8 @@ struct nsjconf_t {
std::string cgroup_cpu_mount;
std::string cgroup_cpu_parent;
unsigned int cgroup_cpu_ms_per_sec;
std::string cgroupv2_mount;
bool use_cgroupv2;
std::string kafel_file_path;
std::string kafel_string;
struct sock_fprog seccomp_fprog;

View File

@ -45,6 +45,7 @@
#include <vector>
#include "cgroup.h"
#include "cgroup2.h"
#include "contain.h"
#include "logs.h"
#include "macros.h"
@ -141,7 +142,12 @@ static void subprocNewProc(nsjconf_t* nsjconf, int fd_in, int fd_out, int fd_err
LOG_E("Couldn't initialize net user namespace");
return;
}
if (!cgroup::initNsFromParent(nsjconf, getpid())) {
if (nsjconf->use_cgroupv2) {
if (!cgroup2::initNsFromParent(nsjconf, getpid())) {
LOG_E("Couldn't initialize net user namespace");
return;
}
} else if (!cgroup::initNsFromParent(nsjconf, getpid())) {
LOG_E("Couldn't initialize net user namespace");
return;
}
@ -296,7 +302,11 @@ static int reapProc(nsjconf_t* nsjconf, pid_t pid, bool should_wait = false) {
int status;
if (wait4(pid, &status, should_wait ? 0 : WNOHANG, NULL) == pid) {
if (nsjconf->use_cgroupv2) {
cgroup2::finishFromParent(nsjconf, pid);
} else {
cgroup::finishFromParent(nsjconf, pid);
}
std::string remote_txt = "[UNKNOWN]";
const pids_t* elem = getPidElem(nsjconf, pid);
@ -378,10 +388,17 @@ static bool initParent(nsjconf_t* nsjconf, pid_t pid, int pipefd) {
LOG_E("Couldn't initialize net namespace for pid=%d", pid);
return false;
}
if (!cgroup::initNsFromParent(nsjconf, pid)) {
if (nsjconf->use_cgroupv2) {
if (!cgroup2::initNsFromParent(nsjconf, pid)) {
LOG_E("Couldn't initialize cgroup 2 user namespace for pid=%d", pid);
exit(0xff);
}
} else if (!cgroup::initNsFromParent(nsjconf, pid)) {
LOG_E("Couldn't initialize cgroup user namespace for pid=%d", pid);
exit(0xff);
}
if (!user::initNsFromParent(nsjconf, pid)) {
LOG_E("Couldn't initialize user namespace for pid=%d", pid);
return false;