mkbox

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 8687d82d6aadb2b9b67cbfba8e52af66775847ba
parent 0f6781f4f5d8125422493f8393daa0d6b959b41d
Author: Brian Swetland <swetland@frotz.net>
Date:   Mon,  4 May 2015 21:53:19 -0700

tidy things up

fancier commandline interface and help message
ability to mount full /dev and /sys if desired
ability to mount namespace-local /proc if desires

As usual, thanks to Andrew and Kenton for sharing their hard-won
knowledge here, particularly around the gymnastics needed to get
/proc setup...

Diffstat:
MMakefile | 2+-
Mmkbox.c | 148++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 111 insertions(+), 39 deletions(-)

diff --git a/Makefile b/Makefile @@ -12,7 +12,7 @@ test: mkbox cp /bin/busybox sandbox/bin chmod 755 sandbox/bin/busybox ( cd sandbox/bin && for x in $$(busybox --list) ; do ln -fs busybox $$x ; done ) - ./mkbox sandbox `pwd`/databox + ./mkbox --data=`pwd`/databox --with-proc sandbox clean-test:: rm -rf sandbox databox diff --git a/mkbox.c b/mkbox.c @@ -54,27 +54,67 @@ int dropcaps(void) { return capset(&header, data); } +void usage(void) { + fprintf(stderr, +"usage: mkbox [ options ]* <root>\n" +"\n" +"options: --with-dev mount /dev at sandbox's /dev\n" +" (otherwise only /dev/{null,zero,random})\n" +" --with-sys mount /sys at sandbox's /sys\n" +" --with-proc mount /proc at sandbox's /proc\n" +" --data=<path> mount <path> at sandbox's /data (rw)\n" +" --init=<path> exec <path> in sandbox (default: /bin/sh)\n" +"\n" + ); +} + int main(int argc, char **argv) { + int newuid = 3333; + int newgid = 3333; + int with_sys = 0; + int with_proc = 0; + int with_dev = 0; char buf[1024]; int fd; - const char *sandbox; - const char *databox; + const char *sandbox = NULL; + const char *databox = NULL; + const char *initbin = "/bin/sh"; uid_t uid; gid_t gid; pid_t cpid; - if (argc != 3) { - fprintf(stderr, - "usage: mkbox <sandbox-rootdir> <sandbox-datadir>\n"); + argv++; + argc--; + while (argc > 0) { + if (argv[0][0] != '-') break; + if (!strcmp(argv[0], "--with-sys")) { + with_sys = 1; + } else if (!strcmp(argv[0], "--with-proc")) { + with_proc = 1; + } else if (!strcmp(argv[0], "--with-dev")) { + with_dev = 1; + } else if (!strncmp(argv[0], "--init=", 7)) { + initbin = argv[0] + 7; + } else if (!strncmp(argv[0], "--data=", 7)) { + databox = argv[0] + 7; + } else { + usage(); + return -1; + } + argv++; + argc--; + } + if (argc != 1) { + usage(); return -1; } - sandbox = argv[1]; - databox = argv[2]; + sandbox = argv[0]; uid = getuid(); gid = getgid(); - ok(unshare, CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS| + ok(unshare, CLONE_NEWPID| + CLONE_NEWNS|CLONE_NEWUTS| CLONE_NEWIPC|CLONE_NEWUSER); /* ensure that changes to our mount namespace do not "leak" to @@ -97,56 +137,80 @@ int main(int argc, char **argv) { ok(mkdir, "dev", 0755); ok(mkdir, ".oldroot", 0755); - /* mount read-write data volume */ - ok(mount, databox, "data", NULL, MS_BIND|MS_NOSUID, NULL); - - /* mount a tmpfs for dev */ - ok(mount, "sandbox-dev", "dev", "tmpfs", - MS_NOSUID|MS_NOEXEC|MS_NOATIME, - "size=64k,nr_inodes=16,mode=755"); - - /* populate bare minimum device nodes */ - /* create bind points */ - ok(close, ok(open, "dev/null", O_WRONLY|O_CREAT, 0666)); - ok(close, ok(open, "dev/zero", O_WRONLY|O_CREAT, 0666)); + if (databox) { + /* mount read-write data volume */ + ok(mount, databox, "data", NULL, MS_BIND|MS_NOSUID|MS_NODEV, NULL); + } - /* bind mount the device nodes we want */ - ok(mount, "/dev/null", "dev/null", NULL, MS_BIND, NULL); - ok(mount, "/dev/zero", "dev/zero", NULL, MS_BIND, NULL); + if (with_proc) { + rmdir("xproc"); + rmdir("proc"); + ok(mkdir, "xproc", 0755); + ok(mkdir, "proc", 0755); + /* we need to hang on to the old proc in order to mount our + * new proc later on + */ + ok(mount, "/proc", "xproc", NULL, MS_BIND|MS_REC, NULL); + } + if (with_sys) { + rmdir("sys"); + ok(mkdir, "sys", 0755); + ok(mount, "/sys", "sys", NULL, MS_BIND|MS_REC, NULL); + } - /* note: MS_RDONLY does not work when doing the initial bind */ - ok(mount, "dev", "dev", NULL, - MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_REMOUNT|MS_NOATIME|MS_BIND, - NULL); + if (with_dev) { + ok(mount, "/dev", "dev", NULL, MS_BIND|MS_REC, NULL); + } else { + /* mount a tmpfs for dev */ + ok(mount, "sandbox-dev", "dev", "tmpfs", + MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, + "size=64k,nr_inodes=16,mode=755"); + + /* populate bare minimum device nodes */ + /* create bind points */ + ok(mknod, "dev/null", S_IFREG | 0666, 0); + ok(mknod, "dev/zero", S_IFREG | 0666, 0); + ok(mknod, "dev/random", S_IFREG | 0666, 0); + ok(mknod, "dev/urandom", S_IFREG | 0666, 0); + + /* bind mount the device nodes we want */ + ok(mount, "/dev/null", "dev/null", NULL, MS_BIND, NULL); + ok(mount, "/dev/zero", "dev/zero", NULL, MS_BIND, NULL); + ok(mount, "/dev/urandom", "dev/random", NULL, MS_BIND, NULL); + ok(mount, "/dev/urandom", "dev/urandom", NULL, MS_BIND, NULL); + + /* note: MS_RDONLY does not work when doing the initial bind */ + ok(mount, "dev", "dev", NULL, + MS_REMOUNT | MS_BIND | MS_NOEXEC | + MS_NOSUID | MS_NODEV | MS_RDONLY, + NULL); + } - /* map UID/GID 3333/3333 to outer UID/GID */ - sprintf(buf, "3333 %d 1\n", uid); + /* map new UID/GID to outer UID/GID */ + sprintf(buf, "%d %d 1\n", newuid, uid); fd = ok(open, "/proc/self/uid_map", O_WRONLY); ok(write, fd, buf, strlen(buf)); ok(close, fd); - sprintf(buf, "3333 %d 1\n", gid); + sprintf(buf, "%d %d 1\n", newgid, gid); fd = ok(open, "/proc/self/gid_map", O_WRONLY); ok(write, fd, buf, strlen(buf)); ok(close, fd); /* initially we're nobody, change to 3333 */ - ok(setresgid, 3333, 3333, 3333); - ok(setresuid, 3333, 3333, 3333); + ok(setresgid, newgid, newgid, newgid); + ok(setresuid, newuid, newuid, newuid); /* sandbox becomes our new root, detach the old one */ ok(pivot_root, ".", ".oldroot"); ok(umount2, ".oldroot", MNT_DETACH); - unlink(".oldroot"); + ok(rmdir, ".oldroot"); /* remount root to finalize permissions */ ok(mount, "/", "/", NULL, - MS_RDONLY|MS_NOSUID|MS_REMOUNT|MS_NOATIME|MS_BIND|MS_RDONLY, + MS_RDONLY|MS_BIND|MS_NOSUID|MS_REMOUNT, NULL); - /* discard all capability bits */ - ok(dropcaps); - /* we must fork to become pid 1 in the new pid namespace */ cpid = ok(fork); @@ -155,7 +219,15 @@ int main(int argc, char **argv) { fprintf(stderr, "mkbox child pid != 1?!\n"); return -1; } - ok(execl, "/bin/sh", "/bin/sh", NULL); + if (with_proc) { + ok(mount, "/proc", "/proc", "proc", MS_NOSUID, NULL); + ok(umount2, "/xproc", MNT_DETACH); + } + + /* discard all capability bits */ + ok(dropcaps); + + ok(execl, initbin, initbin, NULL); exit(0); }