Allow custom UC/standalone passwords

Fix the passwords generation precedence for containerized UC and
standalone installations. This allows users to provide custom
passwords/secrets in tripleo-undercloud-passwords.yaml, or
undercloud-passwords.conf, when upgrading from instack.

Partial-bug: #1784576

Change-Id: If7166c2419794dd9595d25ad29ee1fdafce99d72
Signed-off-by: Bogdan Dobrelya <bdobreli@redhat.com>
This commit is contained in:
Bogdan Dobrelya 2018-07-31 13:43:20 +03:00
parent 9ec31e0e5f
commit 16ccbb1360
3 changed files with 151 additions and 79 deletions

View File

@ -0,0 +1,10 @@
---
upgrade:
- |
Instack undercloud legacy passwords should be customized by its original
``undercloud-passwords.conf`` location, when upgrading from instack UC.
Those passwords will be automatically transitioned to
``tripleo-undercloud-passwords.yaml`` during upgrade. Changes made to the
legacy location become ignored after that. Use
``tripleo-undercloud-passwords.yaml`` to manually update UC passwords
further on.

View File

@ -131,9 +131,53 @@ class TestDeployUndercloud(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
@mock.patch('yaml.safe_dump')
def test_update_passwords_env_update(self, mock_dump, mock_pw, mock_cc,
mock_exists, mock_chmod, mock_user):
pw_dict = {"GeneratedPassword": 123}
def test_update_passwords_env(self, mock_dump, mock_pw, mock_cc,
mock_exists, mock_chmod, mock_user):
pw_dict = {"GeneratedPassword": 123, "LegacyPass": "override me"}
pw_conf_path = os.path.join(self.temp_homedir,
'undercloud-passwords.conf')
t_pw_conf_path = os.path.join(
self.temp_homedir, 'tripleo-undercloud-passwords.yaml')
mock_pw.return_value = pw_dict
mock_exists.return_value = True
with open(t_pw_conf_path, 'w') as t_pw:
t_pw.write('parameter_defaults: {ExistingKey: xyz, '
'LegacyPass: pick-me-legacy-tht, '
'RpcPassword: pick-me-rpc}\n')
with open(pw_conf_path, 'w') as t_pw:
t_pw.write('[auth]\nundercloud_db_password = ignore-me-mysql\n'
'undercloud_rabbit_password = ignore-me-rabbit\n'
'undercloud_rpc_password = ignore-me-rpc\n'
'undercloud_legacy_pass = ignore-me-legacy\n')
self.cmd._update_passwords_env(self.temp_homedir,
'stack', upgrade=False,
passwords={'ADefault': 456,
'ExistingKey':
'dontupdate'})
expected_dict = {
'parameter_defaults': {'GeneratedPassword': 123,
'LegacyPass': 'pick-me-legacy-tht',
'RpcPassword': 'pick-me-rpc',
'ExistingKey': 'xyz',
'ADefault': 456}}
mock_dump.assert_called_once_with(expected_dict,
mock.ANY,
default_flow_style=False)
# TODO(bogdando) drop once we have proper oslo.privsep
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
# TODO(bogdando) drop once we have proper oslo.privsep
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
@mock.patch('yaml.safe_dump')
def test_update_passwords_env_upgrade(self, mock_dump, mock_pw, mock_cc,
mock_exists, mock_chmod, mock_user):
pw_dict = {"GeneratedPassword": 123, "LegacyPass": "override me"}
pw_conf_path = os.path.join(self.temp_homedir,
'undercloud-passwords.conf')
t_pw_conf_path = os.path.join(
@ -145,20 +189,29 @@ class TestDeployUndercloud(TestPluginV1):
return not file_name.startswith('/etc/keystone')
mock_exists.side_effect = mock_file_exists
with open(t_pw_conf_path, 'w') as t_pw:
t_pw.write('parameter_defaults: {ExistingKey: xyz}\n')
t_pw.write('parameter_defaults: {ExistingKey: xyz, '
'LegacyPass: override-me-legacy, '
'RpcPassword: override-me-rpc}\n')
with open(pw_conf_path, 'w') as t_pw:
t_pw.write('[auth]\nundercloud_db_password = abc\n')
t_pw.write('[auth]\nundercloud_db_password = pick-me-mysql\n'
'undercloud_rabbit_password = pick-me-rabbit\n'
'undercloud_rpc_password = pick-me-rpc\n'
'undercloud_legacy_pass = pick-me-legacy-instack\n')
self.cmd._update_passwords_env(self.temp_homedir,
'stack',
'stack', upgrade=True,
passwords={'ADefault': 456,
'ExistingKey':
'dontupdate'})
expected_dict = {'parameter_defaults': {'GeneratedPassword': 123,
'ExistingKey': 'xyz',
'MysqlRootPassword': 'abc',
'ADefault': 456}}
expected_dict = {
'parameter_defaults': {'GeneratedPassword': 123,
'ExistingKey': 'xyz',
'MysqlRootPassword': 'pick-me-mysql',
'RpcPassword': 'pick-me-rpc',
'RabbitPassword': 'pick-me-rabbit',
'LegacyPass': 'pick-me-legacy-instack',
'ADefault': 456}}
mock_dump.assert_called_once_with(expected_dict,
mock.ANY,
default_flow_style=False)

View File

@ -260,84 +260,89 @@ class Deploy(command.Command):
constants.PUPPET_MODULES,
constants.PUPPET_BASE)
def _update_passwords_env(self, output_dir, user, passwords=None):
def _update_passwords_env(self, output_dir, user, upgrade=None,
passwords=None):
pw_file = os.path.join(output_dir, 'tripleo-undercloud-passwords.yaml')
undercloud_pw_file = os.path.join(output_dir,
'undercloud-passwords.conf')
# Generated passwords take the lowest precedence, allowing
# custom overrides
stack_env = {'parameter_defaults': {}}
# Getting passwords that were managed by instack-undercloud so
# we can upgrade to a containerized undercloud and keep old passwords.
legacy_env = {}
if os.path.exists(undercloud_pw_file):
config = configparser.ConfigParser()
config.read(undercloud_pw_file)
for k, v in config.items('auth'):
# Manage exceptions
if k == 'undercloud_db_password':
k = 'MysqlRootPassword'
elif k == 'undercloud_rabbit_username':
k = 'RpcUserName'
elif k == 'undercloud_rabbit_password':
try:
# NOTE(aschultz): Only save rabbit password to rpc
# if it's not already defined for the upgrade case.
# The passwords are usually different so we don't
# want to overwrite it if it already exists because
# we'll end up rewriting the passwords later and
# causing problems.
config.get('auth', 'undercloud_rpc_password')
except Exception:
legacy_env['RpcPassword'] = v
k = 'RabbitPassword'
elif k == 'undercloud_rabbit_cookie':
k = 'RabbitCookie'
elif k == 'undercloud_heat_encryption_key':
k = 'HeatAuthEncryptionKey'
elif k == 'undercloud_libvirt_tls_password':
k = 'LibvirtTLSPassword'
elif k == 'undercloud_ha_proxy_stats_password':
k = 'HAProxyStatsPassword'
else:
k = ''.join(i.capitalize() for i in k.split('_')[1:])
legacy_env[k] = v
stack_env['parameter_defaults'] = password_utils.generate_passwords(
stack_env=stack_env)
if os.path.exists(pw_file):
with open(pw_file) as pf:
stack_env = yaml.safe_load(pf.read())
stack_env['parameter_defaults'].update(
yaml.safe_load(pf.read())['parameter_defaults'])
# Get the keystone keys before upgrade
keystone_fernet_repo = '/etc/keystone/fernet-keys/'
keystone_credential_repo = '/etc/keystone/credential-keys/'
self._set_data_rights('/etc/keystone', user=user)
if upgrade:
# Getting passwords that were managed by instack-undercloud so
# we can upgrade to a containerized undercloud and keep old
# passwords.
legacy_env = {}
if os.path.exists(undercloud_pw_file):
config = configparser.ConfigParser()
config.read(undercloud_pw_file)
for k, v in config.items('auth'):
# Manage exceptions
if k == 'undercloud_db_password':
k = 'MysqlRootPassword'
elif k == 'undercloud_rabbit_username':
k = 'RpcUserName'
elif k == 'undercloud_rabbit_password':
try:
# NOTE(aschultz): Only save rabbit password to rpc
# if it's not already defined for the upgrade case.
# The passwords are usually different so we don't
# want to overwrite it if it already exists because
# we'll end up rewriting the passwords later and
# causing problems.
config.get('auth', 'undercloud_rpc_password')
except Exception:
legacy_env['RpcPassword'] = v
k = 'RabbitPassword'
elif k == 'undercloud_rabbit_cookie':
k = 'RabbitCookie'
elif k == 'undercloud_heat_encryption_key':
k = 'HeatAuthEncryptionKey'
elif k == 'undercloud_libvirt_tls_password':
k = 'LibvirtTLSPassword'
elif k == 'undercloud_ha_proxy_stats_password':
k = 'HAProxyStatsPassword'
else:
k = ''.join(i.capitalize() for i in k.split('_')[1:])
legacy_env[k] = v
for key_index in range(0, 2):
file_name = keystone_credential_repo + str(key_index)
key = 'KeystoneCredential' + str(key_index)
if os.path.exists(file_name):
with open(file_name, 'r') as file_content:
content = file_content.read()
legacy_env[key] = content
# Get the keystone keys before upgrade
keystone_fernet_repo = '/etc/keystone/fernet-keys/'
keystone_credential_repo = '/etc/keystone/credential-keys/'
self._set_data_rights('/etc/keystone', user=user)
fernet_keys = {}
file_count = 0
if os.path.exists(keystone_fernet_repo):
file_count = len(os.listdir(keystone_fernet_repo))
for key_index in range(0, 2):
file_name = keystone_credential_repo + str(key_index)
key = 'KeystoneCredential' + str(key_index)
if os.path.exists(file_name):
with open(file_name, 'r') as file_content:
content = file_content.read()
legacy_env[key] = content
for key_index in range(0, file_count):
file_name = keystone_fernet_repo + str(key_index)
if os.path.exists(file_name):
with open(file_name, 'r') as file_content:
content = file_content.read()
fernet_keys[file_name] = {'content': content}
if fernet_keys:
legacy_env['KeystoneFernetKeys'] = fernet_keys
fernet_keys = {}
file_count = 0
if os.path.exists(keystone_fernet_repo):
file_count = len(os.listdir(keystone_fernet_repo))
pw = password_utils.generate_passwords(stack_env=stack_env)
stack_env['parameter_defaults'].update(pw)
# Override what has been generated by tripleo-common with old passwords
# if any.
stack_env['parameter_defaults'].update(legacy_env)
for key_index in range(0, file_count):
file_name = keystone_fernet_repo + str(key_index)
if os.path.exists(file_name):
with open(file_name, 'r') as file_content:
content = file_content.read()
fernet_keys[file_name] = {'content': content}
if fernet_keys:
legacy_env['KeystoneFernetKeys'] = fernet_keys
# Override with picked legacy instack-undercloud values
stack_env['parameter_defaults'].update(legacy_env)
if passwords:
# These passwords are the DefaultPasswords so we only
@ -584,8 +589,12 @@ class Deploy(command.Command):
for e in plan_env_data.get('environments', {})]
# this will allow the user to overwrite passwords with custom envs
pw_file = self._update_passwords_env(self.output_dir,
parsed_args.deployment_user)
# or pick instack legacy passwords as is, if upgrading from instack
pw_file = self._update_passwords_env(
self.output_dir,
parsed_args.deployment_user,
parsed_args.upgrade,
)
environments.append(pw_file)
# use deployed-server because we run os-collect-config locally