From patchwork Fri Mar 20 18:19:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Golle X-Patchwork-Id: 1259147 X-Patchwork-Delegate: daniel@makrotopia.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20170209 header.b=nDZUkF4L; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48kXBZ2wbBz9sPF for ; Sat, 21 Mar 2020 05:20:22 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Subject:MIME-Version:Message-ID:To:From :Date:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=O47gQv3b/emI1El0T9+MIkGVADNl054IyjXVvciUkDo=; b=nDZUkF4L9pUISJ YF83osDr4hcF8Cp5GGIw/vRRl1dfT/1qOUDU+M4HwgJO3KfQyXPQjkQebvjlQMao6RyqauI0WtpN/ BIB1xV6es5YpnnTp0WkfDMn2wtMyLz/umjvGZI/KgIYOpAUKcYzAAIXqCv5AOO6dd0Hc1I+neo9HJ 9H5EaOzVZC0e2VGYC24lJ29ZEys8aYG8MDL6ydyCEVsH7v3x3Y5NwTXz6Mi66qaZXQQJV4leRqNmo GjVjxYz+T0/YxUy/5lgY60c1zO8PLEwS1eBON5kmIH2kDlHf/I+sRQGroPTE/BIcv42c8KOhHA8mI fJx+nzUwhPqcSSC2Pyrw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jFMFm-0004YJ-FT; Fri, 20 Mar 2020 18:20:18 +0000 Received: from fudo.makrotopia.org ([2a07:2ec0:3002::71]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jFMFj-0004XD-Cp for openwrt-devel@lists.openwrt.org; Fri, 20 Mar 2020 18:20:17 +0000 Received: from local by fudo.makrotopia.org with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92.2) (envelope-from ) id 1jFMFf-0000B3-Oe; Fri, 20 Mar 2020 19:20:12 +0100 Date: Fri, 20 Mar 2020 18:19:53 +0000 From: Daniel Golle To: openwrt-devel@lists.openwrt.org Message-ID: <20200320181953.GA582683@makrotopia.org> MIME-Version: 1.0 Content-Disposition: inline X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200320_112015_437386_786D9BD4 X-CRM114-Status: GOOD ( 13.22 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record Subject: [OpenWrt-Devel] [PATCH/RFC procd 1/3] jail: add support for launching extroot containers X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: john@phrozen.org Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org Add option to ujail to use an existing rootfs when launching a containerized service. Later on this option will also be used to launch full-system containers. Signed-off-by: Daniel Golle --- jail/jail.c | 45 ++++++++++++++++++++++++++++++++++----------- service/instance.c | 15 +++++++++++++++ service/instance.h | 1 + 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/jail/jail.c b/jail/jail.c index 5414519..df1bda7 100644 --- a/jail/jail.c +++ b/jail/jail.c @@ -40,7 +40,7 @@ #include #define STACK_SIZE (1024 * 1024) -#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:N" +#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:" #define NAMESPACE_MOUNT (1U << 0) #define NAMESPACE_IPC (1U << 1) @@ -58,6 +58,7 @@ static struct { char *capabilities; char *user; char *group; + char *extroot; int no_new_privs; int namespace; int procfs; @@ -164,9 +165,16 @@ static int build_jail_fs(void) return -1; } - if (mount("tmpfs", jail_root, "tmpfs", MS_NOATIME, "mode=0755")) { - ERROR("tmpfs mount failed %m\n"); - return -1; + if (opts.extroot) { + if (mount(opts.extroot, jail_root, NULL, MS_BIND | MS_REC, NULL)) { + ERROR("extroot mount failed %m\n"); + return -1; + } + } else { + if (mount("tmpfs", jail_root, "tmpfs", MS_NOATIME, "mode=0755")) { + ERROR("tmpfs mount failed %m\n"); + return -1; + } } if (chdir(jail_root)) { @@ -230,6 +238,7 @@ static char** build_envp(const char *seccomp) static char preload_var[PATH_MAX]; static char seccomp_var[PATH_MAX]; static char debug_var[] = "LD_DEBUG=all"; + static char container_var[] = "container=ujail"; const char *preload_lib = find_lib("libpreload-seccomp.so"); int count = 0; @@ -243,6 +252,10 @@ static char** build_envp(const char *seccomp) snprintf(preload_var, sizeof(preload_var), "LD_PRELOAD=%s", preload_lib); envp[count++] = preload_var; } + + if (is_extroot) + envp[count++] = container_var; + if (debug > 1) envp[count++] = debug_var; @@ -269,6 +282,7 @@ static void usage(void) fprintf(stderr, " -U \tuser to run jailed process\n"); fprintf(stderr, " -G \tgroup to run jailed process\n"); fprintf(stderr, " -o\t\tremont jail root (/) read only\n"); + fprintf(stderr, " -R \texternal jail rootfs (system container)\n"); fprintf(stderr, "\nWarning: by default root inside the jail is the same\n\ and he has the same powers as root outside the jail,\n\ thus he can escape the jail and/or break stuff.\n\ @@ -437,6 +451,10 @@ int main(int argc, char **argv) opts.namespace |= NAMESPACE_MOUNT; opts.ronly = 1; break; + case 'R': + opts.namespace |= NAMESPACE_MOUNT | NAMESPACE_UTS; + opts.extroot = optarg; + break; case 's': opts.namespace |= NAMESPACE_MOUNT; opts.sysfs = 1; @@ -502,14 +520,16 @@ int main(int argc, char **argv) opts.jail_argv = &argv[optind]; - if (opts.namespace && add_path_and_deps(*opts.jail_argv, 1, -1, 0)) { - ERROR("failed to load dependencies\n"); - return -1; - } + if (!opts.extroot) { + if (opts.namespace && add_path_and_deps(*opts.jail_argv, 1, -1, 0)) { + ERROR("failed to load dependencies\n"); + return -1; + } - if (opts.namespace && opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) { - ERROR("failed to load libpreload-seccomp.so\n"); - return -1; + if (opts.namespace && opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) { + ERROR("failed to load libpreload-seccomp.so\n"); + return -1; + } } if (opts.name) @@ -537,8 +557,11 @@ int main(int argc, char **argv) flags |= CLONE_NEWNS; add_mount("/dev/full", 0, -1); add_mount("/dev/null", 0, -1); + add_mount("/dev/random", 0, -1); add_mount("/dev/urandom", 0, -1); + add_mount("/dev/tty", 0, -1); add_mount("/dev/zero", 0, -1); + add_mount("/dev/console", 0, -1); if (opts.user || opts.group) { add_mount("/etc/passwd", 0, -1); diff --git a/service/instance.c b/service/instance.c index ffaaaa1..47b7d09 100644 --- a/service/instance.c +++ b/service/instance.c @@ -102,6 +102,7 @@ enum { JAIL_ATTR_MOUNT, JAIL_ATTR_NETNS, JAIL_ATTR_REQUIREJAIL, + JAIL_ATTR_EXTROOT, __JAIL_ATTR_MAX, }; @@ -116,6 +117,7 @@ static const struct blobmsg_policy jail_attr[__JAIL_ATTR_MAX] = { [JAIL_ATTR_MOUNT] = { "mount", BLOBMSG_TYPE_TABLE }, [JAIL_ATTR_NETNS] = { "netns", BLOBMSG_TYPE_BOOL }, [JAIL_ATTR_REQUIREJAIL] = { "requirejail", BLOBMSG_TYPE_BOOL }, + [JAIL_ATTR_EXTROOT] = { "extroot", BLOBMSG_TYPE_STRING }, }; struct instance_netdev { @@ -258,6 +260,11 @@ jail_run(struct service_instance *in, char **argv) if (jail->netns) argv[argc++] = "-N"; + if (jail->extroot) { + argv[argc++] = "-R"; + argv[argc++] = jail->extroot; + } + blobmsg_list_for_each(&jail->mount, var) { const char *type = blobmsg_data(var->data); @@ -863,6 +870,11 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr) jail->netns = blobmsg_get_bool(tb[JAIL_ATTR_NETNS]); jail->argc++; } + if (tb[JAIL_ATTR_EXTROOT]) { + jail->extroot = strdup(blobmsg_get_string(tb[JAIL_ATTR_EXTROOT])); + jail->argc += 2; + } + if (tb[JAIL_ATTR_MOUNT]) { struct blob_attr *cur; int rem; @@ -1139,6 +1151,7 @@ instance_free(struct service_instance *in) free(in->config); free(in->user); free(in->group); + free(in->jail.extroot); free(in->jail.name); free(in->jail.hostname); free(in->seccomp); @@ -1262,6 +1275,8 @@ void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) blobmsg_add_string(b, "name", in->jail.name); if (in->jail.hostname) blobmsg_add_string(b, "hostname", in->jail.hostname); + if (in->jail.extroot) + blobmsg_add_string(b, "extroot", in->jail.extroot); blobmsg_add_u8(b, "procfs", in->jail.procfs); blobmsg_add_u8(b, "sysfs", in->jail.sysfs); blobmsg_add_u8(b, "ubus", in->jail.ubus); diff --git a/service/instance.h b/service/instance.h index 003a8c9..29406c9 100644 --- a/service/instance.h +++ b/service/instance.h @@ -32,6 +32,7 @@ struct jail { bool netns; char *name; char *hostname; + char *extroot; struct blobmsg_list mount; int argc; }; From patchwork Fri Mar 20 18:20:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Golle X-Patchwork-Id: 1259148 X-Patchwork-Delegate: daniel@makrotopia.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20170209 header.b=XPtWchtV; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48kXCk5TCqz9sPF for ; Sat, 21 Mar 2020 05:21:22 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Subject:MIME-Version:Message-ID:To:From :Date:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=8oSMIxP5wp1uNrSafObTz3CS5ppo6EBHGgLkO3v5NV8=; b=XPtWchtVEpHFc8 qNo3UC0h/Qlx/H4wikgEsWv7Rl+hUIJU502uGJo9byywpwH/9Tvs5QkrXxCOZITVcAKGRjDKWt9Ee 3/Bkuvb/6qC2qOBhqyKI0R62XgyGjnCZ2WbiP2FGHkVnZKeMYVZsSMTtd4QLXdRa8gwOaBF7L2IYc BbgOruIfL/Eo7D1FkqrA+KzUy9g4mzzH2g9sF47VtSyTm2Nwi67qlgscHehb1FGku8qTH3JlszSIT plN+Z0h9Bt0OQC1LVSa+ItI7cKDG1PA/TxEtTBAI0mmK1eUbF2jwsSzbShBMUWJWKBOGk32U3Uskq e8mn656mKxu8bHtXwgmA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jFMGi-0006DT-Ac; Fri, 20 Mar 2020 18:21:16 +0000 Received: from fudo.makrotopia.org ([2a07:2ec0:3002::71]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jFMGe-0006D1-TB for openwrt-devel@lists.openwrt.org; Fri, 20 Mar 2020 18:21:15 +0000 Received: from local by fudo.makrotopia.org with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92.2) (envelope-from ) id 1jFMGa-0000CF-Rm; Fri, 20 Mar 2020 19:21:11 +0100 Date: Fri, 20 Mar 2020 18:20:51 +0000 From: Daniel Golle To: openwrt-devel@lists.openwrt.org Message-ID: <20200320182051.GA582694@makrotopia.org> MIME-Version: 1.0 Content-Disposition: inline X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200320_112113_246550_9590F387 X-CRM114-Status: GOOD ( 17.97 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record Subject: [OpenWrt-Devel] [PATCH/RFC procd 2/3] jail: add support for userns and cgroupsns X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: john@phrozen.org Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org Add options to have jailed process inside new user namespace and cgroups namespace. Currently only the root user inside the container is mapped. Also, mounting /proc currently still fails in the new user namespace with permission denied for unknown reasons. Signed-off-by: Daniel Golle --- jail/jail.c | 291 ++++++++++++++++++++++++++++++++++----------- service/instance.c | 43 +++++-- service/instance.h | 4 +- 3 files changed, 255 insertions(+), 83 deletions(-) diff --git a/jail/jail.c b/jail/jail.c index df1bda7..78f0c6b 100644 --- a/jail/jail.c +++ b/jail/jail.c @@ -40,15 +40,7 @@ #include #define STACK_SIZE (1024 * 1024) -#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:" - -#define NAMESPACE_MOUNT (1U << 0) -#define NAMESPACE_IPC (1U << 1) -#define NAMESPACE_NET (1U << 2) -#define NAMESPACE_PID (1U << 3) -#define NAMESPACE_USER (1U << 4) -#define NAMESPACE_UTS (1U << 5) -#define NAMESPACE_CGROUP (1U << 6) +#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:fF" static struct { char *name; @@ -64,8 +56,12 @@ static struct { int procfs; int ronly; int sysfs; + int pw_uid; + int pw_gid; + int gr_gid; } opts; + extern int pivot_root(const char *new_root, const char *put_old); int debug = 0; @@ -166,7 +162,7 @@ static int build_jail_fs(void) } if (opts.extroot) { - if (mount(opts.extroot, jail_root, NULL, MS_BIND | MS_REC, NULL)) { + if (mount(opts.extroot, jail_root, NULL, MS_BIND, NULL)) { ERROR("extroot mount failed %m\n"); return -1; } @@ -187,7 +183,7 @@ static int build_jail_fs(void) return -1; } - if (opts.namespace & NAMESPACE_NET) { + if (opts.namespace & CLONE_NEWNET) { char hostdir[PATH_MAX], jailetc[PATH_MAX], jaillink[PATH_MAX]; snprintf(hostdir, PATH_MAX, "/tmp/resolv.conf-%s.d", opts.name); @@ -231,6 +227,99 @@ static int build_jail_fs(void) return 0; } +static int write_uid_gid_map(pid_t child_pid, bool gidmap, int id) +{ + int map_file; + char map_path[64]; + const char *map_format = "%d %d %d\n"; + if (snprintf(map_path, sizeof(map_path), "/proc/%d/%s", + child_pid, gidmap?"gid_map":"uid_map") < 0) + return -1; + + if ((map_file = open(map_path, O_WRONLY)) == -1) + return -1; + + if (dprintf(map_file, map_format, 0, id, 1) == -1) { + close(map_file); + return -1; + } + + close(map_file); + return 0; +} + +static int write_setgroups(pid_t child_pid, bool allow) +{ + int setgroups_file; + char setgroups_path[64]; + + if (snprintf(setgroups_path, sizeof(setgroups_path), "/proc/%d/setgroups", + child_pid) < 0) { + return -1; + } + + if ((setgroups_file = open(setgroups_path, O_WRONLY)) == -1) { + return -1; + } + + if (dprintf(setgroups_file, allow?"allow":"deny") == -1) { + close(setgroups_file); + return -1; + } + + close(setgroups_file); + return 0; +} + +static void get_jail_user(int *user, int *user_gid, int *gr_gid) +{ + struct passwd *p = NULL; + struct group *g = NULL; + + if (opts.user) { + p = getpwnam(opts.user); + if (!p) { + ERROR("failed to get uid/gid for user %s: %d (%s)\n", + opts.user, errno, strerror(errno)); + exit(EXIT_FAILURE); + } + *user = p->pw_uid; + *user_gid = p->pw_gid; + } else { + *user = -1; + *user_gid = -1; + } + + if (opts.group) { + g = getgrnam(opts.group); + if (!g) { + ERROR("failed to get gid for group %s: %m\n", opts.group); + exit(EXIT_FAILURE); + } + *gr_gid = g->gr_gid; + } else { + *gr_gid = -1; + } +}; + +static void set_jail_user(int pw_uid, int user_gid, int gr_gid) +{ + if ((user_gid != -1) && initgroups(opts.user, user_gid)) { + ERROR("failed to initgroups() for user %s: %m\n", opts.user); + exit(EXIT_FAILURE); + } + + if ((gr_gid != -1) && setregid(gr_gid, gr_gid)) { + ERROR("failed to set group id %d: %m\n", gr_gid); + exit(EXIT_FAILURE); + } + + if ((pw_uid != -1) && setreuid(pw_uid, pw_uid)) { + ERROR("failed to set user id %d: %m\n", pw_uid); + exit(EXIT_FAILURE); + } +} + #define MAX_ENVP 8 static char** build_envp(const char *seccomp) { @@ -253,8 +342,7 @@ static char** build_envp(const char *seccomp) envp[count++] = preload_var; } - if (is_extroot) - envp[count++] = container_var; + envp[count++] = container_var; if (debug > 1) envp[count++] = debug_var; @@ -292,62 +380,71 @@ ujail will not use namespace/build a jail,\n\ and will only drop capabilities/apply seccomp filter.\n\n"); } -static int exec_jail(void *_notused) +static int exec_jail(void *pipes_ptr) { - struct passwd *p = NULL; - struct group *g = NULL; + int *pipes = (int*)pipes_ptr; + char buf[1]; + int pw_uid, pw_gid, gr_gid; - if (opts.capabilities && drop_capabilities(opts.capabilities)) - exit(EXIT_FAILURE); + close(pipes[0]); + close(pipes[3]); - if (opts.no_new_privs && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %m\n"); + + buf[0] = 'i'; + if (write(pipes[1], buf, 1) < 1) { + ERROR("can't write to parent\n"); exit(EXIT_FAILURE); } - - if (opts.namespace && opts.hostname && strlen(opts.hostname) > 0 - && sethostname(opts.hostname, strlen(opts.hostname))) { - ERROR("sethostname(%s) failed: %m\n", opts.hostname); + if (read(pipes[2], buf, 1) < 1) { + ERROR("can't read from parent\n"); exit(EXIT_FAILURE); } - - if (opts.namespace && build_jail_fs()) { - ERROR("failed to build jail fs\n"); + if (buf[0] != 'O') { + ERROR("parent had an error, child exiting\n"); exit(EXIT_FAILURE); } - if (opts.user) { - p = getpwnam(opts.user); - if (!p) { - ERROR("failed to get uid/gid for user %s: %d (%s)\n", - opts.user, errno, strerror(errno)); + close(pipes[1]); + close(pipes[2]); + + if (opts.namespace & CLONE_NEWUSER) { + if (setgid(0) < 0) { + ERROR("setgid\n"); exit(EXIT_FAILURE); } - } - - if (opts.group) { - g = getgrnam(opts.group); - if (!g) { - ERROR("failed to get gid for group %s: %m\n", opts.group); + if (setuid(0) < 0) { + ERROR("setuid\n"); exit(EXIT_FAILURE); } +// if (setgroups(0, NULL) < 0) { +// ERROR("setgroups\n"); +// exit(EXIT_FAILURE); +// } } - if (p && p->pw_gid && initgroups(opts.user, p->pw_gid)) { - ERROR("failed to initgroups() for user %s: %m\n", opts.user); + if (opts.namespace && opts.hostname && strlen(opts.hostname) > 0 + && sethostname(opts.hostname, strlen(opts.hostname))) { + ERROR("sethostname(%s) failed: %m\n", opts.hostname); exit(EXIT_FAILURE); } - if (g && g->gr_gid && setgid(g->gr_gid)) { - ERROR("failed to set group id %d: %m\n", g?g->gr_gid:p->pw_gid); + if (opts.namespace && build_jail_fs()) { + ERROR("failed to build jail fs\n"); exit(EXIT_FAILURE); } - if (p && p->pw_uid && setuid(p->pw_uid)) { - ERROR("failed to set user id %d: %m\n", p->pw_uid); + if (opts.capabilities && drop_capabilities(opts.capabilities)) + exit(EXIT_FAILURE); + + if (opts.no_new_privs && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %m\n"); exit(EXIT_FAILURE); } + if (!(opts.namespace & CLONE_NEWUSER)) { + get_jail_user(&pw_uid, &pw_gid, &gr_gid); + set_jail_user(pw_uid, pw_gid, gr_gid); + } char **envp = build_envp(opts.seccomp); if (!envp) @@ -398,12 +495,20 @@ static void jail_handle_signal(int signo) kill(jail_process.pid, signo); } -static void netns_updown(bool start) +static int netns_open_pid(const pid_t target_ns) +{ + char pid_net_path[PATH_MAX]; + + snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%u/ns/net", target_ns); + + return open(pid_net_path, O_RDONLY); +} + +static void netns_updown(pid_t pid, bool start) { struct ubus_context *ctx = ubus_connect(NULL); static struct blob_buf req; uint32_t id; - pid_t pid = getpid(); if (!ctx) return; @@ -428,6 +533,9 @@ int main(int argc, char **argv) char log[] = "/dev/log"; char ubus[] = "/var/run/ubus.sock"; int ch, i; + int pipes[4]; + char sig_buf[1]; + int netns_fd; if (uid) { ERROR("not root, aborting: %m\n"); @@ -444,19 +552,24 @@ int main(int argc, char **argv) debug = atoi(optarg); break; case 'p': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; opts.procfs = 1; break; case 'o': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; opts.ronly = 1; break; + case 'f': + opts.namespace |= CLONE_NEWUSER; + break; + case 'F': + opts.namespace |= CLONE_NEWCGROUP; + break; case 'R': - opts.namespace |= NAMESPACE_MOUNT | NAMESPACE_UTS; opts.extroot = optarg; break; case 's': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; opts.sysfs = 1; break; case 'S': @@ -473,25 +586,26 @@ int main(int argc, char **argv) opts.name = optarg; break; case 'N': - opts.namespace |= NAMESPACE_NET; + opts.namespace |= CLONE_NEWNET; break; case 'h': + opts.namespace |= CLONE_NEWUTS; opts.hostname = optarg; break; case 'r': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; add_path_and_deps(optarg, 1, 0, 0); break; case 'w': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; add_path_and_deps(optarg, 0, 0, 0); break; case 'u': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; add_mount(ubus, 0, -1); break; case 'l': - opts.namespace |= NAMESPACE_MOUNT; + opts.namespace |= CLONE_NEWNS; add_mount(log, 0, -1); break; case 'U': @@ -503,6 +617,9 @@ int main(int argc, char **argv) } } + if (opts.namespace) + opts.namespace |= CLONE_NEWIPC | CLONE_NEWPID; + /* no param found */ if (argc - optind < 1) { usage(); @@ -513,13 +630,15 @@ int main(int argc, char **argv) usage(); return EXIT_FAILURE; } - DEBUG("Using namespaces(%d), capabilities(%d), seccomp(%d)\n", + DEBUG("Using namespaces(0x%08x), capabilities(%d), seccomp(%d)\n", opts.namespace, opts.capabilities != 0, opts.seccomp != 0); opts.jail_argv = &argv[optind]; + get_jail_user(&opts.pw_uid, &opts.pw_gid, &opts.gr_gid); + if (!opts.extroot) { if (opts.namespace && add_path_and_deps(*opts.jail_argv, 1, -1, 0)) { ERROR("failed to load dependencies\n"); @@ -551,10 +670,7 @@ int main(int argc, char **argv) } if (opts.namespace) { - int flags = SIGCHLD | CLONE_NEWPID | CLONE_NEWIPC; - - if (opts.namespace & NAMESPACE_MOUNT) { - flags |= CLONE_NEWNS; + if (opts.namespace & CLONE_NEWNS) { add_mount("/dev/full", 0, -1); add_mount("/dev/null", 0, -1); add_mount("/dev/random", 0, -1); @@ -563,7 +679,7 @@ int main(int argc, char **argv) add_mount("/dev/zero", 0, -1); add_mount("/dev/console", 0, -1); - if (opts.user || opts.group) { + if (!opts.extroot && (opts.user || opts.group)) { add_mount("/etc/passwd", 0, -1); add_mount("/etc/group", 0, -1); } @@ -573,21 +689,50 @@ int main(int argc, char **argv) } } - if (opts.hostname) - flags |= CLONE_NEWUTS; - - if (opts.namespace & NAMESPACE_NET) { - unshare(CLONE_NEWNET); - netns_updown(true); - }; + if (pipe(&pipes[0]) < 0 || pipe(&pipes[2]) < 0) + return -1; - jail_process.pid = clone(exec_jail, child_stack + STACK_SIZE, flags, NULL); + jail_process.pid = clone(exec_jail, child_stack + STACK_SIZE, SIGCHLD | opts.namespace, &pipes); } else { jail_process.pid = fork(); } if (jail_process.pid > 0) { + seteuid(0); /* parent process */ + close(pipes[1]); + close(pipes[2]); + if (read(pipes[0], sig_buf, 1) < 1) { + ERROR("can't read from child\n"); + return -1; + } + close(pipes[0]); + if (opts.namespace & CLONE_NEWUSER) { + bool has_gr = (opts.gr_gid != -1); + if (write_setgroups(jail_process.pid, false)) { + ERROR("can't write setgroups\n"); + return -1; + } + if (opts.pw_uid != -1) { + write_uid_gid_map(jail_process.pid, 0, opts.pw_uid); + write_uid_gid_map(jail_process.pid, 1, has_gr?opts.gr_gid:opts.pw_gid); + } else { + write_uid_gid_map(jail_process.pid, 0, 65534); + write_uid_gid_map(jail_process.pid, 1, has_gr?opts.gr_gid:65534); + } + } + + if (opts.namespace & CLONE_NEWNET) { + netns_fd = netns_open_pid(jail_process.pid); + netns_updown(jail_process.pid, true); + } + + sig_buf[0] = 'O'; + if (write(pipes[3], sig_buf, 1) < 0) { + ERROR("can't write to child\n"); + return -1; + } + close(pipes[3]); uloop_process_add(&jail_process); uloop_run(); if (jail_running) { @@ -597,9 +742,11 @@ int main(int argc, char **argv) uloop_run(); } uloop_done(); - if (opts.namespace & NAMESPACE_NET) - netns_updown(false); - + if (opts.namespace & CLONE_NEWNET) { + setns(netns_fd, CLONE_NEWNET); + netns_updown(getpid(), false); + close(netns_fd); + } return jail_return_code; } else if (jail_process.pid == 0) { /* fork child process */ diff --git a/service/instance.c b/service/instance.c index 47b7d09..d29aa0d 100644 --- a/service/instance.c +++ b/service/instance.c @@ -62,6 +62,7 @@ enum { INSTANCE_ATTR_RELOADSIG, INSTANCE_ATTR_TERMTIMEOUT, INSTANCE_ATTR_FACILITY, + INSTANCE_ATTR_EXTROOT, __INSTANCE_ATTR_MAX }; @@ -89,6 +90,7 @@ static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { [INSTANCE_ATTR_RELOADSIG] = { "reload_signal", BLOBMSG_TYPE_INT32 }, [INSTANCE_ATTR_TERMTIMEOUT] = { "term_timeout", BLOBMSG_TYPE_INT32 }, [INSTANCE_ATTR_FACILITY] = { "facility", BLOBMSG_TYPE_STRING }, + [INSTANCE_ATTR_EXTROOT] = { "extroot", BLOBMSG_TYPE_STRING }, }; enum { @@ -101,8 +103,9 @@ enum { JAIL_ATTR_RONLY, JAIL_ATTR_MOUNT, JAIL_ATTR_NETNS, + JAIL_ATTR_USERNS, + JAIL_ATTR_CGROUPSNS, JAIL_ATTR_REQUIREJAIL, - JAIL_ATTR_EXTROOT, __JAIL_ATTR_MAX, }; @@ -116,8 +119,9 @@ static const struct blobmsg_policy jail_attr[__JAIL_ATTR_MAX] = { [JAIL_ATTR_RONLY] = { "ronly", BLOBMSG_TYPE_BOOL }, [JAIL_ATTR_MOUNT] = { "mount", BLOBMSG_TYPE_TABLE }, [JAIL_ATTR_NETNS] = { "netns", BLOBMSG_TYPE_BOOL }, + [JAIL_ATTR_USERNS] = { "userns", BLOBMSG_TYPE_BOOL }, + [JAIL_ATTR_CGROUPSNS] = { "cgroupsns", BLOBMSG_TYPE_BOOL }, [JAIL_ATTR_REQUIREJAIL] = { "requirejail", BLOBMSG_TYPE_BOOL }, - [JAIL_ATTR_EXTROOT] = { "extroot", BLOBMSG_TYPE_STRING }, }; struct instance_netdev { @@ -260,9 +264,15 @@ jail_run(struct service_instance *in, char **argv) if (jail->netns) argv[argc++] = "-N"; - if (jail->extroot) { + if (jail->userns) + argv[argc++] = "-f"; + + if (jail->cgroupsns) + argv[argc++] = "-F"; + + if (in->extroot) { argv[argc++] = "-R"; - argv[argc++] = jail->extroot; + argv[argc++] = in->extroot; } blobmsg_list_for_each(&jail->mount, var) { @@ -870,9 +880,13 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr) jail->netns = blobmsg_get_bool(tb[JAIL_ATTR_NETNS]); jail->argc++; } - if (tb[JAIL_ATTR_EXTROOT]) { - jail->extroot = strdup(blobmsg_get_string(tb[JAIL_ATTR_EXTROOT])); - jail->argc += 2; + if (tb[JAIL_ATTR_USERNS]) { + jail->userns = blobmsg_get_bool(tb[JAIL_ATTR_USERNS]); + jail->argc++; + } + if (tb[JAIL_ATTR_CGROUPSNS]) { + jail->cgroupsns = blobmsg_get_bool(tb[JAIL_ATTR_CGROUPSNS]); + jail->argc++; } if (tb[JAIL_ATTR_MOUNT]) { @@ -892,6 +906,10 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr) if (in->group) jail->argc += 2; + if (in->extroot) { + jail->argc += 2; + } + if (in->no_new_privs) jail->argc++; @@ -1003,6 +1021,9 @@ instance_config_parse(struct service_instance *in) if (!in->trace && tb[INSTANCE_ATTR_SECCOMP]) in->seccomp = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_SECCOMP])); + if (tb[INSTANCE_ATTR_EXTROOT]) + in->extroot = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_EXTROOT])); + if (tb[INSTANCE_ATTR_PIDFILE]) { char *pidfile = blobmsg_get_string(tb[INSTANCE_ATTR_PIDFILE]); if (pidfile) @@ -1151,7 +1172,7 @@ instance_free(struct service_instance *in) free(in->config); free(in->user); free(in->group); - free(in->jail.extroot); + free(in->extroot); free(in->jail.name); free(in->jail.hostname); free(in->seccomp); @@ -1275,14 +1296,16 @@ void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) blobmsg_add_string(b, "name", in->jail.name); if (in->jail.hostname) blobmsg_add_string(b, "hostname", in->jail.hostname); - if (in->jail.extroot) - blobmsg_add_string(b, "extroot", in->jail.extroot); + if (in->extroot) + blobmsg_add_string(b, "extroot", in->extroot); blobmsg_add_u8(b, "procfs", in->jail.procfs); blobmsg_add_u8(b, "sysfs", in->jail.sysfs); blobmsg_add_u8(b, "ubus", in->jail.ubus); blobmsg_add_u8(b, "log", in->jail.log); blobmsg_add_u8(b, "ronly", in->jail.ronly); blobmsg_add_u8(b, "netns", in->jail.netns); + blobmsg_add_u8(b, "userns", in->jail.userns); + blobmsg_add_u8(b, "cgroupsns", in->jail.cgroupsns); blobmsg_close_table(b, r); if (!avl_is_empty(&in->jail.mount.avl)) { struct blobmsg_list_node *var; diff --git a/service/instance.h b/service/instance.h index 29406c9..7e80c61 100644 --- a/service/instance.h +++ b/service/instance.h @@ -30,9 +30,10 @@ struct jail { bool log; bool ronly; bool netns; + bool userns; + bool cgroupsns; char *name; char *hostname; - char *extroot; struct blobmsg_list mount; int argc; }; @@ -65,6 +66,7 @@ struct service_instance { struct jail jail; char *seccomp; char *pidfile; + char *extroot; int syslog_facility; int exit_code; From patchwork Fri Mar 20 18:21:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Golle X-Patchwork-Id: 1259149 X-Patchwork-Delegate: daniel@makrotopia.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20170209 header.b=o/10PTzQ; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48kXDg6s2jz9sPF for ; Sat, 21 Mar 2020 05:22:11 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Subject:MIME-Version:Message-ID:To:From :Date:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=pzflXWKIvHCqPzaE4uW67mTJrSKDsWJ65JcZDGCwORo=; b=o/10PTzQTtizCc xqP8ioXe/SqYySZMCP5MLuKbbNKM3cglvczzArpJfe2wx6WpEHSArWncngRowDAce8e3TvxvZk+SM CiRKyzRXxcqWCea2Sa0T3OrqFhOFvc5bMmLK/GSksNVvXXha5J3appmWNG3tw6VFxgIfILTRpY/6k GdRXPnTZ4/o0ePU3QmfubiurAYHW+Q5vy9vv0ALovBpWAlYvTdc7eX63N2ASQAlEaqgyZ56PgjyUQ /1XrX996GhSoUPYSm6W3WQyY74JABL+6NT1uHO1juFP+H06y/3PAW34gTYPqSNkFIKwxsksVrW9JM suF+KNEfgHmIOkUACEyQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jFMHY-0006Yb-KL; Fri, 20 Mar 2020 18:22:08 +0000 Received: from fudo.makrotopia.org ([2a07:2ec0:3002::71]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jFMHV-0006Y4-Px for openwrt-devel@lists.openwrt.org; Fri, 20 Mar 2020 18:22:07 +0000 Received: from local by fudo.makrotopia.org with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92.2) (envelope-from ) id 1jFMHU-0000Cy-1I; Fri, 20 Mar 2020 19:22:04 +0100 Date: Fri, 20 Mar 2020 18:21:43 +0000 From: Daniel Golle To: openwrt-devel@lists.openwrt.org Message-ID: <20200320182143.GA582704@makrotopia.org> MIME-Version: 1.0 Content-Disposition: inline X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200320_112206_002260_33C095BD X-CRM114-Status: GOOD ( 14.12 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record Subject: [OpenWrt-Devel] [PATCH/RFC procd 3/3] jail: add support for (ram-)overlayfs X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: john@phrozen.org Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org Add support for running service with a read/write filesystem overlay. This can either be a user-defined directory for persistency or reside on a tmpfs with fixed size in the RAM. Signed-off-by: Daniel Golle --- jail/jail.c | 95 +++++++++++++++++++++++++++++++++++++++++++--- service/instance.c | 36 +++++++++++++++++- service/instance.h | 2 + 3 files changed, 126 insertions(+), 7 deletions(-) diff --git a/jail/jail.c b/jail/jail.c index 78f0c6b..ec106d2 100644 --- a/jail/jail.c +++ b/jail/jail.c @@ -40,7 +40,7 @@ #include #define STACK_SIZE (1024 * 1024) -#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:fF" +#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:" static struct { char *name; @@ -51,6 +51,8 @@ static struct { char *user; char *group; char *extroot; + char *overlaydir; + char *tmpoverlaysize; int no_new_privs; int namespace; int procfs; @@ -147,9 +149,46 @@ int mount_bind(const char *root, const char *path, int readonly, int error) { return _mount_bind(root, path, NULL, readonly, 0, error); } +static int mount_overlay(char *jail_root, char *overlaydir) { + char *upperdir, *workdir, *optsstr; + const char mountoptsformat[] = "lowerdir=%s,upperdir=%s,workdir=%s"; + int ret = -1; + + if (asprintf(&upperdir, "%s%s", overlaydir, "/upper") < 0) + goto out; + + if (asprintf(&workdir, "%s%s", overlaydir, "/work") < 0) + goto upper_printf; + + if (asprintf(&optsstr, mountoptsformat, jail_root, upperdir, workdir) < 0) + goto work_printf; + + if (mkdir_p(upperdir, 0755) || mkdir_p(workdir, 0755)) + goto opts_printf; + + DEBUG("mount -t overlay %s %s (%s)\n", jail_root, jail_root, optsstr); + + if (mount(jail_root, jail_root, "overlay", MS_NOATIME, optsstr)) + goto opts_printf; + + ret = 0; + +opts_printf: + free(optsstr); +work_printf: + free(workdir); +upper_printf: + free(upperdir); +out: + return ret; +} + static int build_jail_fs(void) { char jail_root[] = "/tmp/ujail-XXXXXX"; + char tmpovdir[] = "/tmp/ujail-overlay-XXXXXX"; + char *overlaydir = NULL; + if (mkdtemp(jail_root) == NULL) { ERROR("mkdtemp(%s) failed: %m\n", jail_root); return -1; @@ -173,6 +212,29 @@ static int build_jail_fs(void) } } + if (opts.tmpoverlaysize) { + char mountoptsstr[] = "mode=0755,size=XXXXXXXX"; + + snprintf(mountoptsstr, sizeof(mountoptsstr), + "mode=0755,size=%s", opts.tmpoverlaysize); + if (mkdtemp(tmpovdir) == NULL) { + ERROR("mkdtemp(%s) failed: %m\n", jail_root); + return -1; + } + if (mount("tmpfs", tmpovdir, "tmpfs", MS_NOATIME, + mountoptsstr)) { + ERROR("failed to mount tmpfs for overlay (size=%s)\n", opts.tmpoverlaysize); + return -1; + } + overlaydir = tmpovdir; + } + + if (opts.overlaydir) + overlaydir = opts.overlaydir; + + if (overlaydir) + mount_overlay(jail_root, overlaydir); + if (chdir(jail_root)) { ERROR("chdir(%s) (jail_root) failed: %m\n", jail_root); return -1; @@ -209,7 +271,15 @@ static int build_jail_fs(void) } snprintf(dirbuf, sizeof(dirbuf), "/old%s", jail_root); + umount2(dirbuf, MNT_DETACH); rmdir(dirbuf); + if (opts.tmpoverlaysize) { + char tmpdirbuf[sizeof(tmpovdir) + 4]; + snprintf(tmpdirbuf, sizeof(tmpdirbuf), "/old%s", tmpovdir); + umount2(tmpdirbuf, MNT_DETACH); + rmdir(tmpdirbuf); + } + umount2("/old", MNT_DETACH); rmdir("/old"); @@ -361,6 +431,8 @@ static void usage(void) fprintf(stderr, "namespace jail options:\n"); fprintf(stderr, " -h \tchange the hostname of the jail\n"); fprintf(stderr, " -N\t\tjail has network namespace\n"); + fprintf(stderr, " -f\t\tjail has user namespace\n"); + fprintf(stderr, " -F\t\tjail has cgroups namespace\n"); fprintf(stderr, " -r \treadonly files that should be staged\n"); fprintf(stderr, " -w \twriteable files that should be staged\n"); fprintf(stderr, " -p\t\tjail has /proc\n"); @@ -371,6 +443,8 @@ static void usage(void) fprintf(stderr, " -G \tgroup to run jailed process\n"); fprintf(stderr, " -o\t\tremont jail root (/) read only\n"); fprintf(stderr, " -R \texternal jail rootfs (system container)\n"); + fprintf(stderr, " -O \tdirectory for r/w overlayfs\n"); + fprintf(stderr, " -T \tuse tmpfs r/w overlayfs with \n"); fprintf(stderr, "\nWarning: by default root inside the jail is the same\n\ and he has the same powers as root outside the jail,\n\ thus he can escape the jail and/or break stuff.\n\ @@ -614,12 +688,23 @@ int main(int argc, char **argv) case 'G': opts.group = optarg; break; + case 'O': + opts.overlaydir = optarg; + break; + case 'T': + opts.tmpoverlaysize = optarg; + break; } } if (opts.namespace) opts.namespace |= CLONE_NEWIPC | CLONE_NEWPID; + if (opts.tmpoverlaysize && strlen(opts.tmpoverlaysize) > 8) { + ERROR("size parameter too long: \"%s\"\n", opts.tmpoverlaysize); + return -1; + } + /* no param found */ if (argc - optind < 1) { usage(); @@ -644,11 +729,11 @@ int main(int argc, char **argv) ERROR("failed to load dependencies\n"); return -1; } + } - if (opts.namespace && opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) { - ERROR("failed to load libpreload-seccomp.so\n"); - return -1; - } + if (opts.namespace && opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) { + ERROR("failed to load libpreload-seccomp.so\n"); + return -1; } if (opts.name) diff --git a/service/instance.c b/service/instance.c index d29aa0d..c941157 100644 --- a/service/instance.c +++ b/service/instance.c @@ -63,6 +63,8 @@ enum { INSTANCE_ATTR_TERMTIMEOUT, INSTANCE_ATTR_FACILITY, INSTANCE_ATTR_EXTROOT, + INSTANCE_ATTR_OVERLAYDIR, + INSTANCE_ATTR_TMPOVERLAYSIZE, __INSTANCE_ATTR_MAX }; @@ -91,6 +93,8 @@ static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { [INSTANCE_ATTR_TERMTIMEOUT] = { "term_timeout", BLOBMSG_TYPE_INT32 }, [INSTANCE_ATTR_FACILITY] = { "facility", BLOBMSG_TYPE_STRING }, [INSTANCE_ATTR_EXTROOT] = { "extroot", BLOBMSG_TYPE_STRING }, + [INSTANCE_ATTR_OVERLAYDIR] = { "overlaydir", BLOBMSG_TYPE_STRING }, + [INSTANCE_ATTR_TMPOVERLAYSIZE] = { "tmpoverlaysize", BLOBMSG_TYPE_STRING }, }; enum { @@ -275,6 +279,16 @@ jail_run(struct service_instance *in, char **argv) argv[argc++] = in->extroot; } + if (in->overlaydir) { + argv[argc++] = "-O"; + argv[argc++] = in->overlaydir; + } + + if (in->tmpoverlaysize) { + argv[argc++] = "-T"; + argv[argc++] = in->tmpoverlaysize; + } + blobmsg_list_for_each(&jail->mount, var) { const char *type = blobmsg_data(var->data); @@ -906,9 +920,14 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr) if (in->group) jail->argc += 2; - if (in->extroot) { + if (in->extroot) + jail->argc += 2; + + if (in->overlaydir) + jail->argc += 2; + + if (in->tmpoverlaysize) jail->argc += 2; - } if (in->no_new_privs) jail->argc++; @@ -1024,6 +1043,12 @@ instance_config_parse(struct service_instance *in) if (tb[INSTANCE_ATTR_EXTROOT]) in->extroot = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_EXTROOT])); + if (tb[INSTANCE_ATTR_OVERLAYDIR]) + in->overlaydir = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_OVERLAYDIR])); + + if (tb[INSTANCE_ATTR_TMPOVERLAYSIZE]) + in->tmpoverlaysize = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_TMPOVERLAYSIZE])); + if (tb[INSTANCE_ATTR_PIDFILE]) { char *pidfile = blobmsg_get_string(tb[INSTANCE_ATTR_PIDFILE]); if (pidfile) @@ -1173,6 +1198,8 @@ instance_free(struct service_instance *in) free(in->user); free(in->group); free(in->extroot); + free(in->overlaydir); + free(in->tmpoverlaysize); free(in->jail.name); free(in->jail.hostname); free(in->seccomp); @@ -1298,6 +1325,11 @@ void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) blobmsg_add_string(b, "hostname", in->jail.hostname); if (in->extroot) blobmsg_add_string(b, "extroot", in->extroot); + if (in->overlaydir) + blobmsg_add_string(b, "overlaydir", in->overlaydir); + if (in->tmpoverlaysize) + blobmsg_add_string(b, "tmpoverlaysize", in->tmpoverlaysize); + blobmsg_add_u8(b, "procfs", in->jail.procfs); blobmsg_add_u8(b, "sysfs", in->jail.sysfs); blobmsg_add_u8(b, "ubus", in->jail.ubus); diff --git a/service/instance.h b/service/instance.h index 7e80c61..43a6561 100644 --- a/service/instance.h +++ b/service/instance.h @@ -67,6 +67,8 @@ struct service_instance { char *seccomp; char *pidfile; char *extroot; + char *overlaydir; + char *tmpoverlaysize; int syslog_facility; int exit_code;