summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRadostin Stoyanov <rstoyanov1@gmail.com>2017-07-21 13:13:20 +0100
committerRadostin Stoyanov <rstoyanov1@gmail.com>2017-07-21 13:35:48 +0100
commit8028e1054a596077fe7b15d4c254c5d37289ebd6 (patch)
tree58f89f1db48bdaaa4e0d496154c5624cf59a6f5f
parent2762d815643754bc4f5b4a922989fc1acf5ea5b6 (diff)
downloadvirt-bootstrap.git-8028e1054a596077fe7b15d4c254c5d37289ebd6.tar.gz
virt-bootstrap.git-8028e1054a596077fe7b15d4c254c5d37289ebd6.tar.xz
virt-bootstrap.git-8028e1054a596077fe7b15d4c254c5d37289ebd6.zip
Drop need of root privileges to set root password
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
-rwxr-xr-xsetup.py5
-rw-r--r--src/virtBootstrap/utils.py33
-rwxr-xr-xsrc/virtBootstrap/virt_bootstrap.py18
3 files changed, 41 insertions, 15 deletions
diff --git a/setup.py b/setup.py
index 70e3e03..36de2d5 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.6.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 c66cc92..b43c87e 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):