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

Cedric Bosdonnat cbosdonnat at suse.com
Thu Jul 20 12:39:45 UTC 2017


The word 'script' in the title doesn't sound nice and accurate. I would
reinforce the idea that the change is getting rid of root privileges to
change the root password. A title like this would be a better title

"Drop need of root privileges to change root password"

On Thu, 2017-07-20 at 12:29 +0100, Radostin Stoyanov wrote:
> 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'],
> +

Can't that work with older versions of passlib?

>      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)

That reminds me that we'll need to handle the qcow2 case as well.


ACK with the new title.

--
Cedric

>  
>  
>  def set_logging_conf(loglevel=None):




More information about the virt-tools-list mailing list