#include <libubus.h>
#define STACK_SIZE (1024 * 1024)
-#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:"
+#define OPT_ARGS "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:Ey"
static struct {
char *name;
int procfs;
int ronly;
int sysfs;
+ int console;
int pw_uid;
int pw_gid;
int gr_gid;
+ int require_jail;
} opts;
static char child_stack[STACK_SIZE];
+int console_fd;
+
static int mkdir_p(char *dir, mode_t mask)
{
char *l = strrchr(dir, '/');
return ret;
}
+static void pass_console(int console_fd)
+{
+ struct ubus_context *ctx = ubus_connect(NULL);
+ static struct blob_buf req;
+ uint32_t id;
+
+ if (!ctx)
+ return;
+
+ blob_buf_init(&req, 0);
+ blobmsg_add_string(&req, "name", opts.name);
+
+ if (ubus_lookup_id(ctx, "container", &id) ||
+ ubus_invoke_fd(ctx, id, "console_set", req.head, NULL, NULL, 3000, console_fd))
+ INFO("ubus request failed\n");
+ else
+ close(console_fd);
+
+ blob_buf_free(&req);
+ ubus_free(ctx);
+}
+
+static int create_dev_console(const char *jail_root)
+{
+ char *console_fname;
+ char dev_console_path[PATH_MAX];
+ int slave_console_fd;
+
+ /* Open UNIX/98 virtual console */
+ console_fd = posix_openpt(O_RDWR | O_NOCTTY);
+ if (console_fd == -1)
+ return -1;
+
+ console_fname = ptsname(console_fd);
+ DEBUG("got console fd %d and PTS client name %s\n", console_fd, console_fname);
+ if (!console_fname)
+ goto no_console;
+
+ grantpt(console_fd);
+ unlockpt(console_fd);
+
+ /* pass PTY master to procd */
+ pass_console(console_fd);
+
+ /* mount-bind PTY slave to /dev/console in jail */
+ snprintf(dev_console_path, sizeof(dev_console_path), "%s/dev/console", jail_root);
+ close(creat(dev_console_path, 0620));
+
+ if (mount(console_fname, dev_console_path, NULL, MS_BIND, NULL))
+ goto no_console;
+
+ /* use PTY slave for stdio */
+ slave_console_fd = open(console_fname, O_RDWR); /* | O_NOCTTY */
+ dup2(slave_console_fd, 0);
+ dup2(slave_console_fd, 1);
+ dup2(slave_console_fd, 2);
+ close(slave_console_fd);
+
+ INFO("using guest console %s\n", console_fname);
+
+ return 0;
+
+no_console:
+ close(console_fd);
+ return 1;
+}
+
static int build_jail_fs(void)
{
char jail_root[] = "/tmp/ujail-XXXXXX";
char tmpovdir[] = "/tmp/ujail-overlay-XXXXXX";
+ char tmpdevdir[] = "/tmp/ujail-XXXXXX/dev";
+ char tmpdevptsdir[] = "/tmp/ujail-XXXXXX/dev/pts";
char *overlaydir = NULL;
if (mkdtemp(jail_root) == NULL) {
return -1;
}
+ snprintf(tmpdevdir, sizeof(tmpdevdir), "%s/dev", jail_root);
+ mkdir_p(tmpdevdir, 0755);
+ if (mount(NULL, tmpdevdir, "tmpfs", MS_NOATIME | MS_NOEXEC | MS_NOSUID, "size=1M"))
+ return -1;
+
+ snprintf(tmpdevptsdir, sizeof(tmpdevptsdir), "%s/dev/pts", jail_root);
+ mkdir_p(tmpdevptsdir, 0755);
+ if (mount(NULL, tmpdevptsdir, "devpts", MS_NOATIME | MS_NOEXEC | MS_NOSUID, NULL))
+ return -1;
+
+ if (opts.console)
+ create_dev_console(jail_root);
+
if (mount_all(jail_root)) {
ERROR("mount_all() failed\n");
return -1;
fprintf(stderr, " -R <dir>\texternal jail rootfs (system container)\n");
fprintf(stderr, " -O <dir>\tdirectory for r/w overlayfs\n");
fprintf(stderr, " -T <size>\tuse tmpfs r/w overlayfs with <size>\n");
+ fprintf(stderr, " -E\t\tfail if jail cannot be setup\n");
+ fprintf(stderr, " -y\t\tprovide jail console\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\
close(pipes[0]);
close(pipes[3]);
-
buf[0] = 'i';
if (write(pipes[1], buf, 1) < 1) {
ERROR("can't write to parent\n");
case 'T':
opts.tmpoverlaysize = optarg;
break;
+ case 'E':
+ opts.require_jail = 1;
+ break;
+ case 'y':
+ opts.console = 1;
+ break;
}
}
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;
+ opts.seccomp = 0;
+ if (opts.require_jail)
+ return -1;
}
if (opts.name)
if (!sigismember(&sigmask, i))
continue;
- if ((i == SIGCHLD) || (i == SIGPIPE))
+ if ((i == SIGCHLD) || (i == SIGPIPE) || (i == SIGSEGV))
continue;
s.sa_handler = jail_handle_signal;
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);
+ add_mount("/dev/ptmx", 0, -1);
+ add_mount("/dev/tty", 0, -1);
if (!opts.extroot && (opts.user || opts.group)) {
add_mount("/etc/passwd", 0, -1);
add_mount("/etc/group", 0, -1);
}
+#if defined(__GLIBC__)
+ if (!opts.extroot)
+ add_mount("/etc/nsswitch.conf", 0, -1);
+#endif
+
if (!(opts.namespace & CLONE_NEWNET)) {
add_mount("/etc/resolv.conf", 0, -1);
}
}
if (opts.namespace & CLONE_NEWNET) {
+ if (!opts.name) {
+ ERROR("netns needs a named jail\n");
+ return -1;
+ }
netns_fd = netns_open_pid(jail_process.pid);
netns_updown(jail_process.pid, true);
}