diff options
author | Tiago Natel de Moura <t.nateldemoura@f5.com> | 2020-05-28 14:57:41 +0100 |
---|---|---|
committer | Tiago Natel de Moura <t.nateldemoura@f5.com> | 2020-05-28 14:57:41 +0100 |
commit | e2b53e16c60ba1e3bbbe59172c184e97f889326b (patch) | |
tree | f2bda4ea966657fc52189d154e1d6afcb767973f /src/nxt_application.c | |
parent | e9e5ddd5a5d9ce99768833137eac2551a710becf (diff) | |
download | unit-e2b53e16c60ba1e3bbbe59172c184e97f889326b.tar.gz unit-e2b53e16c60ba1e3bbbe59172c184e97f889326b.tar.bz2 |
Added "rootfs" feature.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_application.c | 481 |
1 files changed, 443 insertions, 38 deletions
diff --git a/src/nxt_application.c b/src/nxt_application.c index 6de82257..566bf256 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -17,11 +17,16 @@ #include <glob.h> +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) +#include <sys/prctl.h> +#endif + typedef struct { nxt_app_type_t type; nxt_str_t version; nxt_str_t file; + nxt_array_t *mounts; } nxt_module_t; @@ -40,22 +45,40 @@ static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp); static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); -static nxt_int_t nxt_app_isolation(nxt_task_t *task, +static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src); + +#if (NXT_HAVE_ISOLATION_ROOTFS) +static nxt_int_t nxt_app_prepare_rootfs(nxt_task_t *task, + nxt_process_t *process); +static nxt_int_t nxt_app_prepare_lang_mounts(nxt_task_t *task, + nxt_process_t *process, nxt_array_t *syspaths); +static nxt_int_t nxt_app_set_isolation_rootfs(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); +#endif + +static nxt_int_t nxt_app_set_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process); #if (NXT_HAVE_CLONE) +static nxt_int_t nxt_app_set_isolation_namespaces(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); static nxt_int_t nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, nxt_clone_t *clone); #endif #if (NXT_HAVE_CLONE_NEWUSER) -static nxt_int_t nxt_app_isolation_creds(nxt_task_t *task, +static nxt_int_t nxt_app_set_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process); static nxt_int_t nxt_app_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mem_pool, nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map); #endif +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) +static nxt_int_t nxt_app_set_isolation_new_privs(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); +#endif + nxt_str_t nxt_server = nxt_string(NXT_SERVER); @@ -154,16 +177,17 @@ nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data) static nxt_buf_t * nxt_discovery_modules(nxt_task_t *task, const char *path) { - char *name; - u_char *p, *end; - size_t size; - glob_t glb; - nxt_mp_t *mp; - nxt_buf_t *b; - nxt_int_t ret; - nxt_uint_t i, n; - nxt_array_t *modules; - nxt_module_t *module; + char *name; + u_char *p, *end; + size_t size; + glob_t glb; + nxt_mp_t *mp; + nxt_buf_t *b; + nxt_int_t ret; + nxt_uint_t i, n, j; + nxt_array_t *modules, *mounts; + nxt_module_t *module; + nxt_fs_mount_t *mnt; b = NULL; @@ -206,11 +230,26 @@ nxt_discovery_modules(nxt_task_t *task, const char *path) size += nxt_length("{\"type\": ,"); size += nxt_length(" \"version\": \"\","); - size += nxt_length(" \"file\": \"\"},"); + size += nxt_length(" \"file\": \"\","); + size += nxt_length(" \"mounts\": []},"); size += NXT_INT_T_LEN + module[i].version.length + module[i].file.length; + + mounts = module[i].mounts; + + size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", " + "\"fstype\": \"\", \"flags\": , " + "\"data\": \"\"},"); + + mnt = mounts->elts; + + for (j = 0; j < mounts->nelts; j++) { + size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst) + + nxt_strlen(mnt[j].fstype) + NXT_INT_T_LEN + + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data)); + } } b = nxt_buf_mem_alloc(mp, size, 0); @@ -225,12 +264,34 @@ nxt_discovery_modules(nxt_task_t *task, const char *path) *p++ = '['; for (i = 0; i < n; i++) { - p = nxt_sprintf(p, end, - "{\"type\": %d, \"version\": \"%V\", \"file\": \"%V\"},", - module[i].type, &module[i].version, &module[i].file); + mounts = module[i].mounts; + + p = nxt_sprintf(p, end, "{\"type\": %d, \"version\": \"%V\", " + "\"file\": \"%V\", \"mounts\": [", + module[i].type, &module[i].version, &module[i].file); + + mnt = mounts->elts; + for (j = 0; j < mounts->nelts; j++) { + p = nxt_sprintf(p, end, + "{\"src\": \"%s\", \"dst\": \"%s\", " + "\"fstype\": \"%s\", \"flags\": %d, " + "\"data\": \"%s\"},", + mnt[j].src, mnt[j].dst, mnt[j].fstype, mnt[j].flags, + mnt[j].data == NULL ? (u_char *) "" : mnt[j].data); + } + + *p++ = ']'; + *p++ = '}'; + *p++ = ','; } *p++ = ']'; + + if (nxt_slow_path(p >= end)) { + nxt_alert(task, "discovery write past the buffer"); + goto fail; + } + b->mem.free = p; fail: @@ -245,13 +306,16 @@ static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, const char *name) { - void *dl; - nxt_str_t version; - nxt_int_t ret; - nxt_uint_t i, n; - nxt_module_t *module; - nxt_app_type_t type; - nxt_app_module_t *app; + void *dl; + nxt_str_t version; + nxt_int_t ret; + nxt_uint_t i, j, n; + nxt_array_t *mounts; + nxt_module_t *module; + nxt_app_type_t type; + nxt_fs_mount_t *to; + nxt_app_module_t *app; + const nxt_fs_mount_t *from; /* * Only memory allocation failure should return NXT_ERROR. @@ -328,6 +392,47 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, nxt_memcpy(module->file.start, name, module->file.length); + module->mounts = nxt_array_create(mp, app->nmounts, + sizeof(nxt_fs_mount_t)); + + if (nxt_slow_path(module->mounts == NULL)) { + goto fail; + } + + mounts = module->mounts; + + for (j = 0; j < app->nmounts; j++) { + from = &app->mounts[j]; + to = nxt_array_zero_add(mounts); + if (nxt_slow_path(to == NULL)) { + goto fail; + } + + to->src = nxt_cstr_dup(mp, to->src, from->src); + if (nxt_slow_path(to->src == NULL)) { + goto fail; + } + + to->dst = nxt_cstr_dup(mp, to->dst, from->dst); + if (nxt_slow_path(to->dst == NULL)) { + goto fail; + } + + to->fstype = nxt_cstr_dup(mp, to->fstype, from->fstype); + if (nxt_slow_path(to->fstype == NULL)) { + goto fail; + } + + if (from->data != NULL) { + to->data = nxt_cstr_dup(mp, to->data, from->data); + if (nxt_slow_path(to->data == NULL)) { + goto fail; + } + } + + to->flags = from->flags; + } + } else { nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror()); } @@ -369,17 +474,23 @@ nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) { - nxt_int_t cap_setid; + nxt_int_t cap_setid, cap_chroot; nxt_int_t ret; nxt_runtime_t *rt; nxt_common_app_conf_t *app_conf; + nxt_app_lang_module_t *lang; rt = task->thread->runtime; app_conf = process->data.app; cap_setid = rt->capabilities.setid; + cap_chroot = rt->capabilities.chroot; + + lang = nxt_app_lang_module(rt, &app_conf->type); + + nxt_assert(lang != NULL); if (app_conf->isolation != NULL) { - ret = nxt_app_isolation(task, app_conf->isolation, process); + ret = nxt_app_set_isolation(task, app_conf->isolation, process); if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -388,6 +499,25 @@ nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) #if (NXT_HAVE_CLONE_NEWUSER) if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { cap_setid = 1; + cap_chroot = 1; + } +#endif + +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (process->isolation.rootfs != NULL) { + if (!cap_chroot) { + nxt_log(task, NXT_LOG_ERR, + "The \"rootfs\" field requires privileges"); + + return NXT_ERROR; + } + + if (lang->mounts != NULL && lang->mounts->nelts > 0) { + ret = nxt_app_prepare_lang_mounts(task, process, lang->mounts); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } } #endif @@ -460,6 +590,13 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } + if (nxt_slow_path(nxt_app_set_environment(app_conf->environment) + != NXT_OK)) + { + nxt_alert(task, "failed to set environment"); + return NXT_ERROR; + } + if (nxt_app->setup != NULL) { ret = nxt_app->setup(task, process, app_conf); @@ -468,6 +605,22 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (process->isolation.rootfs != NULL) { + if (process->isolation.mounts != NULL) { + ret = nxt_app_prepare_rootfs(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + } + + ret = nxt_process_change_root(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } +#endif + if (app_conf->working_directory != NULL && app_conf->working_directory[0] != 0) { @@ -481,13 +634,6 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } - if (nxt_slow_path(nxt_app_set_environment(app_conf->environment) - != NXT_OK)) - { - nxt_alert(task, "failed to set environment"); - return NXT_ERROR; - } - init = nxt_process_init(process); init->start = nxt_app->start; @@ -555,10 +701,51 @@ nxt_app_set_environment(nxt_conf_value_t *environment) static nxt_int_t -nxt_app_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, +nxt_app_set_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process) { #if (NXT_HAVE_CLONE) + if (nxt_slow_path(nxt_app_set_isolation_namespaces(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + +#if (NXT_HAVE_CLONE_NEWUSER) + if (nxt_slow_path(nxt_app_set_isolation_creds(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (nxt_slow_path(nxt_app_set_isolation_rootfs(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + if (nxt_slow_path(nxt_app_set_isolation_new_privs(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + + return NXT_OK; +} + + +#if (NXT_HAVE_CLONE) + +static nxt_int_t +nxt_app_set_isolation_namespaces(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ nxt_int_t ret; nxt_conf_value_t *obj; @@ -571,23 +758,82 @@ nxt_app_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, return NXT_ERROR; } } + + return NXT_OK; +} + #endif -#if (NXT_HAVE_CLONE_NEWUSER) - ret = nxt_app_isolation_creds(task, isolation, process); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; + +#if (NXT_HAVE_ISOLATION_ROOTFS) + +static nxt_int_t +nxt_app_set_isolation_rootfs(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ + nxt_str_t str; + nxt_conf_value_t *obj; + + static nxt_str_t rootfs_name = nxt_string("rootfs"); + + obj = nxt_conf_get_object_member(isolation, &rootfs_name, NULL); + if (obj != NULL) { + nxt_conf_get_string(obj, &str); + + if (nxt_slow_path(str.length <= 1 || str.start[0] != '/')) { + nxt_log(task, NXT_LOG_ERR, "rootfs requires an absolute path other " + "than \"/\" but given \"%V\"", &str); + + return NXT_ERROR; + } + + if (str.start[str.length - 1] == '/') { + str.length--; + } + + process->isolation.rootfs = nxt_mp_alloc(process->mem_pool, + str.length + 1); + + if (nxt_slow_path(process->isolation.rootfs == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(process->isolation.rootfs, str.start, str.length); + + process->isolation.rootfs[str.length] = '\0'; } + + return NXT_OK; +} + #endif + +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + +static nxt_int_t +nxt_app_set_isolation_new_privs(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ + nxt_conf_value_t *obj; + + static nxt_str_t new_privs_name = nxt_string("new_privs"); + + obj = nxt_conf_get_object_member(isolation, &new_privs_name, NULL); + if (obj != NULL) { + process->isolation.new_privs = nxt_conf_get_boolean(obj); + } + return NXT_OK; } +#endif + #if (NXT_HAVE_CLONE_NEWUSER) static nxt_int_t -nxt_app_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, +nxt_app_set_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process) { nxt_int_t ret; @@ -753,6 +999,165 @@ nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, #endif +#if (NXT_HAVE_ISOLATION_ROOTFS) + +static nxt_int_t +nxt_app_prepare_lang_mounts(nxt_task_t *task, nxt_process_t *process, + nxt_array_t *lang_mounts) +{ + u_char *p; + size_t i, n, rootfs_len, len; + nxt_mp_t *mp; + nxt_array_t *mounts; + const u_char *rootfs; + nxt_fs_mount_t *mnt, *lang_mnt; + + rootfs = process->isolation.rootfs; + rootfs_len = nxt_strlen(rootfs); + mp = process->mem_pool; + + /* copy to init mem pool */ + mounts = nxt_array_copy(mp, NULL, lang_mounts); + if (mounts == NULL) { + return NXT_ERROR; + } + + n = mounts->nelts; + mnt = mounts->elts; + lang_mnt = lang_mounts->elts; + + for (i = 0; i < n; i++) { + len = nxt_strlen(lang_mnt[i].dst); + + mnt[i].dst = nxt_mp_alloc(mp, rootfs_len + len + 1); + if (mnt[i].dst == NULL) { + return NXT_ERROR; + } + + p = nxt_cpymem(mnt[i].dst, rootfs, rootfs_len); + p = nxt_cpymem(p, lang_mnt[i].dst, len); + *p = '\0'; + } + + process->isolation.mounts = mounts; + + return NXT_OK; +} + + + +static nxt_int_t +nxt_app_prepare_rootfs(nxt_task_t *task, nxt_process_t *process) +{ + size_t i, n; + nxt_int_t ret, hasproc; + struct stat st; + nxt_array_t *mounts; + const u_char *dst; + nxt_fs_mount_t *mnt; + + hasproc = 0; + +#if (NXT_HAVE_CLONE_NEWPID) && (NXT_HAVE_CLONE_NEWNS) + nxt_fs_mount_t mount; + + if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID) + && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS)) + { + /* + * This mount point will automatically be gone when the namespace is + * destroyed. + */ + + mount.fstype = (u_char *) "proc"; + mount.src = (u_char *) "proc"; + mount.dst = (u_char *) "/proc"; + mount.data = (u_char *) ""; + mount.flags = 0; + + ret = nxt_fs_mkdir_all(mount.dst, S_IRWXU | S_IRWXG | S_IRWXO); + if (nxt_fast_path(ret == NXT_OK)) { + ret = nxt_fs_mount(task, &mount); + if (nxt_fast_path(ret == NXT_OK)) { + hasproc = 1; + } + + } else { + nxt_log(task, NXT_LOG_WARN, "mkdir(%s) %E", mount.dst, nxt_errno); + } + } +#endif + + mounts = process->isolation.mounts; + + n = mounts->nelts; + mnt = mounts->elts; + + for (i = 0; i < n; i++) { + dst = mnt[i].dst; + + if (nxt_slow_path(nxt_memcmp(mnt[i].fstype, "bind", 4) == 0 + && stat((const char *) mnt[i].src, &st) != 0)) + { + nxt_log(task, NXT_LOG_WARN, "host path not found: %s", mnt[i].src); + continue; + } + + if (hasproc && nxt_memcmp(mnt[i].fstype, "proc", 4) == 0 + && nxt_memcmp(mnt[i].dst, "/proc", 5) == 0) + { + continue; + } + + ret = nxt_fs_mkdir_all(dst, S_IRWXU | S_IRWXG | S_IRWXO); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "mkdir(%s) %E", dst, nxt_errno); + goto undo; + } + + ret = nxt_fs_mount(task, &mnt[i]); + if (nxt_slow_path(ret != NXT_OK)) { + goto undo; + } + } + + return NXT_OK; + +undo: + + n = i + 1; + + for (i = 0; i < n; i++) { + nxt_fs_unmount(mnt[i].dst); + } + + return NXT_ERROR; +} + +#endif + + +static u_char * +nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src) +{ + u_char *p; + size_t len; + + len = nxt_strlen(src); + + if (dst == NULL) { + dst = nxt_mp_alloc(mp, len + 1); + if (nxt_slow_path(dst == NULL)) { + return NULL; + } + } + + p = nxt_cpymem(dst, src, len); + *p = '\0'; + + return dst; +} + nxt_app_lang_module_t * nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) |