summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_process.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_process.c')
-rw-r--r--src/nxt_process.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/src/nxt_process.c b/src/nxt_process.c
index e84549b3..c4c44d14 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -13,6 +13,14 @@
#include <signal.h>
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+#include <sys/prctl.h>
+#endif
+
+#if (NXT_HAVE_PIVOT_ROOT)
+#include <mntent.h>
+#endif
+
static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process);
static nxt_int_t nxt_process_child_fixup(nxt_task_t *task,
nxt_process_t *process);
@@ -25,6 +33,19 @@ static void nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg,
static void nxt_process_created_error(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+static nxt_int_t nxt_process_chroot(nxt_task_t *task, const char *path);
+#endif
+
+#if (NXT_HAVE_PIVOT_ROOT)
+static nxt_int_t nxt_process_pivot_root(nxt_task_t *task, const char *rootfs);
+static nxt_int_t nxt_process_private_mount(nxt_task_t *task,
+ const char *rootfs);
+#endif
+
+#if (NXT_HAVE_PIVOT_ROOT)
+static int nxt_pivot_root(const char *new_root, const char *old_root);
+#endif
/* A cached process pid. */
nxt_pid_t nxt_pid;
@@ -495,10 +516,347 @@ nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process)
}
}
+#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
+ if (nxt_slow_path(process->isolation.new_privs == 0
+ && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0))
+ {
+ nxt_alert(task, "failed to set no_new_privs %E", nxt_errno);
+ return NXT_ERROR;
+ }
+#endif
+
+ return NXT_OK;
+}
+
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+
+#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS)
+
+
+nxt_int_t
+nxt_process_change_root(nxt_task_t *task, nxt_process_t *process)
+{
+ char *rootfs;
+ nxt_int_t ret;
+
+ rootfs = (char *) process->isolation.rootfs;
+
+ nxt_debug(task, "change root: %s", rootfs);
+
+ if (NXT_CLONE_MNT(process->isolation.clone.flags)) {
+ ret = nxt_process_pivot_root(task, rootfs);
+ } else {
+ ret = nxt_process_chroot(task, rootfs);
+ }
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ if (nxt_slow_path(chdir("/") < 0)) {
+ nxt_alert(task, "chdir(\"/\") %E", nxt_errno);
+ return NXT_ERROR;
+ }
+ }
+
+ return ret;
+}
+
+
+#else
+
+
+nxt_int_t
+nxt_process_change_root(nxt_task_t *task, nxt_process_t *process)
+{
+ char *rootfs;
+
+ rootfs = (char *) process->isolation.rootfs;
+
+ nxt_debug(task, "change root: %s", rootfs);
+
+ if (nxt_fast_path(nxt_process_chroot(task, rootfs) == NXT_OK)) {
+ if (nxt_slow_path(chdir("/") < 0)) {
+ nxt_alert(task, "chdir(\"/\") %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+#endif
+
+
+#endif
+
+
+#if (NXT_HAVE_ISOLATION_ROOTFS)
+
+static nxt_int_t
+nxt_process_chroot(nxt_task_t *task, const char *path)
+{
+ if (nxt_slow_path(chroot(path) < 0)) {
+ nxt_alert(task, "chroot(%s) %E", path, nxt_errno);
+ return NXT_ERROR;
+ }
+
return NXT_OK;
}
+void
+nxt_process_unmount_all(nxt_task_t *task, nxt_process_t *process)
+{
+ size_t i, n;
+ nxt_array_t *mounts;
+ nxt_fs_mount_t *mnt;
+
+ nxt_debug(task, "unmount all (%s)", process->name);
+
+ mounts = process->isolation.mounts;
+ n = mounts->nelts;
+ mnt = mounts->elts;
+
+ for (i = 0; i < n; i++) {
+ nxt_fs_unmount(mnt[i].dst);
+ }
+}
+
+#endif
+
+
+#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS)
+
+/*
+ * pivot_root(2) can only be safely used with containers, otherwise it can
+ * umount(2) the global root filesystem and screw up the machine.
+ */
+
+static nxt_int_t
+nxt_process_pivot_root(nxt_task_t *task, const char *path)
+{
+ /*
+ * This implementation makes use of a kernel trick that works for ages
+ * and now documented in Linux kernel 5.
+ * https://lore.kernel.org/linux-man/87r24piwhm.fsf@x220.int.ebiederm.org/T/
+ */
+
+ if (nxt_slow_path(mount("", "/", "", MS_SLAVE|MS_REC, "") != 0)) {
+ nxt_alert(task, "failed to make / a slave mount %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_process_private_mount(task, path) != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(mount(path, path, "bind", MS_BIND|MS_REC, "") != 0)) {
+ nxt_alert(task, "error bind mounting rootfs %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(chdir(path) != 0)) {
+ nxt_alert(task, "failed to chdir(%s) %E", path, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_pivot_root(".", ".") != 0)) {
+ nxt_alert(task, "failed to pivot_root %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ /*
+ * Make oldroot a slave mount to avoid unmounts getting propagated to the
+ * host.
+ */
+ if (nxt_slow_path(mount("", ".", "", MS_SLAVE | MS_REC, NULL) != 0)) {
+ nxt_alert(task, "failed to bind mount rootfs %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(umount2(".", MNT_DETACH) != 0)) {
+ nxt_alert(task, "failed to umount old root directory %E", nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+nxt_process_private_mount(nxt_task_t *task, const char *rootfs)
+{
+ char *parent_mnt;
+ FILE *procfile;
+ u_char **mounts;
+ size_t len;
+ uint8_t *shared;
+ nxt_int_t ret, index, nmounts;
+ struct mntent *ent;
+
+ static const char *mount_path = "/proc/self/mounts";
+
+ ret = NXT_ERROR;
+ ent = NULL;
+ shared = NULL;
+ procfile = NULL;
+ parent_mnt = NULL;
+
+ nmounts = 256;
+
+ mounts = nxt_malloc(nmounts * sizeof(uintptr_t));
+ if (nxt_slow_path(mounts == NULL)) {
+ goto fail;
+ }
+
+ shared = nxt_malloc(nmounts);
+ if (nxt_slow_path(shared == NULL)) {
+ goto fail;
+ }
+
+ procfile = setmntent(mount_path, "r");
+ if (nxt_slow_path(procfile == NULL)) {
+ nxt_alert(task, "failed to open %s %E", mount_path, nxt_errno);
+
+ goto fail;
+ }
+
+ index = 0;
+
+again:
+
+ for ( ; index < nmounts; index++) {
+ ent = getmntent(procfile);
+ if (ent == NULL) {
+ nmounts = index;
+ break;
+ }
+
+ mounts[index] = (u_char *) strdup(ent->mnt_dir);
+ shared[index] = hasmntopt(ent, "shared") != NULL;
+ }
+
+ if (ent != NULL) {
+ /* there are still entries to be read */
+
+ nmounts *= 2;
+ mounts = nxt_realloc(mounts, nmounts);
+ if (nxt_slow_path(mounts == NULL)) {
+ goto fail;
+ }
+
+ shared = nxt_realloc(shared, nmounts);
+ if (nxt_slow_path(shared == NULL)) {
+ goto fail;
+ }
+
+ goto again;
+ }
+
+ for (index = 0; index < nmounts; index++) {
+ if (nxt_strcmp(mounts[index], rootfs) == 0) {
+ parent_mnt = (char *) rootfs;
+ break;
+ }
+ }
+
+ if (parent_mnt == NULL) {
+ len = nxt_strlen(rootfs);
+
+ parent_mnt = nxt_malloc(len + 1);
+ if (parent_mnt == NULL) {
+ goto fail;
+ }
+
+ nxt_memcpy(parent_mnt, rootfs, len);
+ parent_mnt[len] = '\0';
+
+ if (parent_mnt[len - 1] == '/') {
+ parent_mnt[len - 1] = '\0';
+ len--;
+ }
+
+ for ( ;; ) {
+ for (index = 0; index < nmounts; index++) {
+ if (nxt_strcmp(mounts[index], parent_mnt) == 0) {
+ goto found;
+ }
+ }
+
+ if (len == 1 && parent_mnt[0] == '/') {
+ nxt_alert(task, "parent mount not found");
+ goto fail;
+ }
+
+ /* parent dir */
+ while (parent_mnt[len - 1] != '/' && len > 0) {
+ len--;
+ }
+
+ if (nxt_slow_path(len == 0)) {
+ nxt_alert(task, "parent mount not found");
+ goto fail;
+ }
+
+ if (len == 1) {
+ parent_mnt[len] = '\0'; /* / */
+ } else {
+ parent_mnt[len - 1] = '\0'; /* /<path> */
+ }
+ }
+ }
+
+found:
+
+ if (shared[index]) {
+ if (nxt_slow_path(mount("", parent_mnt, "", MS_PRIVATE, "") != 0)) {
+ nxt_alert(task, "mount(\"\", \"%s\", MS_PRIVATE) %E", parent_mnt,
+ nxt_errno);
+
+ goto fail;
+ }
+ }
+
+ ret = NXT_OK;
+
+fail:
+
+ if (procfile != NULL) {
+ endmntent(procfile);
+ }
+
+ if (mounts != NULL) {
+ for (index = 0; index < nmounts; index++) {
+ nxt_free(mounts[index]);
+ }
+
+ nxt_free(mounts);
+ }
+
+ if (shared != NULL) {
+ nxt_free(shared);
+ }
+
+ if (parent_mnt != NULL && parent_mnt != rootfs) {
+ nxt_free(parent_mnt);
+ }
+
+ return ret;
+}
+
+
+static int
+nxt_pivot_root(const char *new_root, const char *old_root)
+{
+ return syscall(__NR_pivot_root, new_root, old_root);
+}
+
+#endif
+
+
static nxt_int_t
nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process)
{