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);
/* *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";
{{"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},
};
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));
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':
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";
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) {
}
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;
}
}
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);
}
}
+ 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;
}