[virt-tools-list] [virt-bootstrap] [PATCH v2 10/14] Optimize setting of root password in qcow2 image

Radostin Stoyanov rstoyanov1 at gmail.com
Tue Aug 1 11:28:51 UTC 2017


Use the python module of libguestfs to set the root password when
creating images with qcow2 format.
---
 src/virtBootstrap/sources/docker_source.py |  4 +-
 src/virtBootstrap/sources/file_source.py   |  4 +-
 src/virtBootstrap/utils.py                 | 63 ++++++++++++++++++++++++------
 src/virtBootstrap/virt_bootstrap.py        | 14 ++++---
 4 files changed, 66 insertions(+), 19 deletions(-)

diff --git a/src/virtBootstrap/sources/docker_source.py b/src/virtBootstrap/sources/docker_source.py
index 0ca3b20..74dc548 100644
--- a/src/virtBootstrap/sources/docker_source.py
+++ b/src/virtBootstrap/sources/docker_source.py
@@ -60,6 +60,7 @@ class DockerSource(object):
         self.password = kwargs['password']
         self.uid_map = kwargs['uid_map']
         self.gid_map = kwargs['gid_map']
+        self.root_password = kwargs['root_password']
         self.output_format = kwargs['fmt']
         self.insecure = kwargs['not_secure']
         self.no_cache = kwargs['no_cache']
@@ -265,7 +266,8 @@ class DockerSource(object):
                 self.progress("Extracting container layers into qcow2 images",
                               value=50, logger=logger)
                 utils.Build_QCOW2_Image(self.tar_files, dest, self.progress,
-                                        self.uid_map, self.gid_map)
+                                        self.uid_map, self.gid_map,
+                                        self.root_password)
             else:
                 raise Exception("Unknown format:" + self.output_format)
 
diff --git a/src/virtBootstrap/sources/file_source.py b/src/virtBootstrap/sources/file_source.py
index 748181f..4e73da2 100644
--- a/src/virtBootstrap/sources/file_source.py
+++ b/src/virtBootstrap/sources/file_source.py
@@ -46,6 +46,7 @@ class FileSource(object):
         self.output_format = kwargs['fmt']
         self.uid_map = kwargs['uid_map']
         self.gid_map = kwargs['gid_map']
+        self.root_password = kwargs['root_password']
         self.progress = kwargs['progress'].update_progress
 
     def unpack(self, dest):
@@ -66,7 +67,8 @@ class FileSource(object):
             self.progress("Extracting files into qcow2 image", value=0,
                           logger=logger)
             utils.Build_QCOW2_Image([self.path], dest, self.progress,
-                                    self.uid_map, self.gid_map)
+                                    self.uid_map, self.gid_map,
+                                    self.root_password)
         else:
             raise Exception("Unknown format:" + self.output_format)
 
diff --git a/src/virtBootstrap/utils.py b/src/virtBootstrap/utils.py
index 7bcffa4..6cfe63c 100644
--- a/src/virtBootstrap/utils.py
+++ b/src/virtBootstrap/utils.py
@@ -57,7 +57,8 @@ class Build_QCOW2_Image(object):
     """
     Create qcow2 image with backing chains from list of tar files.
     """
-    def __init__(self, tar_files, dest, progress, uid_map=None, gid_map=None):
+    def __init__(self, tar_files, dest, progress, uid_map=None, gid_map=None,
+                 root_password=None):
         """
         Initialize guestfs
         """
@@ -68,6 +69,7 @@ class Build_QCOW2_Image(object):
         self.progress = progress
         self.uid_map = uid_map
         self.gid_map = gid_map
+        self.root_password = root_password
         self.fmt = 'qcow2'
         self.qcow2_files = [os.path.join(dest, 'layer-%s.qcow2' % i)
                             for i in range(self.nlayers)]
@@ -76,6 +78,18 @@ class Build_QCOW2_Image(object):
         self.create_base_qcow2_layer(self.tar_files[0], self.qcow2_files[0])
         if len(self.tar_files) > 1:
             self.create_backing_chains()
+        elif self.root_password is not None:
+            # Add base disk and launch
+            self.g.add_drive_opts(
+                self.qcow2_files[0],
+                readonly=False,
+                format=self.fmt
+            )
+            self.g.launch()
+
+        # Set root password
+        if self.root_password is not None:
+            self.set_root_password()
         self.g.shutdown()
 
     def create_and_add_disk(self, qcow2_file, backingfile=None,
@@ -150,6 +164,36 @@ class Build_QCOW2_Image(object):
             self.tar_in(tar_file, devices[i])
 
 
+    def set_root_password(self):
+        """
+        Set root password in the shadow file of image.
+
+        Mount the last the layer to update the shadow file with the
+        hash for the root password.
+        """
+        self.progress("Setting root password", logger=logger)
+
+        last_layer_dev = self.g.list_devices()[-1]
+        self.g.mount(last_layer_dev, '/')
+
+        if not self.g.is_file('/etc/shadow'):
+            logger.error('showfile was not found in this image')
+            return
+
+        shadow_content = self.g.read_file('/etc/shadow').split('\n')
+
+        if not shadow_content:
+            logger.error('showfile was empty')
+            return
+
+        new_shadow_content = set_password_in_shadow_content(
+            shadow_content,
+            self.root_password
+        )
+        self.g.write('/etc/shadow', '\n'.join(new_shadow_content))
+        self.g.umount('/')
+
+
     def map_id(self, tar_members, map_uid, map_gid):
         """
         Remapping ownership of all files inside image.
@@ -172,6 +216,13 @@ class Build_QCOW2_Image(object):
                 self.g.lchown(new_uid, new_gid, os.path.join('/', member.name))
 
 
+def get_random_string(n=6):
+    """
+    Return random string of lowercase characters with lenght n.
+    """
+    return ''.join(random.choice(string.ascii_lowercase) for _ in range(n))
+
+
 def get_compression_type(tar_file):
     """
     Get compression type of tar file.
@@ -447,16 +498,6 @@ def set_root_password_in_rootfs(rootfs, password):
         os.chmod(shadow_file, shadow_file_permissions)
 
 
-def set_root_password_in_image(image, password):
-    """
-    Set password on the root user within image
-    """
-    password_hash = passlib.hosts.linux_context.hash(password)
-    execute(['virt-edit',
-             '-a', image, '/etc/shadow',
-             '-e', 's,^root:.*?:,root:%s:,' % re.escape(password_hash)])
-
-
 def set_root_password(fmt, dest, root_password):
     """
     Set root password
diff --git a/src/virtBootstrap/virt_bootstrap.py b/src/virtBootstrap/virt_bootstrap.py
index 54025d2..3cc7edb 100755
--- a/src/virtBootstrap/virt_bootstrap.py
+++ b/src/virtBootstrap/virt_bootstrap.py
@@ -128,15 +128,17 @@ def bootstrap(uri, dest,
            gid_map=gid_map,
            not_secure=not_secure,
            no_cache=no_cache,
+           root_password=root_password,
            progress=prog).unpack(dest)
 
-    if root_password is not None:
-        logger.info("Setting password of the root account")
-        utils.set_root_password(fmt, dest, root_password)
+    if fmt == "dir":
+        if root_password is not None:
+            logger.info("Setting password of the root account")
+            utils.set_root_password_in_rootfs(dest, root_password)
 
-    if fmt == "dir" and uid_map or gid_map:
-        logger.info("Mapping UID/GID")
-        utils.mapping_uid_gid(dest, uid_map, gid_map)
+        if uid_map or gid_map:
+            logger.info("Mapping UID/GID")
+            utils.mapping_uid_gid(dest, uid_map, gid_map)
 
 
 def set_logging_conf(loglevel=None):
-- 
2.13.3




More information about the virt-tools-list mailing list