Initial applications isolation support using Linux namespaces.
This commit is contained in:
19
auto/capability
Normal file
19
auto/capability
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
# Copyright (C) Igor Sysoev
|
||||
# Copyright (C) NGINX, Inc.
|
||||
|
||||
# Linux capability
|
||||
|
||||
nxt_feature="Linux capability"
|
||||
nxt_feature_name=NXT_HAVE_LINUX_CAPABILITY
|
||||
nxt_feature_test="#include <linux/capability.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
int main() {
|
||||
struct __user_cap_header_struct hdr;
|
||||
hdr.version = _LINUX_CAPABILITY_VERSION;
|
||||
syscall(SYS_capget, &hdr, 0);
|
||||
return 0;
|
||||
}"
|
||||
. auto/feature
|
||||
52
auto/isolation
Normal file
52
auto/isolation
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright (C) Igor Sysoev
|
||||
# Copyright (C) NGINX, Inc.
|
||||
|
||||
# Linux clone syscall.
|
||||
|
||||
NXT_ISOLATION=NO
|
||||
NXT_HAVE_CLONE=NO
|
||||
|
||||
nsflags="USER NS PID NET UTS CGROUP"
|
||||
|
||||
nxt_feature="clone(2)"
|
||||
nxt_feature_name=NXT_HAVE_CLONE
|
||||
nxt_feature_run=no
|
||||
nxt_feature_incs=
|
||||
nxt_feature_libs=
|
||||
nxt_feature_test="#include <sys/wait.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
int main() {
|
||||
return __NR_clone | SIGCHLD;
|
||||
}"
|
||||
. auto/feature
|
||||
|
||||
if [ $nxt_found = yes ]; then
|
||||
NXT_HAVE_CLONE=YES
|
||||
|
||||
# Test all isolation flags
|
||||
for flag in $nsflags; do
|
||||
nxt_feature="CLONE_NEW${flag}"
|
||||
nxt_feature_name=NXT_HAVE_CLONE_NEW${flag}
|
||||
nxt_feature_run=no
|
||||
nxt_feature_incs=
|
||||
nxt_feature_libs=
|
||||
nxt_feature_test="#define _GNU_SOURCE
|
||||
#include <sys/wait.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sched.h>
|
||||
|
||||
int main() {
|
||||
return CLONE_NEW$flag;
|
||||
}"
|
||||
. auto/feature
|
||||
|
||||
if [ $nxt_found = yes ]; then
|
||||
if [ "$NXT_ISOLATION" = "NO" ]; then
|
||||
NXT_ISOLATION=$flag
|
||||
else
|
||||
NXT_ISOLATION="$NXT_ISOLATION $flag"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -71,6 +71,7 @@ NXT_LIB_SRCS=" \
|
||||
src/nxt_upstream_round_robin.c \
|
||||
src/nxt_http_parse.c \
|
||||
src/nxt_app_log.c \
|
||||
src/nxt_capability.c \
|
||||
src/nxt_runtime.c \
|
||||
src/nxt_conf.c \
|
||||
src/nxt_conf_validation.c \
|
||||
@@ -132,6 +133,7 @@ NXT_LIB_SOLARIS_SENDFILEV_SRCS="src/nxt_solaris_sendfilev.c"
|
||||
NXT_LIB_MACOSX_SENDFILE_SRCS="src/nxt_macosx_sendfile.c"
|
||||
NXT_LIB_AIX_SEND_FILE_SRCS="src/nxt_aix_send_file.c"
|
||||
NXT_LIB_HPUX_SENDFILE_SRCS="src/nxt_hpux_sendfile.c"
|
||||
NXT_LIB_CLONE_SRCS="src/nxt_clone.c"
|
||||
|
||||
NXT_TEST_BUILD_DEPS="src/nxt_test_build.h"
|
||||
NXT_TEST_BUILD_SRCS="src/nxt_test_build.c"
|
||||
@@ -257,6 +259,11 @@ if [ "$NXT_HAVE_HPUX_SENDFILE" = "YES" \
|
||||
fi
|
||||
|
||||
|
||||
if [ "$NXT_HAVE_CLONE" = "YES" ]; then
|
||||
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_CLONE_SRCS"
|
||||
fi
|
||||
|
||||
|
||||
if [ "$NXT_TEST_BUILD" = "YES" ]; then
|
||||
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_TEST_BUILD_SRCS"
|
||||
fi
|
||||
|
||||
@@ -26,6 +26,8 @@ Unit configuration summary:
|
||||
Unix domain sockets support: $NXT_UNIX_DOMAIN
|
||||
TLS support: ............... $NXT_OPENSSL
|
||||
|
||||
process isolation: ......... $NXT_ISOLATION
|
||||
|
||||
debug logging: ............. $NXT_DEBUG
|
||||
|
||||
END
|
||||
|
||||
2
configure
vendored
2
configure
vendored
@@ -126,6 +126,8 @@ NXT_LIBRT=
|
||||
. auto/os/conf
|
||||
. auto/ssltls
|
||||
. auto/pcre
|
||||
. auto/isolation
|
||||
. auto/capability
|
||||
|
||||
|
||||
case "$NXT_SYSTEM_PLATFORM" in
|
||||
|
||||
@@ -88,6 +88,8 @@ struct nxt_common_app_conf_s {
|
||||
char *working_directory;
|
||||
nxt_conf_value_t *environment;
|
||||
|
||||
nxt_conf_value_t *isolation;
|
||||
|
||||
union {
|
||||
nxt_external_app_conf_t external;
|
||||
nxt_python_app_conf_t python;
|
||||
|
||||
104
src/nxt_capability.c
Normal file
104
src/nxt_capability.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include <nxt_main.h>
|
||||
|
||||
#if (NXT_HAVE_LINUX_CAPABILITY)
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#define nxt_capget(hdrp, datap) \
|
||||
syscall(SYS_capget, hdrp, datap)
|
||||
#define nxt_capset(hdrp, datap) \
|
||||
syscall(SYS_capset, hdrp, datap)
|
||||
|
||||
#endif /* NXT_HAVE_LINUX_CAPABILITY */
|
||||
|
||||
|
||||
static nxt_int_t nxt_capability_specific_set(nxt_task_t *task,
|
||||
nxt_capabilities_t *cap);
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_capability_set(nxt_task_t *task, nxt_capabilities_t *cap)
|
||||
{
|
||||
nxt_assert(cap->setid == 0);
|
||||
|
||||
if (geteuid() == 0) {
|
||||
cap->setid = 1;
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
return nxt_capability_specific_set(task, cap);
|
||||
}
|
||||
|
||||
|
||||
#if (NXT_HAVE_LINUX_CAPABILITY)
|
||||
|
||||
static uint32_t
|
||||
nxt_capability_linux_get_version()
|
||||
{
|
||||
struct __user_cap_header_struct hdr;
|
||||
|
||||
hdr.version = _LINUX_CAPABILITY_VERSION;
|
||||
hdr.pid = nxt_pid;
|
||||
|
||||
nxt_capget(&hdr, NULL);
|
||||
return hdr.version;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_capability_specific_set(nxt_task_t *task, nxt_capabilities_t *cap)
|
||||
{
|
||||
struct __user_cap_data_struct *val, data[2];
|
||||
struct __user_cap_header_struct hdr;
|
||||
|
||||
/*
|
||||
* Linux capability v1 fills an u32 struct.
|
||||
* Linux capability v2 and v3 fills an u64 struct.
|
||||
* We allocate data[2] for compatibility, we waste 4 bytes on v1.
|
||||
*
|
||||
* This is safe as we only need to check CAP_SETUID and CAP_SETGID
|
||||
* that resides in the first 32-bit chunk.
|
||||
*/
|
||||
|
||||
val = &data[0];
|
||||
|
||||
/*
|
||||
* Ask the kernel the preferred capability version
|
||||
* instead of using _LINUX_CAPABILITY_VERSION from header.
|
||||
* This is safer when distributing a pre-compiled Unit binary.
|
||||
*/
|
||||
hdr.version = nxt_capability_linux_get_version();
|
||||
hdr.pid = nxt_pid;
|
||||
|
||||
if (nxt_slow_path(nxt_capget(&hdr, val) == -1)) {
|
||||
nxt_alert(task, "failed to get process capabilities: %E", nxt_errno);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if ((val->effective & (1 << CAP_SETUID)) == 0) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
if ((val->effective & (1 << CAP_SETGID)) == 0) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
cap->setid = 1;
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static nxt_int_t
|
||||
nxt_capability_specific_set(nxt_task_t *task, nxt_capabilities_t *cap)
|
||||
{
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
17
src/nxt_capability.h
Normal file
17
src/nxt_capability.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _NXT_CAPABILITY_INCLUDED_
|
||||
#define _NXT_CAPABILITY_INCLUDED_
|
||||
|
||||
typedef struct {
|
||||
uint8_t setid; /* 1 bit */
|
||||
} nxt_capabilities_t;
|
||||
|
||||
|
||||
NXT_EXPORT nxt_int_t nxt_capability_set(nxt_task_t *task,
|
||||
nxt_capabilities_t *cap);
|
||||
|
||||
#endif /* _NXT_CAPABILITY_INCLUDED_ */
|
||||
263
src/nxt_clone.c
Normal file
263
src/nxt_clone.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include <nxt_main.h>
|
||||
#include <sys/types.h>
|
||||
#include <nxt_conf.h>
|
||||
#include <nxt_clone.h>
|
||||
|
||||
#if (NXT_HAVE_CLONE)
|
||||
|
||||
pid_t
|
||||
nxt_clone(nxt_int_t flags)
|
||||
{
|
||||
#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__)
|
||||
return syscall(__NR_clone, NULL, flags);
|
||||
#else
|
||||
return syscall(__NR_clone, flags, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
|
||||
/* map uid 65534 to unit pid */
|
||||
#define NXT_DEFAULT_UNPRIV_MAP "65534 %d 1"
|
||||
|
||||
nxt_int_t nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid,
|
||||
const char *str);
|
||||
nxt_int_t nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile,
|
||||
pid_t pid, nxt_int_t defval, nxt_conf_value_t *mapobj);
|
||||
nxt_int_t nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile,
|
||||
pid_t pid, u_char *mapinfo);
|
||||
|
||||
|
||||
typedef struct {
|
||||
nxt_int_t container;
|
||||
nxt_int_t host;
|
||||
nxt_int_t size;
|
||||
} nxt_clone_procmap_t;
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_clone_proc_setgroups(nxt_task_t *task, pid_t child_pid, const char *str)
|
||||
{
|
||||
int fd, n;
|
||||
u_char *p, *end;
|
||||
u_char path[PATH_MAX];
|
||||
|
||||
end = path + PATH_MAX;
|
||||
p = nxt_sprintf(path, end, "/proc/%d/setgroups", child_pid);
|
||||
*p = '\0';
|
||||
|
||||
if (nxt_slow_path(p == end)) {
|
||||
nxt_alert(task, "error write past the buffer: %s", path);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
fd = open((char *)path, O_RDWR);
|
||||
|
||||
if (fd == -1) {
|
||||
/*
|
||||
* If the /proc/pid/setgroups doesn't exists, we are
|
||||
* safe to set uid/gid maps. But if the error is anything
|
||||
* other than ENOENT, then we should abort and let user know.
|
||||
*/
|
||||
|
||||
if (errno != ENOENT) {
|
||||
nxt_alert(task, "open(%s): %E", path, nxt_errno);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
n = write(fd, str, strlen(str));
|
||||
close(fd);
|
||||
|
||||
if (nxt_slow_path(n == -1)) {
|
||||
nxt_alert(task, "write(%s): %E", path, nxt_errno);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_clone_proc_map_write(nxt_task_t *task, const char *mapfile, pid_t pid,
|
||||
u_char *mapinfo)
|
||||
{
|
||||
int len, mapfd;
|
||||
u_char *p, *end;
|
||||
ssize_t n;
|
||||
u_char buf[256];
|
||||
|
||||
end = buf + sizeof(buf);
|
||||
|
||||
p = nxt_sprintf(buf, end, "/proc/%d/%s", pid, mapfile);
|
||||
if (nxt_slow_path(p == end)) {
|
||||
nxt_alert(task, "writing past the buffer");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
mapfd = open((char*)buf, O_RDWR);
|
||||
if (nxt_slow_path(mapfd == -1)) {
|
||||
nxt_alert(task, "failed to open proc map (%s) %E", buf, nxt_errno);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
len = nxt_strlen(mapinfo);
|
||||
|
||||
n = write(mapfd, (char *)mapinfo, len);
|
||||
if (nxt_slow_path(n != len)) {
|
||||
|
||||
if (n == -1 && nxt_errno == EINVAL) {
|
||||
nxt_alert(task, "failed to write %s: Check kernel maximum " \
|
||||
"allowed lines %E", buf, nxt_errno);
|
||||
|
||||
} else {
|
||||
nxt_alert(task, "failed to write proc map (%s) %E", buf,
|
||||
nxt_errno);
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_clone_proc_map_set(nxt_task_t *task, const char* mapfile, pid_t pid,
|
||||
nxt_int_t defval, nxt_conf_value_t *mapobj)
|
||||
{
|
||||
u_char *p, *end, *mapinfo;
|
||||
nxt_int_t container, host, size;
|
||||
nxt_int_t ret, len, count, i;
|
||||
nxt_conf_value_t *obj, *value;
|
||||
|
||||
static nxt_str_t str_cont = nxt_string("container");
|
||||
static nxt_str_t str_host = nxt_string("host");
|
||||
static nxt_str_t str_size = nxt_string("size");
|
||||
|
||||
/*
|
||||
* uid_map one-entry size:
|
||||
* alloc space for 3 numbers (32bit) plus 2 spaces and \n.
|
||||
*/
|
||||
len = sizeof(u_char) * (10 + 10 + 10 + 2 + 1);
|
||||
|
||||
if (mapobj != NULL) {
|
||||
count = nxt_conf_array_elements_count(mapobj);
|
||||
|
||||
if (count == 0) {
|
||||
goto default_map;
|
||||
}
|
||||
|
||||
len = len * count + 1;
|
||||
|
||||
mapinfo = nxt_malloc(len);
|
||||
if (nxt_slow_path(mapinfo == NULL)) {
|
||||
nxt_alert(task, "failed to allocate uid_map buffer");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
p = mapinfo;
|
||||
end = mapinfo + len;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
obj = nxt_conf_get_array_element(mapobj, i);
|
||||
|
||||
value = nxt_conf_get_object_member(obj, &str_cont, NULL);
|
||||
container = nxt_conf_get_integer(value);
|
||||
|
||||
value = nxt_conf_get_object_member(obj, &str_host, NULL);
|
||||
host = nxt_conf_get_integer(value);
|
||||
|
||||
value = nxt_conf_get_object_member(obj, &str_size, NULL);
|
||||
size = nxt_conf_get_integer(value);
|
||||
|
||||
p = nxt_sprintf(p, end, "%d %d %d", container, host, size);
|
||||
if (nxt_slow_path(p == end)) {
|
||||
nxt_alert(task, "write past the uid_map buffer");
|
||||
nxt_free(mapinfo);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (i+1 < count) {
|
||||
*p++ = '\n';
|
||||
|
||||
} else {
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
default_map:
|
||||
|
||||
mapinfo = nxt_malloc(len);
|
||||
if (nxt_slow_path(mapinfo == NULL)) {
|
||||
nxt_alert(task, "failed to allocate uid_map buffer");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
end = mapinfo + len;
|
||||
p = nxt_sprintf(mapinfo, end, NXT_DEFAULT_UNPRIV_MAP, defval);
|
||||
*p = '\0';
|
||||
|
||||
if (nxt_slow_path(p == end)) {
|
||||
nxt_alert(task, "write past the %s buffer", mapfile);
|
||||
nxt_free(mapinfo);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nxt_clone_proc_map_write(task, mapfile, pid, mapinfo);
|
||||
|
||||
nxt_free(mapinfo);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_clone_proc_map(nxt_task_t *task, pid_t pid, nxt_process_clone_t *clone)
|
||||
{
|
||||
nxt_int_t ret;
|
||||
nxt_int_t uid, gid;
|
||||
const char *rule;
|
||||
nxt_runtime_t *rt;
|
||||
|
||||
rt = task->thread->runtime;
|
||||
uid = geteuid();
|
||||
gid = getegid();
|
||||
|
||||
rule = rt->capabilities.setid ? "allow" : "deny";
|
||||
|
||||
ret = nxt_clone_proc_map_set(task, "uid_map", pid, uid, clone->uidmap);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
ret = nxt_clone_proc_setgroups(task, pid, rule);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
nxt_alert(task, "failed to write /proc/%d/setgroups", pid);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
ret = nxt_clone_proc_map_set(task, "gid_map", pid, gid, clone->gidmap);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
17
src/nxt_clone.h
Normal file
17
src/nxt_clone.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _NXT_CLONE_INCLUDED_
|
||||
#define _NXT_CLONE_INCLUDED_
|
||||
|
||||
|
||||
pid_t nxt_clone(nxt_int_t flags);
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
nxt_int_t nxt_clone_proc_map(nxt_task_t *task, pid_t pid,
|
||||
nxt_process_clone_t *clone);
|
||||
#endif
|
||||
|
||||
#endif /* _NXT_CLONE_INCLUDED_ */
|
||||
@@ -39,9 +39,6 @@ typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value);
|
||||
typedef nxt_int_t (*nxt_conf_vldt_element_t)(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value);
|
||||
typedef nxt_int_t (*nxt_conf_vldt_system_t)(nxt_conf_validation_t *vldt,
|
||||
char *name);
|
||||
|
||||
|
||||
static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt,
|
||||
nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type);
|
||||
@@ -86,10 +83,6 @@ static nxt_int_t nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value, void *data);
|
||||
static nxt_int_t nxt_conf_vldt_array_iterator(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value, void *data);
|
||||
static nxt_int_t nxt_conf_vldt_system(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value, void *data);
|
||||
static nxt_int_t nxt_conf_vldt_user(nxt_conf_validation_t *vldt, char *name);
|
||||
static nxt_int_t nxt_conf_vldt_group(nxt_conf_validation_t *vldt, char *name);
|
||||
static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt,
|
||||
nxt_str_t *name, nxt_conf_value_t *value);
|
||||
static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt,
|
||||
@@ -101,6 +94,21 @@ static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt,
|
||||
static nxt_int_t nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value);
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
|
||||
void *data);
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value, void *data);
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
static nxt_int_t nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt,
|
||||
const char* mapfile, nxt_conf_value_t *value);
|
||||
static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value);
|
||||
static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt,
|
||||
nxt_conf_value_t *value);
|
||||
#endif
|
||||
|
||||
static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = {
|
||||
{ nxt_string("read_timeout"),
|
||||
@@ -340,6 +348,100 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = {
|
||||
};
|
||||
|
||||
|
||||
static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[] = {
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
{ nxt_string("credential"),
|
||||
NXT_CONF_VLDT_BOOLEAN,
|
||||
NULL,
|
||||
NULL },
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWPID)
|
||||
{ nxt_string("pid"),
|
||||
NXT_CONF_VLDT_BOOLEAN,
|
||||
NULL,
|
||||
NULL },
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWNET)
|
||||
{ nxt_string("network"),
|
||||
NXT_CONF_VLDT_BOOLEAN,
|
||||
NULL,
|
||||
NULL },
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWNS)
|
||||
{ nxt_string("mount"),
|
||||
NXT_CONF_VLDT_BOOLEAN,
|
||||
NULL,
|
||||
NULL },
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUTS)
|
||||
{ nxt_string("uname"),
|
||||
NXT_CONF_VLDT_BOOLEAN,
|
||||
NULL,
|
||||
NULL },
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWCGROUP)
|
||||
{ nxt_string("cgroup"),
|
||||
NXT_CONF_VLDT_BOOLEAN,
|
||||
NULL,
|
||||
NULL },
|
||||
#endif
|
||||
|
||||
NXT_CONF_VLDT_END
|
||||
};
|
||||
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
|
||||
static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = {
|
||||
{ nxt_string("container"),
|
||||
NXT_CONF_VLDT_INTEGER,
|
||||
NULL,
|
||||
NULL },
|
||||
|
||||
{ nxt_string("host"),
|
||||
NXT_CONF_VLDT_INTEGER,
|
||||
NULL,
|
||||
NULL },
|
||||
|
||||
{ nxt_string("size"),
|
||||
NXT_CONF_VLDT_INTEGER,
|
||||
NULL,
|
||||
NULL },
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = {
|
||||
{ nxt_string("namespaces"),
|
||||
NXT_CONF_VLDT_OBJECT,
|
||||
&nxt_conf_vldt_clone_namespaces,
|
||||
(void *) &nxt_conf_vldt_app_namespaces_members },
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
|
||||
{ nxt_string("uidmap"),
|
||||
NXT_CONF_VLDT_ARRAY,
|
||||
&nxt_conf_vldt_array_iterator,
|
||||
(void *) &nxt_conf_vldt_clone_uidmap },
|
||||
|
||||
{ nxt_string("gidmap"),
|
||||
NXT_CONF_VLDT_ARRAY,
|
||||
&nxt_conf_vldt_array_iterator,
|
||||
(void *) &nxt_conf_vldt_clone_gidmap },
|
||||
|
||||
#endif
|
||||
|
||||
NXT_CONF_VLDT_END
|
||||
};
|
||||
|
||||
|
||||
static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = {
|
||||
{ nxt_string("type"),
|
||||
NXT_CONF_VLDT_STRING,
|
||||
@@ -358,13 +460,13 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = {
|
||||
|
||||
{ nxt_string("user"),
|
||||
NXT_CONF_VLDT_STRING,
|
||||
nxt_conf_vldt_system,
|
||||
(void *) &nxt_conf_vldt_user },
|
||||
NULL,
|
||||
NULL },
|
||||
|
||||
{ nxt_string("group"),
|
||||
NXT_CONF_VLDT_STRING,
|
||||
nxt_conf_vldt_system,
|
||||
(void *) &nxt_conf_vldt_group },
|
||||
NULL,
|
||||
NULL },
|
||||
|
||||
{ nxt_string("working_directory"),
|
||||
NXT_CONF_VLDT_STRING,
|
||||
@@ -376,6 +478,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = {
|
||||
&nxt_conf_vldt_object_iterator,
|
||||
(void *) &nxt_conf_vldt_environment },
|
||||
|
||||
{ nxt_string("isolation"),
|
||||
NXT_CONF_VLDT_OBJECT,
|
||||
&nxt_conf_vldt_isolation,
|
||||
(void *) &nxt_conf_vldt_app_isolation_members },
|
||||
|
||||
NXT_CONF_VLDT_END
|
||||
};
|
||||
|
||||
@@ -1251,71 +1358,6 @@ nxt_conf_vldt_array_iterator(nxt_conf_validation_t *vldt,
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_system(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
|
||||
void *data)
|
||||
{
|
||||
size_t length;
|
||||
nxt_str_t name;
|
||||
nxt_conf_vldt_system_t validator;
|
||||
char string[32];
|
||||
|
||||
/* The cast is required by Sun C. */
|
||||
validator = (nxt_conf_vldt_system_t) data;
|
||||
|
||||
nxt_conf_get_string(value, &name);
|
||||
|
||||
length = name.length + 1;
|
||||
length = nxt_min(length, sizeof(string));
|
||||
|
||||
nxt_cpystrn((u_char *) string, name.start, length);
|
||||
|
||||
return validator(vldt, string);
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_user(nxt_conf_validation_t *vldt, char *user)
|
||||
{
|
||||
struct passwd *pwd;
|
||||
|
||||
nxt_errno = 0;
|
||||
|
||||
pwd = getpwnam(user);
|
||||
|
||||
if (pwd != NULL) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
if (nxt_errno == 0) {
|
||||
return nxt_conf_vldt_error(vldt, "User \"%s\" is not found.", user);
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_group(nxt_conf_validation_t *vldt, char *group)
|
||||
{
|
||||
struct group *grp;
|
||||
|
||||
nxt_errno = 0;
|
||||
|
||||
grp = getgrnam(group);
|
||||
|
||||
if (grp != NULL) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
if (nxt_errno == 0) {
|
||||
return nxt_conf_vldt_error(vldt, "Group \"%s\" is not found.", group);
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name,
|
||||
nxt_conf_value_t *value)
|
||||
@@ -1353,6 +1395,133 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name,
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
|
||||
void *data)
|
||||
{
|
||||
return nxt_conf_vldt_object(vldt, value, data);
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
|
||||
void *data)
|
||||
{
|
||||
return nxt_conf_vldt_object(vldt, value, data);
|
||||
}
|
||||
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
|
||||
typedef struct {
|
||||
nxt_int_t container;
|
||||
nxt_int_t host;
|
||||
nxt_int_t size;
|
||||
} nxt_conf_vldt_clone_procmap_conf_t;
|
||||
|
||||
|
||||
static nxt_conf_map_t nxt_conf_vldt_clone_procmap_conf_map[] = {
|
||||
{
|
||||
nxt_string("container"),
|
||||
NXT_CONF_MAP_INT32,
|
||||
offsetof(nxt_conf_vldt_clone_procmap_conf_t, container),
|
||||
},
|
||||
|
||||
{
|
||||
nxt_string("host"),
|
||||
NXT_CONF_MAP_INT32,
|
||||
offsetof(nxt_conf_vldt_clone_procmap_conf_t, host),
|
||||
},
|
||||
|
||||
{
|
||||
nxt_string("size"),
|
||||
NXT_CONF_MAP_INT32,
|
||||
offsetof(nxt_conf_vldt_clone_procmap_conf_t, size),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, const char *mapfile,
|
||||
nxt_conf_value_t *value)
|
||||
{
|
||||
nxt_int_t ret;
|
||||
nxt_conf_vldt_clone_procmap_conf_t procmap;
|
||||
|
||||
procmap.container = -1;
|
||||
procmap.host = -1;
|
||||
procmap.size = -1;
|
||||
|
||||
ret = nxt_conf_map_object(vldt->pool, value,
|
||||
nxt_conf_vldt_clone_procmap_conf_map,
|
||||
nxt_nitems(nxt_conf_vldt_clone_procmap_conf_map),
|
||||
&procmap);
|
||||
if (ret != NXT_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (procmap.container == -1) {
|
||||
return nxt_conf_vldt_error(vldt, "The %s requires the "
|
||||
"\"container\" field set.", mapfile);
|
||||
}
|
||||
|
||||
if (procmap.host == -1) {
|
||||
return nxt_conf_vldt_error(vldt, "The %s requires the "
|
||||
"\"host\" field set.", mapfile);
|
||||
}
|
||||
|
||||
if (procmap.size == -1) {
|
||||
return nxt_conf_vldt_error(vldt, "The %s requires the "
|
||||
"\"size\" field set.", mapfile);
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
|
||||
{
|
||||
nxt_int_t ret;
|
||||
|
||||
if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
|
||||
return nxt_conf_vldt_error(vldt, "The \"uidmap\" array "
|
||||
"must contain only object values.");
|
||||
}
|
||||
|
||||
ret = nxt_conf_vldt_object(vldt, value,
|
||||
(void *) nxt_conf_vldt_app_procmap_members);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nxt_conf_vldt_clone_procmap(vldt, "uid_map", value);
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
|
||||
{
|
||||
nxt_int_t ret;
|
||||
|
||||
if (nxt_conf_type(value) != NXT_CONF_OBJECT) {
|
||||
return nxt_conf_vldt_error(vldt, "The \"gidmap\" array "
|
||||
"must contain only object values.");
|
||||
}
|
||||
|
||||
ret = nxt_conf_vldt_object(vldt, value,
|
||||
(void *) nxt_conf_vldt_app_procmap_members);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nxt_conf_vldt_clone_procmap(vldt, "gid_map", value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value)
|
||||
{
|
||||
|
||||
@@ -57,6 +57,7 @@ typedef uint16_t nxt_port_id_t;
|
||||
#include <nxt_fiber.h>
|
||||
#include <nxt_thread.h>
|
||||
#include <nxt_process_type.h>
|
||||
#include <nxt_capability.h>
|
||||
#include <nxt_process.h>
|
||||
#include <nxt_utf8.h>
|
||||
#include <nxt_file_name.h>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#include <nxt_cert.h>
|
||||
#endif
|
||||
|
||||
#ifdef NXT_LINUX
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
nxt_socket_t socket;
|
||||
@@ -68,6 +72,10 @@ static void nxt_main_port_conf_store_handler(nxt_task_t *task,
|
||||
static void nxt_main_port_access_log_handler(nxt_task_t *task,
|
||||
nxt_port_recv_msg_t *msg);
|
||||
|
||||
static nxt_int_t nxt_init_set_isolation(nxt_task_t *task,
|
||||
nxt_process_init_t *init, nxt_conf_value_t *isolation);
|
||||
static nxt_int_t nxt_init_set_ns(nxt_task_t *task,
|
||||
nxt_process_init_t *init, nxt_conf_value_t *ns);
|
||||
|
||||
const nxt_sig_event_t nxt_main_process_signals[] = {
|
||||
nxt_event_signal(SIGHUP, nxt_main_process_signal_handler),
|
||||
@@ -134,6 +142,12 @@ static nxt_conf_map_t nxt_common_app_conf[] = {
|
||||
NXT_CONF_MAP_PTR,
|
||||
offsetof(nxt_common_app_conf_t, environment),
|
||||
},
|
||||
|
||||
{
|
||||
nxt_string("isolation"),
|
||||
NXT_CONF_MAP_PTR,
|
||||
offsetof(nxt_common_app_conf_t, isolation),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -271,12 +285,11 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
||||
nxt_int_t ret;
|
||||
nxt_buf_t *b;
|
||||
nxt_port_t *port;
|
||||
nxt_runtime_t *rt;
|
||||
nxt_app_type_t idx;
|
||||
nxt_conf_value_t *conf;
|
||||
nxt_common_app_conf_t app_conf;
|
||||
|
||||
static nxt_str_t nobody = nxt_string("nobody");
|
||||
|
||||
ret = NXT_ERROR;
|
||||
|
||||
mp = nxt_mp_create(1024, 128, 256, 32);
|
||||
@@ -311,7 +324,10 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
app_conf.user = nobody;
|
||||
rt = task->thread->runtime;
|
||||
|
||||
app_conf.user.start = (u_char*)rt->user_cred.user;
|
||||
app_conf.user.length = nxt_strlen(rt->user_cred.user);
|
||||
|
||||
ret = nxt_conf_map_object(mp, conf, nxt_common_app_conf,
|
||||
nxt_nitems(nxt_common_app_conf), &app_conf);
|
||||
@@ -458,6 +474,8 @@ nxt_main_start_controller_process(nxt_task_t *task, nxt_runtime_t *rt)
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
nxt_memzero(init, sizeof(nxt_process_init_t));
|
||||
|
||||
init->start = nxt_controller_start;
|
||||
init->name = "controller";
|
||||
init->user_cred = &rt->user_cred;
|
||||
@@ -552,6 +570,8 @@ nxt_main_start_discovery_process(nxt_task_t *task, nxt_runtime_t *rt)
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
nxt_memzero(init, sizeof(nxt_process_init_t));
|
||||
|
||||
init->start = nxt_discovery_start;
|
||||
init->name = "discovery";
|
||||
init->user_cred = &rt->user_cred;
|
||||
@@ -576,6 +596,8 @@ nxt_main_start_router_process(nxt_task_t *task, nxt_runtime_t *rt)
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
nxt_memzero(init, sizeof(nxt_process_init_t));
|
||||
|
||||
init->start = nxt_router_start;
|
||||
init->name = "router";
|
||||
init->user_cred = &rt->user_cred;
|
||||
@@ -589,7 +611,6 @@ nxt_main_start_router_process(nxt_task_t *task, nxt_runtime_t *rt)
|
||||
return nxt_main_create_worker_process(task, rt, init);
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
|
||||
nxt_common_app_conf_t *app_conf, uint32_t stream)
|
||||
@@ -597,41 +618,72 @@ nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
|
||||
char *user, *group;
|
||||
u_char *title, *last, *end;
|
||||
size_t size;
|
||||
nxt_int_t ret;
|
||||
nxt_process_init_t *init;
|
||||
|
||||
size = sizeof(nxt_process_init_t)
|
||||
+ sizeof(nxt_user_cred_t)
|
||||
+ app_conf->user.length + 1
|
||||
+ app_conf->group.length + 1
|
||||
+ app_conf->name.length + sizeof("\"\" application");
|
||||
+ app_conf->name.length
|
||||
+ sizeof("\"\" application");
|
||||
|
||||
if (rt->capabilities.setid) {
|
||||
size += sizeof(nxt_user_cred_t)
|
||||
+ app_conf->user.length + 1
|
||||
+ app_conf->group.length + 1;
|
||||
}
|
||||
|
||||
init = nxt_malloc(size);
|
||||
if (nxt_slow_path(init == NULL)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
init->user_cred = nxt_pointer_to(init, sizeof(nxt_process_init_t));
|
||||
user = nxt_pointer_to(init->user_cred, sizeof(nxt_user_cred_t));
|
||||
nxt_memzero(init, sizeof(nxt_process_init_t));
|
||||
|
||||
nxt_memcpy(user, app_conf->user.start, app_conf->user.length);
|
||||
last = nxt_pointer_to(user, app_conf->user.length);
|
||||
*last++ = '\0';
|
||||
if (rt->capabilities.setid) {
|
||||
init->user_cred = nxt_pointer_to(init, sizeof(nxt_process_init_t));
|
||||
user = nxt_pointer_to(init->user_cred, sizeof(nxt_user_cred_t));
|
||||
|
||||
init->user_cred->user = user;
|
||||
|
||||
if (app_conf->group.start != NULL) {
|
||||
group = (char *) last;
|
||||
|
||||
nxt_memcpy(group, app_conf->group.start, app_conf->group.length);
|
||||
last = nxt_pointer_to(group, app_conf->group.length);
|
||||
nxt_memcpy(user, app_conf->user.start, app_conf->user.length);
|
||||
last = nxt_pointer_to(user, app_conf->user.length);
|
||||
*last++ = '\0';
|
||||
|
||||
} else {
|
||||
group = NULL;
|
||||
}
|
||||
init->user_cred->user = user;
|
||||
|
||||
if (nxt_user_cred_get(task, init->user_cred, group) != NXT_OK) {
|
||||
return NXT_ERROR;
|
||||
if (app_conf->group.start != NULL) {
|
||||
group = (char *) last;
|
||||
|
||||
nxt_memcpy(group, app_conf->group.start, app_conf->group.length);
|
||||
last = nxt_pointer_to(group, app_conf->group.length);
|
||||
*last++ = '\0';
|
||||
|
||||
} else {
|
||||
group = NULL;
|
||||
}
|
||||
|
||||
ret = nxt_user_cred_get(task, init->user_cred, group);
|
||||
if (ret != NXT_OK) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!nxt_str_eq(&app_conf->user, (u_char *) rt->user_cred.user,
|
||||
nxt_strlen(rt->user_cred.user)))
|
||||
{
|
||||
nxt_alert(task, "cannot set user \"%V\" for app \"%V\": "
|
||||
"missing capabilities", &app_conf->user, &app_conf->name);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (app_conf->group.length > 0
|
||||
&& !nxt_str_eq(&app_conf->group, (u_char *) rt->group,
|
||||
nxt_strlen(rt->group)))
|
||||
{
|
||||
nxt_alert(task, "cannot set group \"%V\" for app \"%V\": "
|
||||
"missing capabilities", &app_conf->group,
|
||||
&app_conf->name);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
last = nxt_pointer_to(init, sizeof(nxt_process_init_t));
|
||||
}
|
||||
|
||||
title = last;
|
||||
@@ -648,6 +700,11 @@ nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt,
|
||||
init->stream = stream;
|
||||
init->restart = NULL;
|
||||
|
||||
ret = nxt_init_set_isolation(task, init, app_conf->isolation);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
return nxt_main_create_worker_process(task, rt, init);
|
||||
}
|
||||
|
||||
@@ -1246,7 +1303,7 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
||||
nxt_conf_value_t *conf, *root, *value;
|
||||
nxt_app_lang_module_t *lang;
|
||||
|
||||
static nxt_str_t root_path = nxt_string("/");
|
||||
static nxt_str_t root_path = nxt_string("/");
|
||||
|
||||
rt = task->thread->runtime;
|
||||
|
||||
@@ -1438,3 +1495,105 @@ nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
||||
msg->port_msg.stream, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_init_set_isolation(nxt_task_t *task, nxt_process_init_t *init,
|
||||
nxt_conf_value_t *isolation)
|
||||
{
|
||||
nxt_int_t ret;
|
||||
nxt_conf_value_t *object;
|
||||
|
||||
static nxt_str_t nsname = nxt_string("namespaces");
|
||||
static nxt_str_t uidname = nxt_string("uidmap");
|
||||
static nxt_str_t gidname = nxt_string("gidmap");
|
||||
|
||||
if (isolation == NULL) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
object = nxt_conf_get_object_member(isolation, &nsname, NULL);
|
||||
if (object != NULL) {
|
||||
ret = nxt_init_set_ns(task, init, object);
|
||||
if (ret != NXT_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
object = nxt_conf_get_object_member(isolation, &uidname, NULL);
|
||||
if (object != NULL) {
|
||||
init->isolation.clone.uidmap = object;
|
||||
}
|
||||
|
||||
object = nxt_conf_get_object_member(isolation, &gidname, NULL);
|
||||
if (object != NULL) {
|
||||
init->isolation.clone.gidmap = object;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_init_set_ns(nxt_task_t *task, nxt_process_init_t *init, nxt_conf_value_t *namespaces)
|
||||
{
|
||||
uint32_t index;
|
||||
nxt_str_t name;
|
||||
nxt_int_t flag;
|
||||
nxt_conf_value_t *value;
|
||||
|
||||
index = 0;
|
||||
|
||||
while ((value = nxt_conf_next_object_member(namespaces, &name, &index)) != NULL) {
|
||||
flag = 0;
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
if (nxt_str_eq(&name, "credential", 10)) {
|
||||
flag = CLONE_NEWUSER;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWPID)
|
||||
if (nxt_str_eq(&name, "pid", 3)) {
|
||||
flag = CLONE_NEWPID;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWNET)
|
||||
if (nxt_str_eq(&name, "network", 7)) {
|
||||
flag = CLONE_NEWNET;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUTS)
|
||||
if (nxt_str_eq(&name, "uname", 5)) {
|
||||
flag = CLONE_NEWUTS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWNS)
|
||||
if (nxt_str_eq(&name, "mount", 5)) {
|
||||
flag = CLONE_NEWNS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWCGROUP)
|
||||
if (nxt_str_eq(&name, "cgroup", 6)) {
|
||||
flag = CLONE_NEWCGROUP;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!flag) {
|
||||
nxt_alert(task, "unknown namespace flag: \"%V\"", &name);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nxt_conf_get_integer(value) == 0) {
|
||||
continue; /* process shares everything by default */
|
||||
}
|
||||
|
||||
init->isolation.clone.flags |= flag;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,16 @@
|
||||
#include <nxt_main.h>
|
||||
#include <nxt_main_process.h>
|
||||
|
||||
#if (NXT_HAVE_CLONE)
|
||||
#include <nxt_clone.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
|
||||
static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
|
||||
|
||||
static nxt_int_t nxt_process_worker_setup(nxt_task_t *task,
|
||||
nxt_process_t *process, int parentfd);
|
||||
|
||||
/* A cached process pid. */
|
||||
nxt_pid_t nxt_pid;
|
||||
@@ -34,84 +40,217 @@ nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
|
||||
{ 0, 0, 0, 1, 0 },
|
||||
};
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) {
|
||||
pid_t rpid, pid;
|
||||
ssize_t n;
|
||||
nxt_int_t parent_status;
|
||||
nxt_process_t *p;
|
||||
nxt_runtime_t *rt;
|
||||
nxt_process_init_t *init;
|
||||
nxt_process_type_t ptype;
|
||||
|
||||
pid = getpid();
|
||||
rpid = 0;
|
||||
rt = task->thread->runtime;
|
||||
init = process->init;
|
||||
|
||||
/* Setup the worker process. */
|
||||
|
||||
n = read(parentfd, &rpid, sizeof(rpid));
|
||||
if (nxt_slow_path(n == -1 || n != sizeof(rpid))) {
|
||||
nxt_alert(task, "failed to read real pid");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nxt_slow_path(rpid == 0)) {
|
||||
nxt_alert(task, "failed to get real pid from parent");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
nxt_pid = rpid;
|
||||
|
||||
/* Clean inherited cached thread tid. */
|
||||
task->thread->tid = 0;
|
||||
|
||||
process->pid = nxt_pid;
|
||||
|
||||
if (nxt_pid != pid) {
|
||||
nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid);
|
||||
nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid);
|
||||
}
|
||||
|
||||
n = read(parentfd, &parent_status, sizeof(parent_status));
|
||||
if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) {
|
||||
nxt_alert(task, "failed to read parent status");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nxt_slow_path(close(parentfd) == -1)) {
|
||||
nxt_alert(task, "failed to close reader pipe fd");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nxt_slow_path(parent_status != NXT_OK)) {
|
||||
return parent_status;
|
||||
}
|
||||
|
||||
ptype = init->type;
|
||||
|
||||
nxt_port_reset_next_id();
|
||||
|
||||
nxt_event_engine_thread_adopt(task->thread->engine);
|
||||
|
||||
/* Remove not ready processes. */
|
||||
nxt_runtime_process_each(rt, p) {
|
||||
|
||||
if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
|
||||
nxt_debug(task, "remove not required process %PI", p->pid);
|
||||
|
||||
nxt_process_close_ports(task, p);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!p->ready) {
|
||||
nxt_debug(task, "remove not ready process %PI", p->pid);
|
||||
|
||||
nxt_process_close_ports(task, p);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
nxt_port_mmaps_destroy(&p->incoming, 0);
|
||||
nxt_port_mmaps_destroy(&p->outgoing, 0);
|
||||
|
||||
} nxt_runtime_process_loop;
|
||||
|
||||
nxt_runtime_process_add(task, process);
|
||||
|
||||
nxt_process_start(task, process);
|
||||
|
||||
process->ready = 1;
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
|
||||
nxt_pid_t
|
||||
nxt_process_create(nxt_task_t *task, nxt_process_t *process)
|
||||
{
|
||||
int pipefd[2];
|
||||
nxt_int_t ret;
|
||||
nxt_pid_t pid;
|
||||
nxt_process_t *p;
|
||||
nxt_runtime_t *rt;
|
||||
nxt_process_type_t ptype;
|
||||
nxt_process_init_t *init;
|
||||
|
||||
rt = task->thread->runtime;
|
||||
|
||||
pid = fork();
|
||||
|
||||
switch (pid) {
|
||||
|
||||
case -1:
|
||||
nxt_alert(task, "fork() failed while creating \"%s\" %E",
|
||||
process->init->name, nxt_errno);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* A child. */
|
||||
nxt_pid = getpid();
|
||||
|
||||
/* Clean inherited cached thread tid. */
|
||||
task->thread->tid = 0;
|
||||
|
||||
process->pid = nxt_pid;
|
||||
|
||||
ptype = process->init->type;
|
||||
|
||||
nxt_port_reset_next_id();
|
||||
|
||||
nxt_event_engine_thread_adopt(task->thread->engine);
|
||||
|
||||
/* Remove not ready processes */
|
||||
nxt_runtime_process_each(rt, p) {
|
||||
|
||||
if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
|
||||
nxt_debug(task, "remove not required process %PI", p->pid);
|
||||
|
||||
nxt_process_close_ports(task, p);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!p->ready) {
|
||||
nxt_debug(task, "remove not ready process %PI", p->pid);
|
||||
|
||||
nxt_process_close_ports(task, p);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
nxt_port_mmaps_destroy(&p->incoming, 0);
|
||||
nxt_port_mmaps_destroy(&p->outgoing, 0);
|
||||
|
||||
} nxt_runtime_process_loop;
|
||||
|
||||
nxt_runtime_process_add(task, process);
|
||||
|
||||
nxt_process_start(task, process);
|
||||
|
||||
process->ready = 1;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* A parent. */
|
||||
nxt_debug(task, "fork(\"%s\"): %PI", process->init->name, pid);
|
||||
|
||||
process->pid = pid;
|
||||
|
||||
nxt_runtime_process_add(task, process);
|
||||
|
||||
break;
|
||||
if (nxt_slow_path(pipe(pipefd) == -1)) {
|
||||
nxt_alert(task, "failed to create process pipe for passing rpid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
init = process->init;
|
||||
|
||||
#if (NXT_HAVE_CLONE)
|
||||
pid = nxt_clone(SIGCHLD|init->isolation.clone.flags);
|
||||
#else
|
||||
pid = fork();
|
||||
#endif
|
||||
|
||||
if (nxt_slow_path(pid < 0)) {
|
||||
#if (NXT_HAVE_CLONE)
|
||||
nxt_alert(task, "clone() failed while creating \"%s\" %E",
|
||||
init->name, nxt_errno);
|
||||
#else
|
||||
nxt_alert(task, "fork() failed while creating \"%s\" %E",
|
||||
init->name, nxt_errno);
|
||||
#endif
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* Child. */
|
||||
|
||||
if (nxt_slow_path(close(pipefd[1]) == -1)) {
|
||||
nxt_alert(task, "failed to close writer pipe fd");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
ret = nxt_process_worker_setup(task, process, pipefd[0]);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Explicitly return 0 to notice the caller function this is the child.
|
||||
* The caller must return to the event engine work queue loop.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parent. */
|
||||
|
||||
if (nxt_slow_path(close(pipefd[0]) != 0)) {
|
||||
nxt_alert(task, "failed to close pipe: %E", nxt_errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, the child process is blocked reading the
|
||||
* pipe fd to get its real pid (rpid).
|
||||
*
|
||||
* If anything goes wrong now, we need to terminate the child
|
||||
* process by sending a NXT_ERROR in the pipe.
|
||||
*/
|
||||
|
||||
#if (NXT_HAVE_CLONE)
|
||||
nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid);
|
||||
#else
|
||||
nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid);
|
||||
#endif
|
||||
|
||||
if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) {
|
||||
nxt_alert(task, "failed to write real pid");
|
||||
goto fail_cleanup;
|
||||
}
|
||||
|
||||
#if (NXT_HAVE_CLONE_NEWUSER)
|
||||
if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) {
|
||||
ret = nxt_clone_proc_map(task, pid, &init->isolation.clone);
|
||||
if (nxt_slow_path(ret != NXT_OK)) {
|
||||
goto fail_cleanup;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = NXT_OK;
|
||||
|
||||
if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
|
||||
nxt_alert(task, "failed to write status");
|
||||
goto fail_cleanup;
|
||||
}
|
||||
|
||||
process->pid = pid;
|
||||
|
||||
nxt_runtime_process_add(task, process);
|
||||
|
||||
return pid;
|
||||
|
||||
fail_cleanup:
|
||||
|
||||
ret = NXT_ERROR;
|
||||
|
||||
if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
|
||||
nxt_alert(task, "failed to write status");
|
||||
}
|
||||
|
||||
if (nxt_slow_path(close(pipefd[1]) != 0)) {
|
||||
nxt_alert(task, "failed to close pipe: %E", nxt_errno);
|
||||
}
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,22 +272,17 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process)
|
||||
nxt_process_title(task, "unit: %s", init->name);
|
||||
|
||||
thread = task->thread;
|
||||
rt = thread->runtime;
|
||||
|
||||
nxt_random_init(&thread->random);
|
||||
|
||||
if (init->user_cred != NULL) {
|
||||
/*
|
||||
* Changing user credentials requires either root privileges
|
||||
* or CAP_SETUID and CAP_SETGID capabilities on Linux.
|
||||
*/
|
||||
if (rt->capabilities.setid && init->user_cred != NULL) {
|
||||
ret = nxt_user_cred_set(task, init->user_cred);
|
||||
if (ret != NXT_OK) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
rt = thread->runtime;
|
||||
|
||||
rt->type = init->type;
|
||||
|
||||
engine = thread->engine;
|
||||
@@ -592,15 +726,8 @@ nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
|
||||
uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid);
|
||||
|
||||
if (setgid(uc->base_gid) != 0) {
|
||||
if (nxt_errno == NXT_EPERM) {
|
||||
nxt_log(task, NXT_LOG_NOTICE, "setgid(%d) failed %E, ignored",
|
||||
uc->base_gid, nxt_errno);
|
||||
return NXT_OK;
|
||||
|
||||
} else {
|
||||
nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (uc->gids != NULL) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#ifndef _NXT_PROCESS_H_INCLUDED_
|
||||
#define _NXT_PROCESS_H_INCLUDED_
|
||||
|
||||
#include <nxt_conf.h>
|
||||
|
||||
|
||||
typedef pid_t nxt_pid_t;
|
||||
typedef uid_t nxt_uid_t;
|
||||
@@ -21,26 +23,35 @@ typedef struct {
|
||||
nxt_gid_t *gids;
|
||||
} nxt_user_cred_t;
|
||||
|
||||
typedef struct {
|
||||
nxt_int_t flags;
|
||||
nxt_conf_value_t *uidmap;
|
||||
nxt_conf_value_t *gidmap;
|
||||
} nxt_process_clone_t;
|
||||
|
||||
typedef struct nxt_process_init_s nxt_process_init_t;
|
||||
typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task, void *data);
|
||||
typedef nxt_int_t (*nxt_process_restart_t)(nxt_task_t *task, nxt_runtime_t *rt,
|
||||
nxt_process_init_t *init);
|
||||
|
||||
|
||||
struct nxt_process_init_s {
|
||||
nxt_process_start_t start;
|
||||
const char *name;
|
||||
nxt_user_cred_t *user_cred;
|
||||
nxt_process_start_t start;
|
||||
const char *name;
|
||||
nxt_user_cred_t *user_cred;
|
||||
|
||||
nxt_port_handlers_t *port_handlers;
|
||||
const nxt_sig_event_t *signals;
|
||||
nxt_port_handlers_t *port_handlers;
|
||||
const nxt_sig_event_t *signals;
|
||||
|
||||
nxt_process_type_t type;
|
||||
nxt_process_type_t type;
|
||||
|
||||
void *data;
|
||||
uint32_t stream;
|
||||
void *data;
|
||||
uint32_t stream;
|
||||
|
||||
nxt_process_restart_t restart;
|
||||
nxt_process_restart_t restart;
|
||||
|
||||
union {
|
||||
nxt_process_clone_t clone;
|
||||
} isolation;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -692,14 +692,26 @@ nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt)
|
||||
rt->state = NXT_STATE;
|
||||
rt->control = NXT_CONTROL_SOCK;
|
||||
|
||||
nxt_memzero(&rt->capabilities, sizeof(nxt_capabilities_t));
|
||||
|
||||
if (nxt_runtime_conf_read_cmd(task, rt) != NXT_OK) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nxt_user_cred_get(task, &rt->user_cred, rt->group) != NXT_OK) {
|
||||
if (nxt_capability_set(task, &rt->capabilities) != NXT_OK) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (rt->capabilities.setid) {
|
||||
if (nxt_user_cred_get(task, &rt->user_cred, rt->group) != NXT_OK) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
nxt_log(task, NXT_LOG_WARN, "Unit is running unprivileged, then it "
|
||||
"cannot use arbitrary user and group.");
|
||||
}
|
||||
|
||||
/* An engine's parameters. */
|
||||
|
||||
interface = nxt_service_get(rt->services, "engine", rt->engine);
|
||||
|
||||
@@ -59,6 +59,7 @@ struct nxt_runtime_s {
|
||||
uint32_t engine_connections;
|
||||
uint32_t auxiliary_threads;
|
||||
nxt_user_cred_t user_cred;
|
||||
nxt_capabilities_t capabilities;
|
||||
const char *group;
|
||||
const char *pid;
|
||||
const char *log;
|
||||
|
||||
@@ -333,6 +333,7 @@ nxt_unit_init(nxt_unit_init_t *init)
|
||||
}
|
||||
}
|
||||
|
||||
lib->pid = read_port.id.pid;
|
||||
ctx = &lib->main_ctx.ctx;
|
||||
|
||||
rc = lib->callbacks.add_port(ctx, &ready_port);
|
||||
@@ -398,7 +399,6 @@ nxt_unit_create(nxt_unit_init_t *init)
|
||||
lib->processes.slot = NULL;
|
||||
lib->ports.slot = NULL;
|
||||
|
||||
lib->pid = getpid();
|
||||
lib->log_fd = STDERR_FILENO;
|
||||
lib->online = 1;
|
||||
|
||||
|
||||
79
test/go/ns_inspect/app.go
Normal file
79
test/go/ns_inspect/app.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"nginx/unit"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
NS struct {
|
||||
USER uint64
|
||||
PID uint64
|
||||
IPC uint64
|
||||
CGROUP uint64
|
||||
UTS uint64
|
||||
MNT uint64
|
||||
NET uint64
|
||||
}
|
||||
|
||||
Output struct {
|
||||
PID int
|
||||
UID int
|
||||
GID int
|
||||
NS NS
|
||||
}
|
||||
)
|
||||
|
||||
func abortonerr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// returns: [nstype]:[4026531835]
|
||||
func getns(nstype string) uint64 {
|
||||
str, err := os.Readlink(fmt.Sprintf("/proc/self/ns/%s", nstype))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
str = str[len(nstype)+2:]
|
||||
str = str[:len(str)-1]
|
||||
val, err := strconv.ParseUint(str, 10, 64)
|
||||
abortonerr(err)
|
||||
return val
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
pid := os.Getpid()
|
||||
out := &Output{
|
||||
PID: pid,
|
||||
UID: os.Getuid(),
|
||||
GID: os.Getgid(),
|
||||
NS: NS{
|
||||
PID: getns("pid"),
|
||||
USER: getns("user"),
|
||||
MNT: getns("mnt"),
|
||||
IPC: getns("ipc"),
|
||||
UTS: getns("uts"),
|
||||
NET: getns("net"),
|
||||
CGROUP: getns("cgroup"),
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
unit.ListenAndServe(":7080", nil)
|
||||
}
|
||||
135
test/test_go_isolation.py
Normal file
135
test/test_go_isolation.py
Normal file
@@ -0,0 +1,135 @@
|
||||
import os
|
||||
import json
|
||||
import unittest
|
||||
from unit.applications.lang.go import TestApplicationGo
|
||||
from unit.feature.isolation import TestFeatureIsolation
|
||||
|
||||
|
||||
class TestGoIsolation(TestApplicationGo):
|
||||
prerequisites = {'modules': ['go'], 'features': ['isolation']}
|
||||
|
||||
isolation = TestFeatureIsolation()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, complete_check=True):
|
||||
unit = super().setUpClass(complete_check=False)
|
||||
|
||||
TestFeatureIsolation().check(cls.available, unit.testdir)
|
||||
|
||||
return unit if not complete_check else unit.complete()
|
||||
|
||||
def isolation_key(self, key):
|
||||
return key in self.available['features']['isolation'].keys()
|
||||
|
||||
def conf_isolation(self, isolation):
|
||||
self.assertIn(
|
||||
'success',
|
||||
self.conf(isolation, 'applications/ns_inspect/isolation'),
|
||||
'configure isolation',
|
||||
)
|
||||
|
||||
def test_isolation_values(self):
|
||||
self.load('ns_inspect')
|
||||
|
||||
obj = self.isolation.parsejson(self.get()['body'])
|
||||
|
||||
for ns, ns_value in self.available['features']['isolation'].items():
|
||||
if ns.upper() in obj['NS']:
|
||||
self.assertEqual(
|
||||
obj['NS'][ns.upper()], ns_value, '%s match' % ns
|
||||
)
|
||||
|
||||
def test_isolation_user(self):
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
print('unprivileged clone is not available')
|
||||
raise unittest.SkipTest()
|
||||
|
||||
self.load('ns_inspect')
|
||||
obj = self.isolation.parsejson(self.get()['body'])
|
||||
|
||||
self.assertTrue(obj['UID'] != 0, 'uid not zero')
|
||||
self.assertTrue(obj['GID'] != 0, 'gid not zero')
|
||||
self.assertEqual(obj['UID'], os.getuid(), 'uid match')
|
||||
self.assertEqual(obj['GID'], os.getgid(), 'gid match')
|
||||
|
||||
self.conf_isolation({"namespaces": {"credential": True}})
|
||||
|
||||
obj = self.isolation.parsejson(self.get()['body'])
|
||||
|
||||
# default uid and gid maps current user to nobody
|
||||
self.assertEqual(obj['UID'], 65534, 'uid nobody')
|
||||
self.assertEqual(obj['GID'], 65534, 'gid nobody')
|
||||
|
||||
self.conf_isolation(
|
||||
{
|
||||
"namespaces": {"credential": True},
|
||||
"uidmap": [
|
||||
{"container": 1000, "host": os.geteuid(), "size": 1}
|
||||
],
|
||||
"gidmap": [
|
||||
{"container": 1000, "host": os.getegid(), "size": 1}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
obj = self.isolation.parsejson(self.get()['body'])
|
||||
|
||||
# default uid and gid maps current user to root
|
||||
self.assertEqual(obj['UID'], 1000, 'uid root')
|
||||
self.assertEqual(obj['GID'], 1000, 'gid root')
|
||||
|
||||
def test_isolation_mnt(self):
|
||||
if not self.isolation_key('mnt'):
|
||||
print('mnt namespace is not supported')
|
||||
raise unittest.SkipTest()
|
||||
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
print('unprivileged clone is not available')
|
||||
raise unittest.SkipTest()
|
||||
|
||||
self.load('ns_inspect')
|
||||
self.conf_isolation(
|
||||
{"namespaces": {"mount": True, "credential": True}}
|
||||
)
|
||||
|
||||
obj = self.isolation.parsejson(self.get()['body'])
|
||||
|
||||
# all but user and mnt
|
||||
allns = list(self.available['features']['isolation'].keys())
|
||||
allns.remove('user')
|
||||
allns.remove('mnt')
|
||||
|
||||
for ns in allns:
|
||||
if ns.upper() in obj['NS']:
|
||||
self.assertEqual(
|
||||
obj['NS'][ns.upper()],
|
||||
self.available['features']['isolation'][ns],
|
||||
'%s match' % ns,
|
||||
)
|
||||
|
||||
self.assertNotEqual(
|
||||
obj['NS']['MNT'], self.isolation.getns('mnt'), 'mnt set'
|
||||
)
|
||||
self.assertNotEqual(
|
||||
obj['NS']['USER'], self.isolation.getns('user'), 'user set'
|
||||
)
|
||||
|
||||
def test_isolation_pid(self):
|
||||
if not self.isolation_key('pid'):
|
||||
print('pid namespace is not supported')
|
||||
raise unittest.SkipTest()
|
||||
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
print('unprivileged clone is not available')
|
||||
raise unittest.SkipTest()
|
||||
|
||||
self.load('ns_inspect')
|
||||
self.conf_isolation({"namespaces": {"pid": True, "credential": True}})
|
||||
|
||||
obj = self.isolation.parsejson(self.get()['body'])
|
||||
|
||||
self.assertEqual(obj['PID'], 1, 'pid of container is 1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
TestGoIsolation.main()
|
||||
87
test/unit/feature/isolation.py
Normal file
87
test/unit/feature/isolation.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import os
|
||||
import json
|
||||
from unit.applications.proto import TestApplicationProto
|
||||
from unit.applications.lang.go import TestApplicationGo
|
||||
from unit.applications.lang.java import TestApplicationJava
|
||||
from unit.applications.lang.node import TestApplicationNode
|
||||
from unit.applications.lang.perl import TestApplicationPerl
|
||||
from unit.applications.lang.php import TestApplicationPHP
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.applications.lang.ruby import TestApplicationRuby
|
||||
|
||||
|
||||
class TestFeatureIsolation(TestApplicationProto):
|
||||
allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net']
|
||||
|
||||
def check(self, available, testdir):
|
||||
test_conf = {"namespaces": {"credential": True}}
|
||||
|
||||
module = ''
|
||||
app = 'empty'
|
||||
if 'go' in available['modules']:
|
||||
module = TestApplicationGo()
|
||||
|
||||
elif 'java' in available['modules']:
|
||||
module = TestApplicationJava()
|
||||
|
||||
elif 'node' in available['modules']:
|
||||
module = TestApplicationNode()
|
||||
app = 'basic'
|
||||
|
||||
elif 'perl' in available['modules']:
|
||||
module = TestApplicationPerl()
|
||||
app = 'body_empty'
|
||||
|
||||
elif 'php' in available['modules']:
|
||||
module = TestApplicationPHP()
|
||||
app = 'phpinfo'
|
||||
|
||||
elif 'python' in available['modules']:
|
||||
module = TestApplicationPython()
|
||||
|
||||
elif 'ruby' in available['modules']:
|
||||
module = TestApplicationRuby()
|
||||
|
||||
if not module:
|
||||
return
|
||||
|
||||
module.testdir = testdir
|
||||
module.load(app)
|
||||
|
||||
resp = module.conf(test_conf, 'applications/' + app + '/isolation')
|
||||
if 'success' not in resp:
|
||||
return
|
||||
|
||||
userns = self.getns('user')
|
||||
if not userns:
|
||||
return
|
||||
|
||||
available['features']['isolation'] = {'user': userns}
|
||||
|
||||
unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone'
|
||||
if os.path.exists(unp_clone_path):
|
||||
with open(unp_clone_path, 'r') as f:
|
||||
if str(f.read()).rstrip() == '1':
|
||||
available['features']['isolation'][
|
||||
'unprivileged_userns_clone'
|
||||
] = True
|
||||
|
||||
for ns in self.allns:
|
||||
ns_value = self.getns(ns)
|
||||
if ns_value:
|
||||
available['features']['isolation'][ns] = ns_value
|
||||
|
||||
def getns(self, nstype):
|
||||
# read namespace id from symlink file:
|
||||
# it points to: '<nstype>:[<ns id>]'
|
||||
# # eg.: 'pid:[4026531836]'
|
||||
nspath = '/proc/self/ns/' + nstype
|
||||
data = None
|
||||
|
||||
if os.path.exists(nspath):
|
||||
data = int(os.readlink(nspath)[len(nstype) + 2 : -1])
|
||||
|
||||
return data
|
||||
|
||||
def parsejson(self, data):
|
||||
return json.loads(data.split('\n')[1])
|
||||
Reference in New Issue
Block a user