From 73c64fc242c1d52688d829074b9a4adab6c8e08d Mon Sep 17 00:00:00 2001 From: Tee Ngo Date: Mon, 3 Apr 2023 13:17:31 -0400 Subject: [PATCH] Ensure proper handling of special password Currently subcloud prestage would fail if the sysadmin password that starts with a special character (e.g. @Password123). This is because the inventory file is generated with the password as-is which results in yaml parsing failure by ansible. This commit ensures the sysadmin password that starts with a special character is handled properly. Test Plan: - Verify successful subcloud add with password that starts with or without a special character. - Verify successful subcloud reinstall with password that starts with or without a special character. - Verify successful subcloud reconfigure with password that starts with or without a special character. - Verify successfull subcloud backup and restore with password that starts with or without a special character. - Verify successful subcloud prestage with password that starts with or without a special character. Closes-Bug: 2015116 Signed-off-by: Tee Ngo Change-Id: Ieedaee16ed0489c801cd56b1891daf71a26f1e74 --- distributedcloud/dccommon/subcloud_install.py | 3 +-- .../api/controllers/v1/subcloud_backup.py | 5 ++--- .../dcmanager/api/controllers/v1/subclouds.py | 12 ++++++------ distributedcloud/dcmanager/common/prestage.py | 3 ++- distributedcloud/dcmanager/common/utils.py | 15 +++++++++++++++ .../dcmanager/manager/subcloud_manager.py | 2 +- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/distributedcloud/dccommon/subcloud_install.py b/distributedcloud/dccommon/subcloud_install.py index fa1703cde..304dde400 100644 --- a/distributedcloud/dccommon/subcloud_install.py +++ b/distributedcloud/dccommon/subcloud_install.py @@ -14,7 +14,6 @@ # from eventlet.green import subprocess -import json import netaddr import os from oslo_log import log as logging @@ -234,7 +233,7 @@ class SubcloudInstall(object): + '\n' ) for k, v in payload.items(): - f_out_override_file.write("%s: %s\n" % (k, json.dumps(v))) + f_out_override_file.write("%s: %s\n" % (k, v)) def create_ks_conf_file(self, filename, values): try: diff --git a/distributedcloud/dcmanager/api/controllers/v1/subcloud_backup.py b/distributedcloud/dcmanager/api/controllers/v1/subcloud_backup.py index 7157abd1f..a070c8936 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/subcloud_backup.py +++ b/distributedcloud/dcmanager/api/controllers/v1/subcloud_backup.py @@ -7,7 +7,6 @@ import json from collections import namedtuple -import base64 import os from oslo_config import cfg from oslo_log import log as logging @@ -135,8 +134,8 @@ class SubcloudBackupController(object): if not sysadmin_password: pecan.abort(400, _('subcloud sysadmin_password required')) try: - payload['sysadmin_password'] = base64.b64decode( - sysadmin_password).decode('utf-8') + payload['sysadmin_password'] = \ + utils.decode_and_normalize_passwd(sysadmin_password) except Exception: msg = _('Failed to decode subcloud sysadmin_password, ' 'verify the password is base64 encoded') diff --git a/distributedcloud/dcmanager/api/controllers/v1/subclouds.py b/distributedcloud/dcmanager/api/controllers/v1/subclouds.py index 6c6155f74..caf1c3fee 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/subclouds.py +++ b/distributedcloud/dcmanager/api/controllers/v1/subclouds.py @@ -1198,8 +1198,8 @@ class SubcloudsController(object): if not sysadmin_password: pecan.abort(400, _('subcloud sysadmin_password required')) try: - payload['sysadmin_password'] = base64.b64decode( - sysadmin_password).decode('utf-8') + payload['sysadmin_password'] = \ + utils.decode_and_normalize_passwd(sysadmin_password) except Exception: msg = _('Failed to decode subcloud sysadmin_password, ' 'verify the password is base64 encoded') @@ -1401,8 +1401,8 @@ class SubcloudsController(object): pecan.abort(400, _('subcloud sysadmin_password required')) try: - payload['sysadmin_password'] = base64.b64decode( - sysadmin_password).decode('utf-8') + payload['sysadmin_password'] = \ + utils.decode_and_normalize_passwd(sysadmin_password) except Exception: msg = _('Failed to decode subcloud sysadmin_password, ' 'verify the password is base64 encoded') @@ -1499,8 +1499,8 @@ class SubcloudsController(object): pecan.abort(400, _('subcloud sysadmin_password required')) try: - payload['sysadmin_password'] = base64.b64decode( - sysadmin_password).decode('utf-8') + payload['sysadmin_password'] = \ + utils.decode_and_normalize_passwd(sysadmin_password) except Exception: msg = _('Failed to decode subcloud sysadmin_password, ' 'verify the password is base64 encoded') diff --git a/distributedcloud/dcmanager/common/prestage.py b/distributedcloud/dcmanager/common/prestage.py index 761400822..317b99fcb 100644 --- a/distributedcloud/dcmanager/common/prestage.py +++ b/distributedcloud/dcmanager/common/prestage.py @@ -408,7 +408,8 @@ def _run_ansible(context, prestage_command, phase, subcloud.name, ansible_subcloud_inventory_file, oam_floating_ip, - ansible_pass=base64.b64decode(sysadmin_password).decode('utf-8')) + ansible_pass=utils.decode_and_normalize_passwd(sysadmin_password)) + try: run_playbook(log_file, prestage_command, timeout=timeout_seconds, register_cleanup=True) diff --git a/distributedcloud/dcmanager/common/utils.py b/distributedcloud/dcmanager/common/utils.py index c1c2c50ab..94b7e1f46 100644 --- a/distributedcloud/dcmanager/common/utils.py +++ b/distributedcloud/dcmanager/common/utils.py @@ -24,6 +24,7 @@ import pwd import re import resource as sys_resource import six.moves +import string import subprocess import tsconfig.tsconfig as tsc import yaml @@ -942,3 +943,17 @@ def get_value_from_yaml_file(filename, key): data = yaml.load(data, Loader=yaml.SafeLoader) value = data.get(key) return value + + +def decode_and_normalize_passwd(input_passwd): + pattern = r'^[' + string.punctuation + ']' + passwd = base64.decode_as_text(input_passwd) + # Ensure that sysadmin password which starts with a special + # character will be enclosed in quotes so that the generated + # inventory file will be parsable by Ansible. + if not passwd.startswith('"') and re.search(pattern, passwd): + passwd = '"' + passwd + '"' + elif passwd.startswith('"') and not passwd.endswith('"'): + passwd = "'" + passwd + "'" + + return passwd diff --git a/distributedcloud/dcmanager/manager/subcloud_manager.py b/distributedcloud/dcmanager/manager/subcloud_manager.py index 9e14cad2b..2138a866a 100644 --- a/distributedcloud/dcmanager/manager/subcloud_manager.py +++ b/distributedcloud/dcmanager/manager/subcloud_manager.py @@ -1107,7 +1107,7 @@ class SubcloudManager(manager.Manager): '---\n' ) for k, v in payload['override_values'].items(): - f_out.write("%s: %s\n" % (k, json.dumps(v))) + f_out.write("%s: %s\n" % (k, v)) return backup_overrides_file