def test_fernet_keys_and_credentials(self, mock_create_creds):

        keys = [uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False)]

        snmpd_password = uuidutils.generate_uuid(dashed=False)

        mock_mistral = mock.Mock()
        mock_mistral.environments.get.return_value = mock.Mock(variables={
            "undercloud_ceilometer_snmpd_password": snmpd_password
        })

        # generate_passwords will be called multiple times
        # but the order is based on how the strings are hashed, and thus
        # not really predictable. So, make sure it is a unique one of the
        # generated values

        mock_create_creds.side_effect = keys
        value = password_utils.generate_passwords(mock_mistral)
        self.assertIn(value['KeystoneCredential0'], keys)
        self.assertIn(value['KeystoneCredential1'], keys)
        self.assertIn(value['KeystoneFernetKey0'], keys)
        self.assertIn(value['KeystoneFernetKey1'], keys)
        self.assertIn(value['BarbicanSimpleCryptoKek'], keys)

        self.assertNotEqual(value['KeystoneFernetKey0'],
                            value['KeystoneFernetKey1'])

        self.assertNotEqual(value['KeystoneCredential0'],
                            value['KeystoneCredential1'])
Exemple #2
0
    def test_fernet_keys_and_credentials(self, mock_create_creds):

        keys = [uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False)]

        snmpd_password = uuidutils.generate_uuid(dashed=False)

        mock_mistral = mock.Mock()
        mock_mistral.environments.get.return_value = mock.Mock(variables={
            "undercloud_ceilometer_snmpd_password": snmpd_password
        })

        # generate_passwords will be called multiple times
        # but the order is based on how the strings are hashed, and thus
        # not really predictable. So, make sure it is a unique one of the
        # generated values

        mock_create_creds.side_effect = keys
        value = password_utils.generate_passwords(mock_mistral)
        self.assertIn(value['KeystoneCredential0'], keys)
        self.assertIn(value['KeystoneCredential1'], keys)
        self.assertIn(value['KeystoneFernetKey0'], keys)
        self.assertIn(value['KeystoneFernetKey1'], keys)

        self.assertNotEqual(value['KeystoneFernetKey0'],
                            value['KeystoneFernetKey1'])

        self.assertNotEqual(value['KeystoneCredential0'],
                            value['KeystoneCredential1'])
Exemple #3
0
    def run(self, context):
        heat = self.get_orchestration_client(context)
        swift = self.get_object_client(context)
        mistral = self.get_workflow_client(context)

        try:
            env = plan_utils.get_env(swift, self.container)
        except swiftexceptions.ClientException as err:
            err_msg = ("Error retrieving environment for plan %s: %s" % (
                self.container, err))
            LOG.exception(err_msg)
            return actions.Result(error=err_msg)

        try:
            stack_env = heat.stacks.environment(
                stack_id=self.container)

            # legacy heat resource names from overcloud.yaml
            # We don't modify these to avoid changing defaults
            for pw_res in constants.LEGACY_HEAT_PASSWORD_RESOURCE_NAMES:
                try:
                    res = heat.resources.get(self.container, pw_res)
                    param_defaults = stack_env.get('parameter_defaults', {})
                    param_defaults[pw_res] = res.attributes['value']
                except heat_exc.HTTPNotFound:
                    LOG.debug('Heat resouce not found: %s' % pw_res)
                    pass

        except heat_exc.HTTPNotFound:
            stack_env = None

        passwords = password_utils.generate_passwords(mistral, stack_env)

        # if passwords don't yet exist in plan environment
        if 'passwords' not in env:
            env['passwords'] = {}

        # ensure all generated passwords are present in plan env,
        # but respect any values previously generated and stored
        for name, password in passwords.items():
            if name not in env['passwords']:
                env['passwords'][name] = password

        try:
            plan_utils.put_env(swift, env)
        except swiftexceptions.ClientException as err:
            err_msg = "Error uploading to container: %s" % err
            LOG.exception(err_msg)
            return actions.Result(error=err_msg)

        self.cache_delete(context,
                          self.container,
                          "tripleo.parameters.get")
        return env['passwords']
    def run(self, context):
        heat = self.get_orchestration_client(context)
        swift = self.get_object_client(context)
        mistral = self.get_workflow_client(context)

        try:
            env = plan_utils.get_env(swift, self.container)
        except swiftexceptions.ClientException as err:
            err_msg = ("Error retrieving environment for plan %s: %s" % (
                self.container, err))
            LOG.exception(err_msg)
            return actions.Result(error=err_msg)

        try:
            stack_env = heat.stacks.environment(
                stack_id=self.container)
        except heat_exc.HTTPNotFound:
            stack_env = None

        passwords = password_utils.generate_passwords(mistral, stack_env)

        # if passwords don't yet exist in plan environment
        if 'passwords' not in env:
            env['passwords'] = {}

        # ensure all generated passwords are present in plan env,
        # but respect any values previously generated and stored
        for name, password in passwords.items():
            if name not in env['passwords']:
                env['passwords'][name] = password

        try:
            plan_utils.put_env(swift, env)
        except swiftexceptions.ClientException as err:
            err_msg = "Error uploading to container: %s" % err
            LOG.exception(err_msg)
            return actions.Result(error=err_msg)

        self.cache_delete(context,
                          self.container,
                          "tripleo.parameters.get")
        return env['passwords']
Exemple #5
0
    def _update_passwords_env(self, passwords=None):
        pw_file = os.path.join(os.environ.get('HOME', ''),
                               'tripleo-undercloud-passwords.yaml')
        stack_env = {'parameter_defaults': {}}
        if os.path.exists(pw_file):
            with open(pw_file) as pf:
                stack_env = yaml.safe_load(pf.read())

        pw = password_utils.generate_passwords(stack_env=stack_env)
        stack_env['parameter_defaults'].update(pw)

        if passwords:
            # These passwords are the DefaultPasswords so we only
            # update if they don't already exist in stack_env
            for p, v in passwords.items():
                if p not in stack_env['parameter_defaults']:
                    stack_env['parameter_defaults'][p] = v

        with open(pw_file, 'w') as pf:
            yaml.safe_dump(stack_env, pf, default_flow_style=False)

        return pw_file
Exemple #6
0
    def run(self, context):
        heat = self.get_orchestration_client(context)
        swift = self.get_object_client(context)
        mistral = self.get_workflow_client(context)

        try:
            env = plan_utils.get_env(swift, self.container)
        except swiftexceptions.ClientException as err:
            err_msg = ("Error retrieving environment for plan %s: %s" %
                       (self.container, err))
            LOG.exception(err_msg)
            return actions.Result(error=err_msg)

        try:
            stack_env = heat.stacks.environment(stack_id=self.container)
        except heat_exc.HTTPNotFound:
            stack_env = None

        passwords = password_utils.generate_passwords(mistral, stack_env)

        # if passwords don't yet exist in plan environment
        if 'passwords' not in env:
            env['passwords'] = {}

        # ensure all generated passwords are present in plan env,
        # but respect any values previously generated and stored
        for name, password in passwords.items():
            if name not in env['passwords']:
                env['passwords'][name] = password

        try:
            plan_utils.put_env(swift, env)
        except swiftexceptions.ClientException as err:
            err_msg = "Error uploading to container: %s" % err
            LOG.exception(err_msg)
            return actions.Result(error=err_msg)

        self.cache_delete(context, self.container, "tripleo.parameters.get")
        return env['passwords']
Exemple #7
0
    def run(self):

        orchestration = self.get_orchestration_client()
        wc = self.get_workflow_client()
        try:
            wf_env = wc.environments.get(self.container)
        except Exception:
            msg = "Error retrieving mistral environment: %s" % self.container
            LOG.exception(msg)
            return mistral_workflow_utils.Result(error=msg)

        try:
            stack_env = orchestration.stacks.environment(
                stack_id=self.container)
        except heat_exc.HTTPNotFound:
            stack_env = None

        passwords = password_utils.generate_passwords(wc, stack_env)

        # if passwords don't yet exist in mistral environment
        if 'passwords' not in wf_env.variables:
            wf_env.variables['passwords'] = {}

        # ensure all generated passwords are present in mistral env,
        # but respect any values previously generated and stored
        for name, password in passwords.items():
            if name not in wf_env.variables['passwords']:
                wf_env.variables['passwords'][name] = password

        env_kwargs = {
            'name': wf_env.name,
            'variables': wf_env.variables,
        }

        wc.environments.update(**env_kwargs)
        self.cache_delete(self.container, "tripleo.parameters.get")

        return wf_env.variables['passwords']
    def test_fernet_keys_and_credentials(self, mock_create_creds):

        keys = [uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False),
                uuidutils.generate_uuid(dashed=False)]

        # generate_passwords will be called multiple times
        # but the order is based on how the strings are hashed, and thus
        # not really predictable. So, make sure it is a unique one of the
        # generated values

        mock_create_creds.side_effect = keys
        with mock.patch(self.open_builtins, mock.mock_open(read_data="data")):
            with mock.patch('yaml.load') as mock_yaml:
                mock_yaml.return_value = {
                    'parameter_defaults': {
                        'SnmpdReadonlyUserPassword': self.snmp_test_pw
                    }
                }
                value = password_utils.generate_passwords()
        self.assertIn(value['KeystoneCredential0'], keys)
        self.assertIn(value['KeystoneCredential1'], keys)
        self.assertIn(value['KeystoneFernetKey0'], keys)
        self.assertIn(value['KeystoneFernetKey1'], keys)
        self.assertIn(value['BarbicanSimpleCryptoKek'], keys)

        self.assertNotEqual(value['KeystoneFernetKey0'],
                            value['KeystoneFernetKey1'])

        self.assertNotEqual(value['KeystoneCredential0'],
                            value['KeystoneCredential1'])
        self.assertEqual(len(value['OctaviaServerCertsKeyPassphrase']), 32)
    def _update_passwords_env(self, output_dir, passwords=None):
        pw_file = os.path.join(output_dir, 'tripleo-undercloud-passwords.yaml')
        undercloud_pw_file = os.path.join(output_dir,
                                          'undercloud-passwords.conf')
        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 = 'RabbitUserName'
                elif k == 'undercloud_heat_encryption_key':
                    k = 'HeatAuthEncryptionKey'
                else:
                    k = ''.join(i.capitalize() for i in k.split('_')[1:])
                legacy_env[k] = v

        if os.path.exists(pw_file):
            with open(pw_file) as pf:
                stack_env = yaml.safe_load(pf.read())

        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)

        if passwords:
            # These passwords are the DefaultPasswords so we only
            # update if they don't already exist in stack_env
            for p, v in passwords.items():
                if p not in stack_env['parameter_defaults']:
                    stack_env['parameter_defaults'][p] = v

        # Write out the password file in yaml for heat.
        # This contains sensitive data so ensure it's not world-readable
        with open(pw_file, 'w') as pf:
            yaml.safe_dump(stack_env, pf, default_flow_style=False)
        # Using chmod here instead of permissions on the open above so we don't
        # have to fight with umask.
        os.chmod(pw_file, 0o600)
        # Write out an instack undercloud compatible version.
        # This contains sensitive data so ensure it's not world-readable
        with open(undercloud_pw_file, 'w') as pf:
            pf.write('[auth]\n')
            for p, v in stack_env['parameter_defaults'].items():
                if 'Password' in p or 'Token' in p:
                    # Convert camelcase from heat templates into the underscore
                    # format used by instack undercloud.
                    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', p)
                    pw_key = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
                    pf.write('undercloud_%s: %s\n' % (pw_key, v))
        os.chmod(undercloud_pw_file, 0o600)

        return pw_file
Exemple #10
0
def generate_passwords(swift=None,
                       heat=None,
                       container=constants.DEFAULT_CONTAINER_NAME,
                       rotate_passwords=False,
                       rotate_pw_list=None,
                       passwords_env=None):
    """Generates passwords needed for Overcloud deployment

    This method generates passwords. By default, this method respects
    previously generated passwords and in stack environment.

    If rotate_passwords is set to True, then passwords will be replaced as
    follows:
    - if password names are specified in the rotate_pw_list, then only those
      passwords will be replaced.
    - otherwise, all passwords not in the DO_NOT_ROTATE list (as they require
      special handling, like KEKs and Fernet keys) will be replaced.
    """
    if rotate_pw_list is None:
        rotate_pw_list = []

    if passwords_env:
        stack_env = passwords_env
        placement_extracted = True
    elif heat is None:
        stack_env = None
        placement_extracted = True
    else:
        try:
            stack_env = heat.stacks.environment(stack_id=container)
        except heat_exc.HTTPNotFound:
            stack_env = None

        placement_extracted = False
        try:
            # We can't rely on the existence of PlacementPassword to
            # determine if placement extraction has occured as it was added
            # in stein while the service extraction was delayed to train.
            # Inspect the endpoint map instead.
            endpoint_res = heat.resources.get(container, 'EndpointMap')
            endpoints = endpoint_res.attributes.get('endpoint_map', None)
            placement_extracted = endpoints and 'PlacementPublic' in endpoints
        except heat_exc.HTTPNotFound:
            pass

    passwords = password_utils.generate_passwords(
        stack_env=stack_env,
        rotate_passwords=rotate_passwords,
        rotate_pw_list=rotate_pw_list)

    # NOTE(ansmith): if rabbit password previously generated and
    # stored, facilitate upgrade and use for oslo messaging in plan env
    if 'RabbitPassword' in passwords:
        for i in ('RpcPassword', 'NotifyPassword'):
            if i not in passwords:
                passwords[i] = passwords['RabbitPassword']

    # NOTE(owalsh): placement previously used NovaPassword
    # Default to the same password for PlacementPassword if it is an
    # upgrade (i.e NovaPassword is set) so we do not need to update the
    # password in keystone
    if not placement_extracted and 'NovaPassword' in passwords:
        LOG.debug('Setting PlacementPassword to NovaPassword')
        passwords['PlacementPassword'] = passwords['NovaPassword']

    return passwords