From 52c5c1980ba33a71611204957f3061cb51fb65a8 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sun, 29 Dec 2019 16:23:34 +0200 Subject: [PATCH] jail: set user and group inside jail This allows jailed services to run as users other than root, simply because some services refuse to be run as UID 0. Previously, setting the the process UID and GID before launching the jail wrapper prevented the jail from starting. Rather than setting them in procd/service.c, pass user and group parameters to ujail and set them inside ujail just before executing the service. Signed-off-by: Daniel Golle --- jail/jail.c | 59 +++++++++++++++++++++++++++++++++++++++++++++- service/instance.c | 22 ++++++++++++++--- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/jail/jail.c b/jail/jail.c index 54e7841..47da1c1 100644 --- a/jail/jail.c +++ b/jail/jail.c @@ -15,10 +15,13 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -36,7 +39,7 @@ #include #define STACK_SIZE (1024 * 1024) -#define OPT_ARGS "S:C:n:h:r:w:d:psuloc" +#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:" static struct { char *name; @@ -44,6 +47,8 @@ static struct { char **jail_argv; char *seccomp; char *capabilities; + char *user; + char *group; int no_new_privs; int namespace; int procfs; @@ -225,6 +230,8 @@ static void usage(void) fprintf(stderr, " -s\t\tjail has /sys\n"); fprintf(stderr, " -l\t\tjail has /dev/log\n"); fprintf(stderr, " -u\t\tjail has a ubus socket\n"); + 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, "\nWarning: by default root inside the jail is the same\n\ and he has the same powers as root outside the jail,\n\ @@ -237,6 +244,9 @@ and will only drop capabilities/apply seccomp filter.\n\n"); static int exec_jail(void *_notused) { + struct passwd *p = NULL; + struct group *g = NULL; + if (opts.capabilities && drop_capabilities(opts.capabilities)) exit(EXIT_FAILURE); @@ -256,6 +266,39 @@ static int exec_jail(void *_notused) 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)); + exit(EXIT_FAILURE); + } + } + + if (opts.group) { + g = getgrnam(opts.group); + if (!g) { + ERROR("failed to get gid for group %s: %m\n", opts.group); + 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); + 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); + exit(EXIT_FAILURE); + } + + if (p && p->pw_uid && setuid(p->pw_uid)) { + ERROR("failed to set user id %d: %m\n", p->pw_uid); + exit(EXIT_FAILURE); + } + + char **envp = build_envp(opts.seccomp); if (!envp) exit(EXIT_FAILURE); @@ -371,6 +414,12 @@ int main(int argc, char **argv) opts.namespace = 1; add_mount(log, 0, -1); break; + case 'U': + opts.user = optarg; + break; + case 'G': + opts.group = optarg; + break; } } @@ -425,6 +474,14 @@ int main(int argc, char **argv) add_mount("/dev/urandom", 0, -1); add_mount("/dev/zero", 0, -1); + if (opts.user || opts.group) { + add_mount("/etc/passwd", 0, -1); + add_mount("/etc/group", 0, -1); + } + + if (opts.namespace & NAMESPACE_IPC) + flags |= CLONE_NEWIPC; + int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | SIGCHLD; if (opts.hostname) flags |= CLONE_NEWUTS; diff --git a/service/instance.c b/service/instance.c index 3098ff3..abd1f34 100644 --- a/service/instance.c +++ b/service/instance.c @@ -222,6 +222,16 @@ jail_run(struct service_instance *in, char **argv) argv[argc++] = in->seccomp; } + if (in->user) { + argv[argc++] = "-U"; + argv[argc++] = in->user; + } + + if (in->group) { + argv[argc++] = "-G"; + argv[argc++] = in->group; + } + if (in->no_new_privs) argv[argc++] = "-c"; @@ -370,15 +380,15 @@ instance_run(struct service_instance *in, int _stdout, int _stderr) closefd(_stderr); } - if (in->user && in->pw_gid && initgroups(in->user, in->pw_gid)) { + if (!in->has_jail && in->user && in->pw_gid && initgroups(in->user, in->pw_gid)) { ERROR("failed to initgroups() for user %s: %m\n", in->user); exit(127); } - if (in->gr_gid && setgid(in->gr_gid)) { + if (!in->has_jail && in->gr_gid && setgid(in->gr_gid)) { ERROR("failed to set group id %d: %m\n", in->gr_gid); exit(127); } - if (in->uid && setuid(in->uid)) { + if (!in->has_jail && in->uid && setuid(in->uid)) { ERROR("failed to set user id %d: %m\n", in->uid); exit(127); } @@ -833,6 +843,12 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr) if (in->seccomp) jail->argc += 2; + if (in->user) + jail->argc += 2; + + if (in->group) + jail->argc += 2; + if (in->no_new_privs) jail->argc++; -- 2.25.1