[lxc-devel] Patch/RFC: allow pivot_root, unmount old fs
Michael Holzt
lxc at my.fqdn.org
Tue Jan 5 05:31:14 UTC 2010
Hello everyone,
i've written another (experimental) patch and would like comments on it.
lxc currently does a chroot into the target rootfs. chroot is insecure and
can easily be broken, as demonstrated here:
| root at synergy:~# touch /this_is_the_realrootfs_ouch
| # touch /container/webhost/this_is_the_container
| # lxc-start -n webhost /bin/sh
| # ls this*
| this_is_the_container
| # ./breakchroot
| # ls this*
| this_is_the_realrootfs_ouch
code to break chroot taken from
http://www.bpfh.net/simes/computing/chroot-break.html
Now this can be fixed. As our container has his own mount namespace, we can
easily pivot_root into the rootfs and then unmount all old mounts. The patch
attached add a new config keyword which contains the path to a temporary
mount for the old rootfs (inside the container). This stops the chroot break
method shown before.
Example:
| root at synergy:~# grep pivotdir /var/lib/lxc/webhost/config
| lxc.pivotdir = /oldrootfs
| root at synergy:~# ls -lad /container/webhost/oldrootfs
| drwxr-xr-x 2 root root 4096 2010-01-02 03:59 /container/webhost/oldrootfs
| root at synergy:~# lxc-start -n webhost /bin/sh
| # mount -t proc proc /proc
| # cat /proc/mounts
| rootfs / rootfs rw 0 0
| /dev/root / ext3 rw,relatime,errors=remount-ro,data=writeback 0 0
| devpts /dev/console devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
| proc /proc proc rw,relatime 0 0
| # ls this*
| this_is_the_container
| # ./breakchroot
| # ls this*
| this_is_the_container
The patch is somewhat ugly for now, but i think this feature is very
usefull and this approach should be implemented.
Please try and comment!
Regards,
Michael
--
It's an insane world, but i'm proud to be a part of it. -- Bill Hicks
-------------- next part --------------
diff -Naur lxc-0.6.4/src/lxc/conf.c lxc-0.6.4-pivot/src/lxc/conf.c
--- lxc-0.6.4/src/lxc/conf.c 2009-11-24 09:47:26.000000000 +0100
+++ lxc-0.6.4-pivot/src/lxc/conf.c 2010-01-05 06:18:16.000000000 +0100
@@ -65,6 +65,8 @@
#define MS_REC 16384
#endif
+extern int pivot_root(const char * new_root, const char * put_old);
+
typedef int (*instanciate_cb)(struct lxc_netdev *);
struct mount_opt {
@@ -334,7 +336,108 @@
return 0;
}
-static int setup_rootfs(const char *rootfs)
+static int setup_pivot_root(const char *rootfs, const char *pivotdir)
+{
+ char path[MAXPATHLEN];
+ FILE *mounts;
+ struct lxc_list mountlist, *listentry, *iterator;
+ char buf[1000], *mountpoint, *p;
+ int counter, oldcounter;
+
+ chdir(rootfs);
+
+ snprintf(path, sizeof(path), ".%s", pivotdir);
+ if (pivot_root(".", path)) {
+ SYSERROR("pivot_root syscall failed");
+ return -1;
+ }
+
+ chdir("/");
+
+ DEBUG("pivot_root syscall successfull");
+
+ /* open /proc/mounts in old rootfs */
+ snprintf(path, sizeof(path), "/%s/proc/mounts", pivotdir);
+ mounts = fopen(path, "r");
+ if ( !mounts ) {
+ SYSERROR("Can't open '%s'", path);
+ return -1;
+ }
+
+ /* parse mounts */
+ snprintf(path, sizeof(path), "%s/", pivotdir);
+
+ lxc_list_init(&mountlist);
+ while (!feof(mounts)) {
+ fgets(buf, sizeof(buf), mounts);
+
+ /* strtok does not work here. why? */
+ p = buf;
+ while ( *p++ != ' ' ) ;
+ mountpoint = p;
+ while ( *++p != ' ' );
+ *p = 0;
+
+ /* only mounts in old rootfs */
+ if (!strncmp(mountpoint, path, strlen(path))) {
+
+ /* first try to unmount right now*/
+ if (!umount(mountpoint)) {
+ DEBUG("unmounted %s", mountpoint);
+ }
+
+ /* when failed, put on list */
+ else {
+ DEBUG("mounted: %s", mountpoint);
+ listentry = malloc(sizeof(*listentry));
+ if (!listentry) {
+ SYSERROR("malloc for mountpoint list");
+ return -1;
+ }
+
+ listentry->elem = strdup(mountpoint);
+ lxc_list_add_tail(&mountlist, listentry);
+ }
+ }
+ }
+
+ fclose(mounts);
+
+ /* process list if not empty */
+ if (!lxc_list_empty(&mountlist)) {
+ counter = 0;
+
+ /* try unmount until all done or list no longer shrinks */
+ do {
+ oldcounter = 0;
+ counter = 0;
+ lxc_list_for_each(iterator, &mountlist) {
+ counter++;
+ if (!umount(iterator->elem)) {
+ DEBUG("unmounted %s", iterator->elem);
+ lxc_list_del(iterator);
+ }
+ }
+
+ } while ( counter > 0 && counter != oldcounter );
+
+ if ( counter > 0 ) {
+ ERROR("could not unmount all old mountd");
+ return -1;
+ }
+
+ }
+
+ /* unmount old rootfs */
+ if (umount(pivotdir)) {
+ SYSERROR("could not unmount old rootfs");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int setup_rootfs(const char *rootfs, const char *pivotdir)
{
char *tmpname;
int ret = -1;
@@ -358,18 +461,28 @@
goto out;
}
- if (chroot(tmpname)) {
- SYSERROR("failed to set chroot %s", tmpname);
- goto out;
- }
+ if (pivotdir) {
+ if (setup_pivot_root(tmpname,pivotdir)) {
+ ERROR("failed to pivot_root to '%s' (put_old: '%s')", rootfs, pivotdir);
+ goto out;
+ }
- if (chdir(getenv("HOME")) && chdir("/")) {
- SYSERROR("failed to change to home directory");
- goto out;
+ INFO("pivoted to '%s'", rootfs);
}
+ else {
+ if (chroot(tmpname)) {
+ SYSERROR("failed to set chroot %s", tmpname);
+ goto out;
+ }
- INFO("chrooted to '%s'", rootfs);
+ if (chdir(getenv("HOME")) && chdir("/")) {
+ SYSERROR("failed to change to home directory");
+ goto out;
+ }
+ INFO("chrooted to '%s'", rootfs);
+ }
+
ret = 0;
out:
rmdir(tmpname);
@@ -816,6 +929,7 @@
int lxc_conf_init(struct lxc_conf *conf)
{
conf->rootfs = NULL;
+ conf->pivotdir = NULL;
conf->fstab = NULL;
conf->utsname = NULL;
conf->tty = 0;
@@ -1065,7 +1179,7 @@
ERROR("failed to setup the cgroups for '%s'", name);
return -1;
}
-
+
if (setup_mount(lxc_conf->fstab)) {
ERROR("failed to setup the mounts for '%s'", name);
return -1;
@@ -1076,6 +1190,7 @@
return -1;
}
+
if (setup_console(lxc_conf->rootfs, lxc_conf->console)) {
ERROR("failed to setup the console for '%s'", name);
return -1;
@@ -1086,7 +1201,7 @@
return -1;
}
- if (setup_rootfs(lxc_conf->rootfs)) {
+ if (setup_rootfs(lxc_conf->rootfs, lxc_conf->pivotdir)) {
ERROR("failed to set rootfs for '%s'", name);
return -1;
}
diff -Naur lxc-0.6.4/src/lxc/conf.h lxc-0.6.4-pivot/src/lxc/conf.h
--- lxc-0.6.4/src/lxc/conf.h 2009-11-19 15:06:02.000000000 +0100
+++ lxc-0.6.4-pivot/src/lxc/conf.h 2010-01-04 22:58:50.000000000 +0100
@@ -133,9 +133,11 @@
*/
struct lxc_conf {
char *rootfs;
+ char *pivotdir;
char *fstab;
int tty;
int pts;
+ int do_pivot;
struct utsname *utsname;
struct lxc_list cgroup;
struct lxc_list network;
@@ -167,6 +169,7 @@
#define conf_has_fstab(__name) conf_has(__name, "fstab")
#define conf_has_rootfs(__name) conf_has(__name, "rootfs")
+#define conf_has_pivotdir(__name) conf_has(__name, "pivotdir")
#define conf_has_utsname(__name) conf_has(__name, "utsname")
#define conf_has_network(__name) conf_has(__name, "network")
#define conf_has_console(__name) conf_has(__name, "console")
diff -Naur lxc-0.6.4/src/lxc/confile.c lxc-0.6.4-pivot/src/lxc/confile.c
--- lxc-0.6.4/src/lxc/confile.c 2009-11-19 15:06:02.000000000 +0100
+++ lxc-0.6.4-pivot/src/lxc/confile.c 2010-01-04 22:59:18.000000000 +0100
@@ -44,6 +44,7 @@
static int config_cgroup(const char *, char *, struct lxc_conf *);
static int config_mount(const char *, char *, struct lxc_conf *);
static int config_rootfs(const char *, char *, struct lxc_conf *);
+static int config_pivotdir(const char *, char *, struct lxc_conf *);
static int config_utsname(const char *, char *, struct lxc_conf *);
static int config_network_type(const char *, char *, struct lxc_conf *);
static int config_network_flags(const char *, char *, struct lxc_conf *);
@@ -68,6 +69,7 @@
{ "lxc.cgroup", config_cgroup },
{ "lxc.mount", config_mount },
{ "lxc.rootfs", config_rootfs },
+ { "lxc.pivotdir", config_pivotdir },
{ "lxc.utsname", config_utsname },
{ "lxc.network.type", config_network_type },
{ "lxc.network.flags", config_network_flags },
@@ -494,6 +496,22 @@
return 0;
}
+static int config_pivotdir(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+ if (strlen(value) >= MAXPATHLEN) {
+ ERROR("%s path is too long", value);
+ return -1;
+ }
+
+ lxc_conf->pivotdir = strdup(value);
+ if (!lxc_conf->rootfs) {
+ SYSERROR("failed to duplicate string %s", value);
+ return -1;
+ }
+
+ return 0;
+}
+
static int config_utsname(const char *key, char *value, struct lxc_conf *lxc_conf)
{
struct utsname *utsname;
More information about the lxc-devel
mailing list