[virt-tools-list] [virt-bootstrap] [PATCH v3 05/12] bootstrap: Implement UID/GID mapping

Radostin Stoyanov rstoyanov1 at gmail.com
Thu Jul 20 11:29:40 UTC 2017


When Libvirt creates LXC container with enabled user namespace the
ownership of files in the container should be mapped to the specified
target UID/GID.

Files and directories in the root file system are mapped as foll:

New file's UID/GID = Target UID/GID + File's UID/GID - User's UID/GID

Example:
- User which creates container:        uid/gid = 0
- File extracted from container image: uid/gid = 0
- Target:                              uid/gid = 1000

New file's uid/gid: 1000 = 1000 + 0 - 0

The new file's uid/gid will be 1000 on the host and 0 inside container
with:

```xml
    <idmap>
      <uid start='0' target='1000' count='10'/>
      <gid start='0' target='1000' count='10'/>
    </idmap>
```
---
 src/virtBootstrap/virt_bootstrap.py | 53 +++++++++++++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 5 deletions(-)

diff --git a/src/virtBootstrap/virt_bootstrap.py b/src/virtBootstrap/virt_bootstrap.py
index 98c629a..588d1f7 100755
--- a/src/virtBootstrap/virt_bootstrap.py
+++ b/src/virtBootstrap/virt_bootstrap.py
@@ -69,12 +69,36 @@ def get_source(source_type):
         raise Exception("Invalid image URL scheme: '%s'" % source_type)
 
 
+def map_id(path, map_uid, map_gid):
+    """
+    Map the ownership of files in the root file system
+
+    New file's UID/GID = Mapped UID/GID + File's UID/GID - User's UID/GID
+    """
+    user_uid = os.geteuid()
+    user_gid = os.getegid()
+    path = os.path.realpath(path)
+
+    for root, _ignore, files in os.walk(path):
+        for name in [root] + files:
+
+            filepath = os.path.join(root, name)
+            stat_info = os.lstat(filepath)
+            file_uid = stat_info.st_uid
+            file_gid = stat_info.st_gid
+
+            new_uid = map_uid + (file_uid - user_uid)
+            new_gid = map_gid + (file_gid - user_gid)
+            os.lchown(filepath, new_uid, new_gid)
+
+
 # pylint: disable=too-many-arguments
 def bootstrap(uri, dest,
               fmt='dir',
               username=None,
               password=None,
               root_password=None,
+              idmap=None,
               not_secure=False,
               no_cache=False,
               progress_cb=None):
@@ -104,9 +128,14 @@ def bootstrap(uri, dest,
            no_cache=no_cache,
            progress=prog).unpack(dest)
 
-    if fmt == "dir" and root_password is not None:
-        logger.info("Setting password of the root account")
-        utils.set_root_password(dest, root_password)
+    if fmt == "dir":
+        if root_password is not None:
+            logger.info("Setting password of the root account")
+            utils.set_root_password(dest, root_password)
+
+        if idmap is not None:
+            logger.info("Mapping UID/GID")
+            map_id(dest, *idmap)
 
 
 def set_logging_conf(loglevel=None):
@@ -157,6 +186,8 @@ def main():
                         help=_("Password for accessing the source registry"))
     parser.add_argument("--root-password", default=None,
                         help=_("Root password to set in the created rootfs"))
+    parser.add_argument("-i", "--idmap", default=None,
+                        help=_("UID / GID mapping of the created rootfs"))
     parser.add_argument("--no-cache", action="store_true",
                         help=_("Do not store downloaded Docker images"))
     parser.add_argument("-f", "--format", default='dir',
@@ -173,8 +204,6 @@ def main():
                         help=_("Show only the current status and progress"
                                "of virt-bootstrap"))
 
-    # TODO add UID / GID mapping parameters
-
     try:
         args = parser.parse_args()
 
@@ -182,6 +211,19 @@ def main():
             # Configure logging lovel/format
             set_logging_conf(args.loglevel)
 
+        idmap = None
+        if args.idmap:
+            try:
+                uid, gid = args.idmap.split(':')
+                idmap = (int(uid), int(gid))
+            except Exception:
+                msg = ("Invalid UID / GID mapping value.\n"
+                       "Supported format:\n\t"
+                       "<uid>:<gid>\n"
+                       "Example:\n\t"
+                       "virt-bootstrap docker://fedora /tmp/foo -i 1000:1000")
+                raise ValueError(msg)
+
         # do the job here!
         bootstrap(uri=args.uri,
                   dest=args.dest,
@@ -189,6 +231,7 @@ def main():
                   username=args.username,
                   password=args.password,
                   root_password=args.root_password,
+                  idmap=idmap,
                   not_secure=args.not_secure,
                   no_cache=args.no_cache,
                   progress_cb=args.status_only)
-- 
2.9.4




More information about the virt-tools-list mailing list