[lxc-devel] [PATCH RFC] create_run_template: tell the template what caller's uid was mapped to

Serge Hallyn serge.hallyn at ubuntu.com
Mon Nov 4 17:18:05 UTC 2013


This is still RFC at the conceptual level.

Problem: when creating containers unprivileged, we run the template in a
new user ns.  For instance if I have

lxc.id_map = u 0 100000 10000
lxc.id_map = g 0 100000 10000

in my lxc.conf and do
   lxc-create -f lxc.conf -P ~/lxcbase -t ubuntu-cloud -n a1,
then the ubuntu-cloud template is run as root in a user ns where
root is mapped to 100000 on the host.  Any files it creates are
owned by uid 100000, which can become inconvenient.

I've considered splitting the templates up into two separate runs.  One
run as the calling uid in the host uid namespace to fetch and cache the
image/tarball/whatever.  The next, in a mapped user namespace as root in
that namespace, to only extract and configure the container rootfs.

What I'm doing here is passing the namespace uid to which the caller's
uid (on the host) is mapped, to the template.  The ubuntu-cloud template
then chowns the cached images to that user.  This doesn't clean
everything up perfectly - the ubuntu-cloudimg-query has cached some
info as well.

# ls -l .cache
drwxrwxr-x 5 serge  100000 4096 Nov  4 18:03 lxc
-rw-r--r-- 1 serge  serge     0 Oct 21 21:21 motd.legal-displayed
drwxrwxr-x 2 100000 100000 4096 Nov  4 17:58 ubuntu-cloudimg-query

This suggests that there will always be side effects resulting in
mis-owned files, and trying to fix those will just become unmaintainable
in itself.

I do also have a patch which splits the template calls into two (one
for caching), which I wrote on friday, but I wasn't happy with that
either.  So I'm sending this out for comment.  Failing any brilliant
ideas, I will probably combine the two patches and just aim for the
cleanest result with ubuntu-cloud and cirros templates.

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 src/lxc/conf.c                |  6 +++---
 src/lxc/conf.h                |  2 +-
 src/lxc/lxccontainer.c        | 38 ++++++++++++++++++++++++++++++--------
 templates/lxc-ubuntu-cloud.in | 19 +++++++++++++++++--
 4 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 2a47e77..afdaa14 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2912,7 +2912,7 @@ uid_t get_mapped_rootid(struct lxc_conf *conf)
 	return (uid_t)-1;
 }
 
-bool hostid_is_mapped(int id, struct lxc_conf *conf)
+int mapped_hostid(int id, struct lxc_conf *conf)
 {
 	struct lxc_list *it;
 	struct id_map *map;
@@ -2921,9 +2921,9 @@ bool hostid_is_mapped(int id, struct lxc_conf *conf)
 		if (map->idtype != ID_TYPE_UID)
 			continue;
 		if (id >= map->hostid && id < map->hostid + map->range)
-			return true;
+			return (id - map->hostid) + map->nsid;
 	}
-	return false;
+	return -1;
 }
 
 int find_unmapped_nsuid(struct lxc_conf *conf)
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 71399b9..940d493 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -362,7 +362,7 @@ extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf);
 
 extern uid_t get_mapped_rootid(struct lxc_conf *conf);
 extern int find_unmapped_nsuid(struct lxc_conf *conf);
-extern bool hostid_is_mapped(int id, struct lxc_conf *conf);
+extern int mapped_hostid(int id, struct lxc_conf *conf);
 extern int chown_mapped_root(char *path, struct lxc_conf *conf);
 extern int ttys_shift_ids(struct lxc_conf *c);
 #endif
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 946133d..594a96d 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -916,20 +916,28 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
 		 * If we're running the template in a mapped userns, then
 		 * we prepend the template command with:
 		 * lxc-usernsexec <-m map1> ... <-m mapn> --
+		 * and we append "--mapped-uid x", where x is the mapped uid
+		 * for our geteuid()
 		 */
 		if (geteuid() != 0 && !lxc_list_empty(&conf->id_map)) {
 			int n2args = 1;
+			char txtuid[20];
 			char **n2 = malloc(n2args * sizeof(*n2));
 			struct lxc_list *it;
 			struct id_map *map;
 
+			if (!n2) {
+				SYSERROR("out of memory");
+				exit(1);
+			}
 			newargv[0] = tpath;
 			tpath = "lxc-usernsexec";
 			n2[0] = "lxc-usernsexec";
 			lxc_list_for_each(it, &conf->id_map) {
 				map = it->elem;
 				n2args += 2;
-				n2 = realloc(n2, n2args * sizeof(*n2));
+				n2 = realloc(n2, n2args * sizeof(char *));
+INFO("allocated %d items to n2", n2args);
 				if (!n2)
 					exit(1);
 				n2[n2args-2] = "-m";
@@ -942,15 +950,15 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
 				if (ret < 0 || ret >= 200)
 					exit(1);
 			}
-			bool hostid_mapped = hostid_is_mapped(geteuid(), conf);
-			int extraargs = hostid_mapped ?  1 : 3;
-			n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(*n2));
+			int hostid_mapped = mapped_hostid(geteuid(), conf);
+			int extraargs = hostid_mapped >= 0 ?  1 : 3;
+			n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *));
 			if (!n2)
 				exit(1);
-			if (!hostid_mapped) {
-				int free_id = find_unmapped_nsuid(conf);
+			if (hostid_mapped < 0) {
+				hostid_mapped = find_unmapped_nsuid(conf);
 				n2[n2args++] = "-m";
-				if (free_id < 0) {
+				if (hostid_mapped < 0) {
 					ERROR("Could not find free uid to map");
 					exit(1);
 				}
@@ -960,7 +968,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
 					exit(1);
 				}
 				ret = snprintf(n2[n2args-1], 200, "u:%d:%d:1",
-					free_id, geteuid());
+					hostid_mapped, geteuid());
 				if (ret < 0 || ret >= 200) {
 					ERROR("string too long");
 					exit(1);
@@ -969,6 +977,20 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
 			n2[n2args++] = "--";
 			for (i = 0; i < nargs; i++)
 				n2[i + n2args] = newargv[i];
+			n2args += nargs;
+			// Finally add "--mapped-uid $uid" to tell template what to chown
+			// cached images to
+			n2args += 2;
+			n2 = realloc(n2, n2args * sizeof(char *));
+			if (!n2) {
+				SYSERROR("out of memory");
+				exit(1);
+			}
+			// note n2[n2args-1] is NULL
+			n2[n2args-3] = "--mapped-uid";
+			snprintf(txtuid, 20, "%d", hostid_mapped);
+			n2[n2args-2] = txtuid;
+			n2[n2args-1] = NULL;
 			free(newargv);
 			newargv = n2;
 		}
diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in
index 82a7f74..fd395b4 100644
--- a/templates/lxc-ubuntu-cloud.in
+++ b/templates/lxc-ubuntu-cloud.in
@@ -161,13 +161,14 @@ EOF
     return 0
 }
 
-options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,rootfs:,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata: -- "$@")
+options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,rootfs:,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata:,mapped-uid: -- "$@")
 if [ $? -ne 0 ]; then
     usage $(basename $0)
     exit 1
 fi
 eval set -- "$options"
 
+mapped_uid=-1
 # default release is precise, or the systems release if recognized
 release=precise
 if [ -f /etc/lsb-release ]; then
@@ -224,11 +225,13 @@ do
     -u|--userdata)     cloneargs[${#cloneargs[@]}]="--userdata=$2"; shift 2;;
     -C|--cloud)        cloneargs[${#cloneargs[@]}]="--cloud"; shift 1;;
     -S|--auth-key)     cloneargs[${#cloneargs[@]}]="--auth-key=$2"; shift 2;;
+    --mapped-uid)      mapped_uid=$2; shift 2;;
     --)                shift 1; break ;;
         *)              break ;;
     esac
 done
 
+echo "mapped_uid is .$mapped_uid."
 cloneargs=( "--name=$name" "${cloneargs[@]}" )
 
 if [ $debug -eq 1 ]; then
@@ -296,6 +299,8 @@ type wget
 # determine the url, tarball, and directory names
 # download if needed
 cache="$STATE_DIR/cache/lxc/cloud-$release"
+STATE_DIR="$HOME/.cache/lxc/"
+cache="$HOME/.cache/lxc/cloud-$release"
 
 mkdir -p $cache
 
@@ -371,7 +376,11 @@ do_extract_rootfs() {
     echo "Extracting container rootfs"
     mkdir -p $rootfs
     cd $rootfs
-    tar --numeric-owner -xpzf "$cache/$filename"
+    if [ $in_userns -eq 1 ]; then
+        tar --anchored --exclude="dev/*" --numeric-owner -xpzf "$cache/$filename"
+    else
+        tar --numeric-owner -xpzf "$cache/$filename"
+    fi
 }
 
 if [ -n "$tarball" ]; then
@@ -388,6 +397,12 @@ copy_configuration $path $rootfs $name $arch $release
 
 "$CLONE_HOOK_FN" "${cloneargs[@]}" "$rootfs"
 
+if [ $mapped_uid -ne -1 ]; then
+	chown $mapped_uid $path/config
+	chown -R $mapped_uid $STATE_DIR
+	chown -R $mapped_uid $cache
+fi
+
 echo "Container $name created."
 exit 0
 
-- 
1.8.3.2





More information about the lxc-devel mailing list