From 2efd898eb74e98246867dff9a831e020292cdd41 Mon Sep 17 00:00:00 2001 From: Alex Kozyrev Date: Fri, 18 Jan 2019 16:04:13 -0500 Subject: [PATCH] Store BMC password in Openstack Barbican instead of keyring. Replacing existing mechanism of storing BMC passwords in SysInv. Implementing access to Barbican API in SysInv and using it to write the passwords into a Barbican secrets. Note that a Barbican cannot change the existing password inside its secret, so we need to remove the old secret and create a new one in case of password update. Another thing to mention: SysInv has to create Barbican secrets in context of "services" project in order MTCE can read them later. Change-Id: I7102a9662f3757c062ab310737f4ba08379d0100 Story: 2003108 Task: 27700 Signed-off-by: Alex Kozyrev --- .../src/hieradata/controller.yaml | 17 +- .../modules/openstack/manifests/barbican.pp | 9 +- sysinv/sysinv/centos/build_srpm.data | 2 +- .../sysinv/sysinv/api/controllers/v1/host.py | 25 +-- .../sysinv/sysinv/sysinv/conductor/manager.py | 36 ++-- .../sysinv/sysinv/conductor/openstack.py | 156 ++++++++++++++---- .../sysinv/sysinv/sysinv/conductor/rpcapi.py | 34 ++-- .../sysinv/sysinv/sysinv/puppet/barbican.py | 2 +- 8 files changed, 165 insertions(+), 116 deletions(-) diff --git a/puppet-manifests/src/hieradata/controller.yaml b/puppet-manifests/src/hieradata/controller.yaml index c7e1336342..ef0ab3bf11 100644 --- a/puppet-manifests/src/hieradata/controller.yaml +++ b/puppet-manifests/src/hieradata/controller.yaml @@ -546,20 +546,13 @@ fm::database_max_overflow: 20 fm::database_max_pool_size: 1 # Barbican -barbican::use_syslog: true -barbican::log_facility: 'local2' -barbican::database_idle_timeout: 60 -barbican::database_max_pool_size: 1 -barbican::database_max_overflow: 10 -barbican::alarm_history_time_to_live: 86400 - -barbican::auth::auth_endpoint_type: 'internalURL' - -barbican::db::sync::user: 'root' - barbican::api::enabled: false barbican::api::service_name: 'barbican-api' barbican::api::enable_proxy_headers_parsing: true - +barbican::api::logging::use_syslog: true +barbican::api::logging::log_facility: 'local2' +barbican::db::sync::user: 'root' +barbican::db::database_idle_timeout: 60 +barbican::db::database_max_pool_size: 1 barbican::keystone-listener::enabled: false barbican::worker::enabled: false diff --git a/puppet-manifests/src/modules/openstack/manifests/barbican.pp b/puppet-manifests/src/modules/openstack/manifests/barbican.pp index c8086682e6..dd1a5d14b7 100644 --- a/puppet-manifests/src/modules/openstack/manifests/barbican.pp +++ b/puppet-manifests/src/modules/openstack/manifests/barbican.pp @@ -55,12 +55,15 @@ class openstack::barbican::service } $api_fqdn = $::platform::params::controller_hostname $url_host = "http://${api_fqdn}:${api_port}" - + if str2bool($::is_initial_config_primary) { + $enabled = true + } else { + $enabled = false + } include ::platform::amqp::params class { '::barbican::api': - enabled => true, - manage_service => true, + enabled => $enabled, bind_host => $api_host, bind_port => $api_port, host_href => $url_host, diff --git a/sysinv/sysinv/centos/build_srpm.data b/sysinv/sysinv/centos/build_srpm.data index 2c13de6a0d..2d590250af 100644 --- a/sysinv/sysinv/centos/build_srpm.data +++ b/sysinv/sysinv/centos/build_srpm.data @@ -1,2 +1,2 @@ SRC_DIR="sysinv" -TIS_PATCH_VER=299 +TIS_PATCH_VER=300 diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index 300f4dd6f3..6d0f50affc 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -2440,15 +2440,9 @@ class HostController(rest.RestController): "problem persists then contact your system " "administrator.")) - # tell conductor to delete the keystore entry associated - # with this host (if present) - try: - pecan.request.rpcapi.unconfigure_keystore_account( - pecan.request.context, - KEYRING_BM_SERVICE, - ihost.uuid) - except exception.NotFound: - pass + # tell conductor to delete the barbican entry associated with this host (if present) + pecan.request.rpcapi.delete_barbican_secret(pecan.request.context, + host.uuid) # Notify patching to drop the host if ihost.hostname is not None: @@ -3795,7 +3789,7 @@ class HostController(rest.RestController): raise wsme.exc.ClientSideError( _("Host-add Rejected: bm_ip %s already exists") % phost['bm_ip']) - # Update keyring with updated board management credentials (if supplied) + # Update barbican with updated board management credentials (if supplied) if (ohost['bm_username'] and phost['bm_username'] and (ohost['bm_username'] != phost['bm_username'])): if not password_exists: @@ -3806,12 +3800,11 @@ class HostController(rest.RestController): ohost['bm_username'], phost['bm_username']))) - if password_exists: - # The conductor will handle creating the keystore acct - pecan.request.rpcapi.configure_keystore_account(pecan.request.context, - KEYRING_BM_SERVICE, - phost['uuid'], - patch_bm_password) + if password_exists and patch_bm_password: + pecan.request.rpcapi.create_barbican_secret(pecan.request.context, + phost['uuid'], + patch_bm_password) + LOG.info("%s bm semantic checks for user_agent %s passed" % (phost['hostname'], pecan.request.user_agent)) diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 84a35b2f59..bd678038a1 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -45,7 +45,6 @@ import uuid import xml.etree.ElementTree as ElementTree from contextlib import contextmanager -import keyring import tsconfig.tsconfig as tsc from cgcs_patch.patch_verify import verify_files from controllerconfig.upgrades import management as upgrades_management @@ -9428,38 +9427,23 @@ class ConductorManager(service.PeriodicService): target_load = self.dbapi.load_get(host_upgrade.target_load) return target_load.software_version == tsc.SW_VERSION - def configure_keystore_account(self, context, service_name, - username, password): - """Synchronously, have a conductor configure a ks(keyring) account. - - Does the following tasks: - - call keyring API to create an account under a service. + def create_barbican_secret(self, context, name, payload): + """Calls Barbican API to create a secret :param context: request context. - :param service_name: the keystore service. - :param username: account username - :param password: account password + :param name: secret name + :param payload: secret payload """ - if (not service_name.strip()): - raise exception.SysinvException(_( - "Keystore service is a blank value")) + self._openstack.create_barbican_secret(context=context, + name=name, payload=payload) - keyring.set_password(service_name, username, password) - - def unconfigure_keystore_account(self, context, service_name, username): - """Synchronously, have a conductor unconfigure a ks(keyring) account. - - Does the following tasks: - - call keyring API to delete an account under a service. + def delete_barbican_secret(self, context, name): + """Calls Barbican API to delete a secret :param context: request context. - :param service_name: the keystore service. - :param username: account username + :param name: secret name """ - try: - keyring.delete_password(service_name, username) - except keyring.errors.PasswordDeleteError: - pass + self._openstack.delete_barbican_secret(context=context, name=name) def update_snmp_config(self, context): """Update the snmpd configuration""" diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py b/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py index 28fc478630..517cc950cd 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/openstack.py @@ -26,7 +26,7 @@ from keystoneclient.auth.identity import v3 from keystoneclient import session from sqlalchemy.orm import exc from magnumclient.v1 import client as magnum_client_v1 - +from barbicanclient.v1 import client as barbican_client_v1 LOG = logging.getLogger(__name__) @@ -105,6 +105,9 @@ openstack_keystone_opts = [ cfg.StrOpt('magnum_region_name', default='RegionOne', help=_("Magnum Region Name")), + cfg.StrOpt('barbican_region_name', + default='RegionOne', + help=_("Barbican Region Name")), cfg.StrOpt('project_name', default='admin', help=_("keystone user project name")), @@ -129,9 +132,12 @@ class OpenStackOperator(object): def __init__(self, dbapi): self.dbapi = dbapi + self.barbican_client = None self.cinder_client = None self.keystone_client = None self.keystone_session = None + self.openstack_keystone_client = None + self.openstack_keystone_session = None self.magnum_client = None self.nova_client = None self.neutron_client = None @@ -530,29 +536,7 @@ class OpenStackOperator(object): ################# # Keystone ################# - def _get_keystone_session(self, service_config): - if not self.keystone_session: - if service_config == OPENSTACK_CONFIG: - password = keyring.get_password(cfg.CONF[OPENSTACK_CONFIG]. - keyring_service, - cfg.CONF[OPENSTACK_CONFIG]. - username) - else: - password = cfg.CONF[service_config].password - - auth = v3.Password(auth_url=self._get_auth_url(service_config), - username=cfg.CONF[service_config].username, - password=password, - user_domain_name=cfg.CONF[service_config]. - user_domain_name, - project_name=cfg.CONF[service_config]. - project_name, - project_domain_name=cfg.CONF[service_config]. - project_domain_name) - self.keystone_session = session.Session(auth=auth) - return self.keystone_session - - def _get_keystoneclient(self, service_config): + def _get_keystone_password(self, service_config): if service_config == OPENSTACK_CONFIG: password = keyring.get_password(cfg.CONF[OPENSTACK_CONFIG]. keyring_service, @@ -560,18 +544,70 @@ class OpenStackOperator(object): username) else: password = cfg.CONF[service_config].password + return password - if not self.keystone_client: # should not cache this forever - self.keystone_client = keystone_client.Client( - username=cfg.CONF[service_config].username, - user_domain_name=cfg.CONF[service_config].user_domain_name, - project_name=cfg.CONF[service_config].project_name, - project_domain_name=cfg.CONF[service_config] - .project_domain_name, - password=password, - auth_url=self._get_auth_url(service_config), - region_name=cfg.CONF[service_config].region_name) - return self.keystone_client + def _get_new_keystone_session(self, service_config): + auth = v3.Password(auth_url=self._get_auth_url(service_config), + username=cfg.CONF[service_config].username, + password=self._get_keystone_password(service_config), + user_domain_name=cfg.CONF[service_config]. + user_domain_name, + project_name=cfg.CONF[service_config]. + project_name, + project_domain_name=cfg.CONF[service_config]. + project_domain_name) + sess = session.Session(auth=auth) + return sess + + def _get_cached_keystone_session(self, service_config): + if service_config == OPENSTACK_CONFIG: + return self.openstack_keystone_session + else: + return self.keystone_session + + def _set_cached_keystone_session(self, service_config, sess): + if service_config == OPENSTACK_CONFIG: + self.openstack_keystone_session = sess + else: + self.keystone_session = sess + + def _get_keystone_session(self, service_config): + sess = self._get_cached_keystone_session(service_config) + if not sess: + sess = self._get_new_keystone_session(service_config) + self._set_cached_keystone_session(service_config, sess) + return sess + + def _get_new_keystone_client(self, service_config): + client = keystone_client.Client( + username=cfg.CONF[service_config].username, + user_domain_name=cfg.CONF[service_config].user_domain_name, + project_name=cfg.CONF[service_config].project_name, + project_domain_name=cfg.CONF[service_config] + .project_domain_name, + password=self._get_keystone_password(service_config), + auth_url=self._get_auth_url(service_config), + region_name=cfg.CONF[service_config].region_name) + return client + + def _get_cached_keystone_client(self, service_config): + if service_config == OPENSTACK_CONFIG: + return self.openstack_keystone_client + else: + return self.keystone_client + + def _set_cached_keystone_client(self, service_config, client): + if service_config == OPENSTACK_CONFIG: + self.openstack_keystone_client = client + else: + self.keystone_client = client + + def _get_keystone_client(self, service_config): + client = self._get_cached_keystone_client(service_config) + if not client: + client = self._get_new_keystone_client(service_config) + self._set_cached_keystone_client(service_config, client) + return client ################# # Cinder @@ -785,6 +821,56 @@ class OpenStackOperator(object): LOG.error("Unable to get backend list of magnum clusters") return 0 + ################# + # Barbican + ################# + def _get_barbicanclient(self): + if not self.barbican_client: + self.barbican_client = barbican_client_v1.Client( + session=self._get_keystone_session(PLATFORM_CONFIG), + interface='internalURL', + region_name=cfg.CONF[OPENSTACK_CONFIG].barbican_region_name) + return self.barbican_client + + def get_barbican_secret_by_name(self, context, name): + try: + client = self._get_barbicanclient() + secret_list = client.secrets.list(name=name) + secret = next(iter(secret_list), None) + return secret + except Exception: + LOG.error("Unable to find Barbican secret %s", name) + return None + + def create_barbican_secret(self, context, name, payload): + if not payload: + LOG.error("Empty password is passed to Barbican %s" % name) + return None + try: + client = self._get_barbicanclient() + secret = self.get_barbican_secret_by_name(context, name) + if secret: + client.secrets.delete(secret.secret_ref) + secret = client.secrets.create(name, payload) + secret.store() + return secret.secret_ref + except Exception: + LOG.error("Unable to create Barbican secret %s" % name) + return None + + def delete_barbican_secret(self, context, name): + try: + client = self._get_barbicanclient() + secret = self.get_barbican_secret_by_name(context=context, name=name) + if not secret: + LOG.error("Unable to delete unknown Barbican secret %s" % name) + return False + client.secrets.delete(secret_ref=secret.secret_ref) + return True + except Exception: + LOG.error("Unable to delete Barbican secret %s" % name) + return False + def get_region_name(region): # get region name from platform.conf diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index 3c064f6a56..0c8e866302 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -1343,37 +1343,27 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): return self.call(context, self.make_msg('update_vswitch_type')) - def configure_keystore_account(self, context, service_name, - username, password): - """Synchronously, have a conductor configure a ks(keyring) account. - - Does the following tasks: - - call keyring API to create an account under a service. + def create_barbican_secret(self, context, name, payload): + """Calls Barbican API to create a secret :param context: request context. - :param service_name: the keystore service. - :param username: account username - :param password: account password + :param name: secret name + :param payload: secret payload """ return self.call(context, - self.make_msg('configure_keystore_account', - service_name=service_name, - username=username, password=password)) + self.make_msg('create_barbican_secret', + name=name, + payload=payload)) - def unconfigure_keystore_account(self, context, service_name, username): - """Synchronously, have a conductor unconfigure a ks(keyring) account. - - Does the following tasks: - - call keyring API to delete an account under a service. + def delete_barbican_secret(self, context, name): + """Calls Barbican API to delete a secret :param context: request context. - :param service_name: the keystore service. - :param username: account username + :param name: secret name """ return self.call(context, - self.make_msg('unconfigure_keystore_account', - service_name=service_name, - username=username)) + self.make_msg('delete_barbican_secret', + name=name)) def update_snmp_config(self, context): """Synchronously, have a conductor configure the SNMP configuration. diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/barbican.py b/sysinv/sysinv/sysinv/sysinv/puppet/barbican.py index c108949852..b20f9733af 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/barbican.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/barbican.py @@ -59,7 +59,7 @@ class BarbicanPuppet(openstack.OpenstackBasePuppet): 'barbican::keystone::authtoken::project_domain_name': self._get_service_project_domain_name(), 'barbican::keystone::authtoken::project_name': - self._get_service_tenant_name(), + self._get_service_project_name(), 'barbican::keystone::authtoken::region_name': self._keystone_region_name(), 'barbican::keystone::authtoken::username': ksuser,