[virt-tools-list] [virt-bootstrap] [PATCH v3 04/12] set_root_password: Replace chpasswd with script

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


These changes aim to avoid the requirement for root privileges when
setting the password of root user on root file system.

The "-R, --root" flag of chpasswd is using chroot to apply changes in
root file system and this requires root privileges. [1]

Instead compute hash of the root password using passlib [2] and insert
the value in the /etc/shadow file in the rootfs.

[1] https://en.wikipedia.org/wiki/Chroot#Limitations
[2] http://passlib.readthedocs.io/en/stable/lib/passlib.hosts.html
---
 setup.py                            |  5 +++++
 src/virtBootstrap/utils.py          | 33 +++++++++++++++++++++++++++++++++
 src/virtBootstrap/virt_bootstrap.py | 18 +++---------------
 3 files changed, 41 insertions(+), 15 deletions(-)

diff --git a/setup.py b/setup.py
index 70e3e03..b2e17ac 100755
--- a/setup.py
+++ b/setup.py
@@ -112,6 +112,11 @@ setup(
     cmdclass={
         'pylint': CheckPylint
     },
+
+    # virt-bootstrap uses passlib to compute the hash of
+    # root password for root file system.
+    install_requires=['passlib>=1.7.1'],
+
     extras_require={
         'dev': [
             'pylint',
diff --git a/src/virtBootstrap/utils.py b/src/virtBootstrap/utils.py
index a65d3f5..e1e681c 100644
--- a/src/virtBootstrap/utils.py
+++ b/src/virtBootstrap/utils.py
@@ -32,6 +32,7 @@ import tempfile
 import logging
 
 from subprocess import CalledProcessError, PIPE, Popen
+import passlib.hosts
 
 # pylint: disable=invalid-name
 # Create logger
@@ -331,6 +332,38 @@ def str2float(element):
         return None
 
 
+def set_root_password(rootfs, password):
+    """
+    Set password on the root user within root filesystem
+    """
+    shadow_file = os.path.join(rootfs, "etc/shadow")
+
+    shadow_file_permissions = os.stat(shadow_file)[0]
+    # Set read-write permissions to shadow file
+    # 438 = 0110110110 = -rw-rw-rw-
+    os.chmod(shadow_file, 438)
+    try:
+        with open(shadow_file) as orig_file:
+            shadow_content = orig_file.read().split('\n')
+
+        for index, line in enumerate(shadow_content):
+            if line.startswith('root'):
+                line_split = line.split(':')
+                line_split[1] = passlib.hosts.linux_context.hash(password)
+                shadow_content[index] = ':'.join(line_split)
+                break
+
+        with open(shadow_file, "w") as new_file:
+            new_file.write('\n'.join(shadow_content))
+
+    except Exception:
+        raise
+
+    finally:
+        # Restore original permissions
+        os.chmod(shadow_file, shadow_file_permissions)
+
+
 def write_progress(prog):
     """
     Write progress output to console
diff --git a/src/virtBootstrap/virt_bootstrap.py b/src/virtBootstrap/virt_bootstrap.py
index fe27398..98c629a 100755
--- a/src/virtBootstrap/virt_bootstrap.py
+++ b/src/virtBootstrap/virt_bootstrap.py
@@ -27,7 +27,6 @@ import logging
 import sys
 import os
 from textwrap import dedent
-from subprocess import CalledProcessError, Popen, PIPE
 try:
     from urlparse import urlparse
 except ImportError:
@@ -70,18 +69,6 @@ def get_source(source_type):
         raise Exception("Invalid image URL scheme: '%s'" % source_type)
 
 
-def set_root_password(rootfs, password):
-    """
-    Set password on the root user in rootfs
-    """
-    users = 'root:%s' % password
-    args = ['chpasswd', '-R', rootfs]
-    chpasswd = Popen(args, stdin=PIPE)
-    chpasswd.communicate(input=users.encode('utf-8'))
-    if chpasswd.returncode != 0:
-        raise CalledProcessError(chpasswd.returncode, cmd=args, output=None)
-
-
 # pylint: disable=too-many-arguments
 def bootstrap(uri, dest,
               fmt='dir',
@@ -117,8 +104,9 @@ def bootstrap(uri, dest,
            no_cache=no_cache,
            progress=prog).unpack(dest)
 
-    if root_password is not None:
-        set_root_password(dest, root_password)
+    if fmt == "dir" and root_password is not None:
+        logger.info("Setting password of the root account")
+        utils.set_root_password(dest, root_password)
 
 
 def set_logging_conf(loglevel=None):
-- 
2.9.4




More information about the virt-tools-list mailing list