[virt-tools-list] [virt-manager PATCH v2 1/2] unattended: Read the passwords from a file

Fabiano Fidêncio fidencio at redhat.com
Wed Jul 3 14:01:28 UTC 2019


Let's not expose the user/root password in the CLI and, instead, let's
rely on a file passed by the admin and read the password from there.

'CVE-2019-10183' has been assigned to the virt-install --unattended
admin-password=xxx disclosure issue.

Signed-off-by: Fabiano Fidêncio <fidencio at redhat.com>
---
 man/virt-install.pod                  | 24 ++++++++++++++-------
 tests/cli-test-xml/admin-password.txt |  1 +
 tests/cli-test-xml/user-password.txt  |  3 +++
 tests/clitest.py                      | 18 +++++++++-------
 virtinst/cli.py                       |  4 ++--
 virtinst/install/unattended.py        | 30 ++++++++++++++++++---------
 6 files changed, 53 insertions(+), 27 deletions(-)
 create mode 100644 tests/cli-test-xml/admin-password.txt
 create mode 100644 tests/cli-test-xml/user-password.txt

diff --git a/man/virt-install.pod b/man/virt-install.pod
index d8bd4127..081f28c3 100644
--- a/man/virt-install.pod
+++ b/man/virt-install.pod
@@ -612,13 +612,23 @@ Choose which libosinfo unattended profile to use. Most distros have
 a 'desktop' and a 'jeos' profile. virt-install will default to 'desktop'
 if this is unspecified.
 
-=item B<admin-password=>
-
-Set the VM OS admin/root password
-
-=item B<user-password=>
-
-Set the VM user password. The username is your current host username
+=item B<admin-password-file=>
+
+A file used to set the VM OS admin/root password from. This option can
+be used either as "admin-password-file=/path/to/password-file" or as
+"admin-password-file=/dev/fd/n", being n the file descriptor of the
+password-file.
+Note that only the first line of the file will be considered, including
+any whitespace characters and excluding new-line.
+
+=item B<user-password-file=>
+
+A file used to set the VM user password. This option can be used either as
+"user-password-file=/path/to/password-file" or as
+"user-password-file=/dev/fd/n", being n the file descriptor of the
+password-file. The username is your current host username.
+Note that only the first line of the file will be considered, including
+any whitespace characters and excluding new-line.
 
 =item B<product-key=>
 
diff --git a/tests/cli-test-xml/admin-password.txt b/tests/cli-test-xml/admin-password.txt
new file mode 100644
index 00000000..323fae03
--- /dev/null
+++ b/tests/cli-test-xml/admin-password.txt
@@ -0,0 +1 @@
+foobar
diff --git a/tests/cli-test-xml/user-password.txt b/tests/cli-test-xml/user-password.txt
new file mode 100644
index 00000000..625999ba
--- /dev/null
+++ b/tests/cli-test-xml/user-password.txt
@@ -0,0 +1,3 @@
+blah
+     
+        
diff --git a/tests/clitest.py b/tests/clitest.py
index 1165baed..2618e04b 100644
--- a/tests/clitest.py
+++ b/tests/clitest.py
@@ -9,6 +9,7 @@ import os
 import shlex
 import shutil
 import sys
+import tempfile
 import traceback
 import unittest
 
@@ -89,6 +90,8 @@ test_files = {
     'ISO-F29-LIVE': iso_links[5],
     'TREEDIR': "%s/fakefedoratree" % XMLDIR,
     'COLLIDE': "/dev/default-pool/collidevol1.img",
+    'ADMIN-PASSWORD-FILE': "%s/admin-password.txt" % XMLDIR,
+    'USER-PASSWORD-FILE': "%s/user-password.txt" % XMLDIR,
 }
 
 
@@ -872,22 +875,21 @@ c.add_valid("--connect %s --pxe --disk size=1" % utils.URIs.test_defaultpool_col
 ####################
 
 c = vinst.add_category("unattended-install", "--connect %(URI-KVM)s --nographics --noautoconsole --disk none", prerun_check=no_osinfo_unattend_cb)
-c.add_compare("--install fedora26 --unattended profile=desktop,admin-password=foobar,user-password=blah,product-key=1234", "osinfo-url-unattended")  # unattended install for fedora, using initrd injection
-c.add_compare("--cdrom %(ISO-WIN7)s --unattended profile=desktop,admin-password=foobar", "osinfo-win7-unattended")  # unattended install for win7
-c.add_compare("--os-variant fedora26 --unattended profile=jeos,admin-password=123456 --location %(ISO-F26-NETINST)s", "osinfo-netinst-unattended")  # triggering the special netinst checking code
+c.add_compare("--install fedora26 --unattended profile=desktop,admin-password-file=%(ADMIN-PASSWORD-FILE)s,user-password-file=%(USER-PASSWORD-FILE)s,product-key=1234", "osinfo-url-unattended")  # unattended install for fedora, using initrd injection
+c.add_compare("--cdrom %(ISO-WIN7)s --unattended profile=desktop,admin-password-file=%(ADMIN-PASSWORD-FILE)s", "osinfo-win7-unattended")  # unattended install for win7
+c.add_compare("--os-variant fedora26 --unattended profile=jeos,admin-password-file=%(ADMIN-PASSWORD-FILE)s --location %(ISO-F26-NETINST)s", "osinfo-netinst-unattended")  # triggering the special netinst checking code
 c.add_compare("--os-variant silverblue29 --location http://example.com", "network-install-resources")  # triggering network-install resources override
 c.add_valid("--pxe --os-variant fedora26 --unattended", grep="Using unattended profile 'desktop'")  # filling in default 'desktop' profile
 c.add_invalid("--os-variant fedora26 --unattended profile=jeos --location http://example.foo", grep="admin-password")  # will trigger admin-password required error
 c.add_invalid("--os-variant fedora26 --unattended profile=jeos --location http://example.foo", grep="admin-password")  # will trigger admin-password required error
-c.add_invalid("--os-variant debian9 --unattended profile=desktop,admin-password=foobar --location http://example.foo", grep="user-password")  # will trigger user-password required error
-c.add_invalid("--os-variant debian9 --unattended profile=FRIBBER,admin-password=foobar --location http://example.foo", grep="Available profiles")  # will trigger unknown profile error
-c.add_invalid("--os-variant fedora29 --unattended profile=desktop,admin-password=foobar --cdrom %(ISO-F29-LIVE)s", grep="media does not support")  # live media doesn't support installscript
+c.add_invalid("--os-variant debian9 --unattended profile=desktop,admin-password-file=%(ADMIN-PASSWORD-FILE)s --location http://example.foo", grep="user-password")  # will trigger user-password required error
+c.add_invalid("--os-variant debian9 --unattended profile=FRIBBER,admin-password-file=%(ADMIN-PASSWORD-FILE)s --location http://example.foo", grep="Available profiles")  # will trigger unknown profile error
+c.add_invalid("--os-variant fedora29 --unattended profile=desktop,admin-password-file=%(ADMIN-PASSWORD-FILE)s --cdrom %(ISO-F29-LIVE)s", grep="media does not support")  # live media doesn't support installscript
 c.add_invalid("--os-variant msdos --unattended profile=desktop --location http://example.com")  # msdos doesn't support unattended install
 c.add_invalid("--os-variant winxp --unattended profile=desktop --cdrom %(ISO-WIN7)s")  # winxp doesn't support expected injection method 'cdrom'
 c.add_invalid("--connect %(URI-TEST-REMOTE)s --os-variant win7 --cdrom %(EXISTIMG1)s --unattended")  # --unattended method=cdrom rejected for remote connections
 
 
-
 #############################
 # Remote URI specific tests #
 #############################
@@ -1356,7 +1358,7 @@ _add_argcomplete_cmd("virt-install --install i", "initrd")
 _add_argcomplete_cmd("virt-install --test-stub", None,
         nogrep="--test-stub-command")
 _add_argcomplete_cmd("virt-install --unattended ", "profile=")  # will list all --unattended subprops
-_add_argcomplete_cmd("virt-install --unattended a", "admin-password=")
+_add_argcomplete_cmd("virt-install --unattended a", "admin-password-file=")
 _add_argcomplete_cmd("virt-clone --preserve", "--preserve-data")
 _add_argcomplete_cmd("virt-xml --sound mode", "model")
 _add_argcomplete_cmd("virt-convert --dest", "--destination")
diff --git a/virtinst/cli.py b/virtinst/cli.py
index e5645d84..3f540373 100644
--- a/virtinst/cli.py
+++ b/virtinst/cli.py
@@ -1508,8 +1508,8 @@ class ParserUnattended(VirtCLIParser):
     def _init_class(cls, **kwargs):
         VirtCLIParser._init_class(**kwargs)
         cls.add_arg("profile", "profile")
-        cls.add_arg("admin-password", "admin_password")
-        cls.add_arg("user-password", "user_password")
+        cls.add_arg("admin-password-file", "admin_password_file")
+        cls.add_arg("user-password-file", "user_password_file")
         cls.add_arg("product-key", "product_key")
 
 
diff --git a/virtinst/install/unattended.py b/virtinst/install/unattended.py
index ea3f9066..ae99bfdb 100644
--- a/virtinst/install/unattended.py
+++ b/virtinst/install/unattended.py
@@ -39,23 +39,21 @@ def _make_installconfig(script, osobj, unattended_data, arch, hostname, url):
 
     # Set user-password.
     # In case it's required and not passed, just raise a RuntimeError.
-    if script.requires_user_password() and not unattended_data.user_password:
+    if (script.requires_user_password() and
+        not unattended_data.get_user_password()):
         raise RuntimeError(
             _("%s requires the user-password to be set.") %
             osobj.name)
-    config.set_user_password(
-        unattended_data.user_password if unattended_data.user_password
-        else "")
+    config.set_user_password(unattended_data.get_user_password() or "")
 
     # Set the admin-password:
     # In case it's required and not passed, just raise a RuntimeError.
-    if script.requires_admin_password() and not unattended_data.admin_password:
+    if (script.requires_admin_password() and
+        not unattended_data.get_admin_password()):
         raise RuntimeError(
             _("%s requires the admin-password to be set.") %
             osobj.name)
-    config.set_admin_password(
-        unattended_data.admin_password if unattended_data.admin_password
-        else "")
+    config.set_admin_password(unattended_data.get_admin_password() or "")
 
     # Set the target disk.
     # virtiodisk is the preferred way, in case it's supported, otherwise
@@ -205,10 +203,22 @@ class OSInstallScript:
 
 class UnattendedData():
     profile = None
-    admin_password = None
-    user_password = None
+    admin_password_file = None
+    user_password_file = None
     product_key = None
 
+    def _get_password(self, pwdfile):
+        with open(pwdfile, "r") as pwd:
+            return pwd.readline().rstrip("\n\r")
+
+    def get_user_password(self):
+        if self.user_password_file:
+            return self._get_password(self.user_password_file)
+
+    def get_admin_password(self):
+        if self.admin_password_file:
+            return self._get_password(self.admin_password_file)
+
 
 def _make_scriptmap(script_list):
     """
-- 
2.21.0




More information about the virt-tools-list mailing list