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 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list