Improve bindmount interface.
authorJT Olds <hello@jtolds.com>
Tue, 7 Jul 2015 17:15:37 +0000 (11:15 -0600)
committerJT Olds <hello@jtolds.com>
Tue, 7 Jul 2015 17:52:32 +0000 (11:52 -0600)
Now supports readonly bindmounts and
differentiating between source and target path.

cmdline.c
common.h
contain.c

index a2e9f91388372940d853ce887d7c51b6f066ddf9..85c77d43f370b6330f3fc9a3da50b62e76582083 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -95,8 +95,11 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
             logYesNo(nsjconf->clone_newuts), logYesNo(nsjconf->apply_sandbox), logYesNo(nsjconf->keep_caps));
 
        struct constchar_t *p;
-       LIST_FOREACH(p, &nsjconf->bindmountpts, pointers) {
-               LOG_I("Additional bind mount point: '%s'", p->value);
+       LIST_FOREACH(p, &nsjconf->robindmountpts, pointers) {
+               LOG_I("Additional (ro) bind mount point: '%s'", p->value);
+       }
+       LIST_FOREACH(p, &nsjconf->rwbindmountpts, pointers) {
+               LOG_I("Additional (rw) bind mount point: '%s'", p->value);
        }
        LIST_FOREACH(p, &nsjconf->tmpfsmountpts, pointers) {
                LOG_I("Additional tmpfs mount point: '%s'", p->value);
@@ -181,7 +184,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
        /*  *INDENT-OFF* */
 
        LIST_INIT(&nsjconf->pids);
-       LIST_INIT(&nsjconf->bindmountpts);
+       LIST_INIT(&nsjconf->robindmountpts);
+       LIST_INIT(&nsjconf->rwbindmountpts);
        LIST_INIT(&nsjconf->tmpfsmountpts);
 
        const char *user = "nobody";
@@ -228,8 +232,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                {{"disable_sandbox", no_argument, NULL, 0x0501}, "Don't enable the seccomp-bpf sandboxing (default: false)"},
                {{"rw", no_argument, NULL, 0x0503}, "Mount / as RW (default: RO)"},
                {{"silent", no_argument, NULL, 0x0504}, "Redirect child's fd:0/1/2 to /dev/null (default: false)"},
-               {{"bindmount", required_argument, NULL, 'B'}, "List of mountpoints to be mounted --bind inside the container. Can be specified multiple times (default: none)"},
-               {{"tmpfsmount", required_argument, NULL, 'T'}, "List of mountpoints to be mounted as RW/tmpfs inside the container. Can be specified multiple times (default: none)"},
+               {{"bindmount_ro", required_argument, NULL, 0x0505}, "List of mountpoints to be mounted --bind (ro) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'. (default: none)"},
+               {{"bindmount", required_argument, NULL, 'B'}, "List of mountpoints to be mounted --bind (rw) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'. (default: none)"},
+               {{"tmpfsmount", required_argument, NULL, 'T'}, "List of mountpoints to be mounted as RW/tmpfs inside the container. Can be specified multiple times. Supports 'dest' syntax. (default: none)"},
                {{"iface", required_argument, NULL, 'I'}, "Interface which will be cloned (MACVTAP) and put inside the subprocess' namespace"},
                {{0, 0, 0, 0}, NULL},
        };
@@ -350,6 +355,16 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                case 0x0504:
                        nsjconf->is_silent = true;
                        break;
+               case 0x0505:
+                       {
+                               struct constchar_t *p = malloc(sizeof(struct constchar_t));
+                               if (p == NULL) {
+                                       PLOG_F("malloc(%zu)", sizeof(struct constchar_t));
+                               }
+                               p->value = optarg;
+                               LIST_INSERT_HEAD(&nsjconf->robindmountpts, p, pointers);
+                       }
+                       break;
                case 'B':
                        {
                                struct constchar_t *p = malloc(sizeof(struct constchar_t));
@@ -357,7 +372,7 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                                        PLOG_F("malloc(%zu)", sizeof(struct constchar_t));
                                }
                                p->value = optarg;
-                               LIST_INSERT_HEAD(&nsjconf->bindmountpts, p, pointers);
+                               LIST_INSERT_HEAD(&nsjconf->rwbindmountpts, p, pointers);
                        }
                        break;
                case 'T':
index 89e56ce155ddc26636dcdc42e6e184ae6cd7228e..facbba5299625d2e172af39179ab697c47d039d5 100644 (file)
--- a/common.h
+++ b/common.h
@@ -84,7 +84,8 @@ struct nsjconf_t {
        gid_t initial_gid;
        unsigned int max_conns_per_ip;
         LIST_HEAD(pidslist, pids_t) pids;
-        LIST_HEAD(bindmountptslist, constchar_t) bindmountpts;
+        LIST_HEAD(rwbindmountptslist, constchar_t) rwbindmountpts;
+        LIST_HEAD(robindmountptslist, constchar_t) robindmountpts;
         LIST_HEAD(tmpfsmountptslist, constchar_t) tmpfsmountpts;
 };
 
index 4a8951f6085a1add7dc6576b3d852aa6d8ade197..f5a18a1446ee8999fab49aad5f6003b6e1d72542 100644 (file)
--- a/contain.c
+++ b/contain.c
@@ -174,6 +174,67 @@ bool containPrepareEnv(struct nsjconf_t * nsjconf)
        return true;
 }
 
+/* findSpecDestination mutates spec (source:dest) to have a null byte instead
+ * of ':' in between source and dest, then returns a pointer to the dest
+ * string. */
+static char *findSpecDestination(char *spec) {
+       char *dest = spec;
+       while (*dest != ':' && *dest != '\0') {
+               dest++;
+       }
+
+       switch (*dest) {
+       case ':':
+               *dest = '\0';
+               return dest + 1;
+       case '\0':
+               return spec;
+       default:
+               // not reached
+               return spec;
+       }
+}
+
+static bool bindMount(const char *newrootdir, const char *spec) {
+       char mount_pt[PATH_MAX];
+       bool success = false;
+       char *source = strdup(spec);
+       char *dest = findSpecDestination(source);
+
+       snprintf(mount_pt, sizeof(mount_pt), "%s/%s", newrootdir, dest);
+       if (mkdir(mount_pt, 0700) == -1 && errno != EEXIST) {
+               PLOG_E("mkdir('%s')", mount_pt);
+               goto cleanup;
+       }
+       LOG_D("Mounting (bind) '%s' on '%s'", source, mount_pt);
+       if (mount(source, mount_pt, NULL, MS_BIND | MS_REC, NULL) == -1) {
+               PLOG_E("mount('%s', '%s', MS_BIND|MS_REC)", source, mount_pt);
+               goto cleanup;
+       }
+       success = true;
+
+cleanup:
+       free(source);
+       return success;
+}
+
+static bool remountBindMount(const char *spec, unsigned long flags) {
+       bool success = false;
+       char *source = strdup(spec);
+       char *dest = findSpecDestination(source);
+
+       LOG_D("Remounting (bind|%lu) '%s' on '%s'", flags, dest, dest);
+       if (mount(dest, dest, NULL, MS_BIND | MS_NOSUID | MS_NODEV | MS_REMOUNT | MS_PRIVATE | flags, NULL) != 0) {
+               PLOG_E("mount('%s', '%s', MS_BIND|MS_NOSUID|MS_NODEV|MS_REMOUNT|MS_PRIVATE|%lu)", dest, dest, flags);
+               goto cleanup;
+       }
+       success = true;
+
+cleanup:
+       free(source);
+       return success;
+}
+
 bool containMountFS(struct nsjconf_t * nsjconf)
 {
        const char *destdir = "/tmp";
@@ -184,7 +245,7 @@ bool containMountFS(struct nsjconf_t * nsjconf)
        char newrootdir[PATH_MAX];
        snprintf(newrootdir, sizeof(newrootdir), "%s/%s", destdir, "new_root");
        if (mkdir(newrootdir, 0755) == -1) {
-               PLOG_E("mkdir(/tmp/new_root");
+               PLOG_E("mkdir(/tmp/new_root)");
                return false;
        }
        if (mount(nsjconf->chroot, newrootdir, NULL, MS_BIND | MS_REC, NULL) == -1) {
@@ -193,16 +254,13 @@ bool containMountFS(struct nsjconf_t * nsjconf)
        }
 
        struct constchar_t *p;
-       char mount_pt[PATH_MAX];
-       LIST_FOREACH(p, &nsjconf->bindmountpts, pointers) {
-               snprintf(mount_pt, sizeof(mount_pt), "%s/%s", newrootdir, p->value);
-               if (mkdir(mount_pt, 0700) == -1 && errno != EEXIST) {
-                       PLOG_E("mkdir('%s')", mount_pt);
+       LIST_FOREACH(p, &nsjconf->robindmountpts, pointers) {
+               if (!bindMount(newrootdir, p->value)) {
                        return false;
                }
-               LOG_D("Mounting (bind) '%s' on '%s'", p->value, mount_pt);
-               if (mount(p->value, mount_pt, NULL, MS_BIND | MS_REC, NULL) == -1) {
-                       PLOG_E("mount('%s', '%s', MS_BIND|MS_REC", p->value, mount_pt);
+       }
+       LIST_FOREACH(p, &nsjconf->rwbindmountpts, pointers) {
+               if (!bindMount(newrootdir, p->value)) {
                        return false;
                }
        }
@@ -236,10 +294,14 @@ bool containMountFS(struct nsjconf_t * nsjconf)
                PLOG_E("chdir('/')");
                return false;
        }
-       /* It only makes sense with "--chroot /", so don't worry about erorrs */
+       /* It only makes sense with "--chroot /", so don't worry about errors */
        umount2(destdir, MNT_DETACH);
 
        LIST_FOREACH(p, &nsjconf->tmpfsmountpts, pointers) {
+               if (strchr(p->value, ':') != NULL) {
+                       PLOG_E("invalid tmpfs mount spec. source:dest format unsupported.");
+                       return false;
+               }
                if (mkdir(p->value, 0700) == -1 && errno != EEXIST) {
                        PLOG_E("mkdir('%s'); You probably need to create it in your --chroot ('%s') directory",
                               p->value, nsjconf->chroot);
@@ -261,6 +323,18 @@ bool containMountFS(struct nsjconf_t * nsjconf)
                }
        }
 
+       LIST_FOREACH(p, &nsjconf->robindmountpts, pointers) {
+               if (!remountBindMount(p->value, MS_RDONLY)) {
+                       return false;
+               }
+       }
+       LIST_FOREACH(p, &nsjconf->rwbindmountpts, pointers) {
+               if (!remountBindMount(p->value, 0)) {
+                       return false;
+               }
+       }
+
+
        return true;
 }