Improve bindmount interface.

Now supports readonly bindmounts and
differentiating between source and target path.
This commit is contained in:
JT Olds 2015-07-07 11:15:37 -06:00
parent 459a220133
commit 821eb78054
3 changed files with 107 additions and 17 deletions

View File

@ -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':

View File

@ -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;
};

View File

@ -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;
}