def delete(self, certificate_uuid): """Uninstall a certificate.""" # Only support ssl_ca cert type log_start = cutils.timestamped("certificate_do_delete_start") try: certificate = pecan.request.dbapi.certificate_get(certificate_uuid) except exception.InvalidParameterValue: raise wsme.exc.ClientSideError( _("No certificate found for %s" % certificate_uuid)) if certificate and \ certificate.certtype not in [constants.CERT_MODE_SSL_CA]: msg = "Unupported mode: {}".format(certificate.certtype) raise wsme.exc.ClientSideError(_(msg)) LOG.info("certificate %s certificate_uuid=%s" % (log_start, certificate_uuid)) try: pecan.request.rpcapi.delete_certificate(pecan.request.context, certificate.certtype, certificate.signature) except RemoteError as e: msg = "Exception occurred e={}".format(e) LOG.warn(msg) raise wsme.exc.ClientSideError( _("Failed to delete the certificate: %s, %s" % (certificate_uuid, str(e.value)))) pecan.request.dbapi.certificate_destroy(certificate_uuid) log_end = cutils.timestamped("certificate_do_delete_end") LOG.info("certificate %s" % log_end) return Certificate.convert_with_links(certificate)
def certificate_install(self): """Install the certificate. Certificates are installed according to one of the following modes: default: install certificate for ssl tpm_mode: install certificate to tpm devices for ssl docker_registry: install certificate for docker registry openstack: install certificate for openstack openstack_ca: install ca certificate for openstack """ log_start = cutils.timestamped("certificate_do_post_start") fileitem = pecan.request.POST['file'] passphrase = pecan.request.POST.get('passphrase') mode = pecan.request.POST.get('mode') LOG.info("certificate %s mode=%s" % (log_start, mode)) if mode and mode not in constants.CERT_MODES_SUPPORTED: msg = "Invalid mode: %s" % mode LOG.info(msg) return dict(success="", error=msg) elif not mode: # Default certificate install is non-tpm SSL mode = constants.CERT_MODE_SSL system = pecan.request.dbapi.isystem_get_one() capabilities = system.capabilities # platform-cert 'force' check for backward compatibility if mode == constants.CERT_MODE_SSL: # Call may not contain 'force' parameter # Note: cert-mon will pass a HTTP POST 'force'='true' param force = pecan.request.POST.get('force') if force == 'true': force = True else: force = False # if PLATFORM_CERT_SECRET_NAME secret is present in k8s, we # assume that SSL cert is managed by cert-manager/cert-mon managed_by_cm = self._kube_op.kube_get_secret( constants.PLATFORM_CERT_SECRET_NAME, constants.CERT_NAMESPACE_PLATFORM_CERTS) if force is False and managed_by_cm is not None: msg = "Certificate is currently being managed by cert-manager. \n" \ "To manage certificate with this command, first delete " \ "the %s Certificate and Secret." % constants.PLATFORM_CERT_SECRET_NAME LOG.info(msg) return dict(success="", error=msg) standalone_certs = [ constants.CERT_MODE_DOCKER_REGISTRY, constants.CERT_MODE_SSL_CA ] if mode not in standalone_certs: system_https_enabled = capabilities.get('https_enabled', False) if system_https_enabled is False or system_https_enabled == 'n': msg = "No certificates have been added, https is not enabled." LOG.info(msg) return dict(success="", error=msg) if mode.startswith(constants.CERT_MODE_OPENSTACK): try: pecan.request.dbapi.certificate_get_by_certtype( constants.CERT_MODE_SSL) except exception.CertificateTypeNotFound: try: pecan.request.dbapi.certificate_get_by_certtype( constants.CERT_MODE_TPM) except exception.CertificateTypeNotFound: msg = "No openstack certificates have been added, " \ "platform SSL certificate is not installed." LOG.info(msg) return dict(success="", error=msg) if not fileitem.filename: return dict(success="", error="Error: No file uploaded") try: fileitem.file.seek(0, os.SEEK_SET) pem_contents = fileitem.file.read() except Exception as e: return dict(success="", error=("No certificates have been added, " "invalid PEM document: %s" % e)) # Extract the certificates from the pem file try: certs = cutils.extract_certs_from_pem(pem_contents) except Exception as e: msg = "No certificates have been added, %s" % e return dict(success="", error=msg) if not certs: msg = "No certificates have been added, " \ "no valid certificates found in file." LOG.info(msg) return dict(success="", error=msg) hash_issuers = [] for index, cert in enumerate(certs): msg = self._check_cert_validity(cert) if msg is not True: return dict(success="", error=msg) # validation checking for ssl, tpm_mode, docker_registry # and openstack certficcates if mode in [ constants.CERT_MODE_SSL, constants.CERT_MODE_TPM, constants.CERT_MODE_DOCKER_REGISTRY, constants.CERT_MODE_OPENSTACK, ]: try: hash_issuers.append(cutils.get_cert_issuer_hash(cert)) if index == 0: if cutils.is_ca_cert(cert): msg = "The first cert in the file should not be " \ "a CA cert" return dict(success="", error=msg) else: if not cutils.is_ca_cert(cert): msg = "Number %s cert in the file should be a " \ "CA cert" % (index + 1) return dict(success="", error=msg) hash_subject = cutils.get_cert_subject_hash(cert) if hash_subject != hash_issuers[index - 1]: msg = "Number %s cert in the file is not " \ "signing cert of the preceding one. Check " \ "certs order in the file." % (index + 1) return dict(success="", error=msg) except Exception as e: msg = "No certificates have been added, exception " \ "occured on cert %s: %s" % (index, e) return dict(success="", error=msg) if mode == constants.CERT_MODE_OPENSTACK and index == 0: domain, msg = _check_endpoint_domain_exists() if domain: msg = _check_cert_dns_name(cert, domain) if msg is not True: return dict(success="", error=msg.message) elif msg: return dict(success="", error=msg) if mode == constants.CERT_MODE_TPM: try: tpm = pecan.request.dbapi.tpmconfig_get_one() except exception.NotFound: tpm = None pass if tpm: tpmdevices = pecan.request.dbapi.tpmdevice_get_list() # if any of the tpm devices are in APPLYING state # then disallow a modification until previous config # either applies or fails for device in tpmdevices: if device.state == constants.TPMCONFIG_APPLYING: msg = ("TPM Device %s is still in APPLYING state. " "Wait for the configuration to finish " "before attempting a modification." % device.uuid) LOG.info(msg) return dict(success="", error=msg) try: config_dict = { 'passphrase': passphrase, 'mode': mode, } inv_certs = pecan.request.rpcapi.config_certificate( pecan.request.context, pem_contents, config_dict) except RemoteError as e: msg = "Exception occurred e={}".format(e) LOG.warn(msg) return dict(success="", error=str(e.value), body="", certificates={}) certificates = pecan.request.dbapi.certificate_get_list() # ssl and ssl_tpm certs are mutual exclusive, so # if the new cert is a SSL cert, delete the existing TPM cert as well # if the new cert is a TPM cert, delete the existing SSL cert as well for certificate in certificates: if (mode == constants.CERT_MODE_SSL and certificate.certtype == constants.CERT_MODE_TPM) or \ (mode == constants.CERT_MODE_TPM and certificate.certtype == constants.CERT_MODE_SSL): pecan.request.dbapi.certificate_destroy(certificate.uuid) # Create new or update existing certificates in sysinv with the # information returned from conductor manager. certificate_dicts = [] for inv_cert in inv_certs: # for ssl, tmp_mode, docker_registry and openstack certs, if the # cert is ICA signed cert (ie, the pem_contents contains # intermediate CA certs), skip these intermediate CA certs. if mode in [constants.CERT_MODE_SSL, constants.CERT_MODE_TPM, constants.CERT_MODE_DOCKER_REGISTRY, constants.CERT_MODE_OPENSTACK] \ and inv_cert.get('is_ca', None): continue values = { 'certtype': mode, 'signature': inv_cert.get('signature'), 'start_date': inv_cert.get('not_valid_before'), 'expiry_date': inv_cert.get('not_valid_after'), } LOG.info("config_certificate values=%s" % values) # check to see if the installed cert exist in sysinv uuid = None for certificate in certificates: if mode == constants.CERT_MODE_SSL_CA: if inv_cert.get('signature') == certificate.signature: uuid = certificate.uuid break else: if mode == certificate.certtype: uuid = certificate.uuid break if uuid: certificate = pecan.request.dbapi.certificate_update( uuid, values) else: certificate = pecan.request.dbapi.certificate_create(values) certificate_dict = certificate.as_dict() LOG.debug( "certificate_install certificate={}".format(certificate_dict)) certificate_dicts.append(certificate_dict) log_end = cutils.timestamped("certificate_do_post_end") LOG.info("certificate %s" % log_end) return dict(success="", error="", body="", certificates=certificate_dicts)
def certificate_install(self): """Install the certificate. Certificates are installed according to one of the following modes: default: install certificate for ssl tpm_mode: install certificate to tpm devices for ssl docker_registry: install certificate for docker registry openstack: install certificate for openstack openstack_ca: install ca certificate for openstack """ log_start = cutils.timestamped("certificate_do_post_start") fileitem = pecan.request.POST['file'] passphrase = pecan.request.POST.get('passphrase') mode = pecan.request.POST.get('mode') LOG.info("certificate %s mode=%s" % (log_start, mode)) if mode and mode not in constants.CERT_MODES_SUPPORTED: msg = "Invalid mode: %s" % mode LOG.info(msg) return dict(success="", error=msg) elif not mode: # Default certificate install is non-tpm SSL mode = constants.CERT_MODE_SSL system = pecan.request.dbapi.isystem_get_one() capabilities = system.capabilities standalone_certs = [ constants.CERT_MODE_DOCKER_REGISTRY, constants.CERT_MODE_SSL_CA ] if mode not in standalone_certs: system_https_enabled = capabilities.get('https_enabled', False) if system_https_enabled is False or system_https_enabled == 'n': msg = "No certificates have been added, https is not enabled." LOG.info(msg) return dict(success="", error=msg) if mode.startswith(constants.CERT_MODE_OPENSTACK): try: pecan.request.dbapi.certificate_get_by_certtype( constants.CERT_MODE_SSL) except exception.CertificateTypeNotFound: msg = "No openstack certificates have been added, " \ "platform SSL certificate is not installed." LOG.info(msg) return dict(success="", error=msg) if not fileitem.filename: return dict(success="", error="Error: No file uploaded") try: fileitem.file.seek(0, os.SEEK_SET) pem_contents = fileitem.file.read() except Exception as e: return dict(success="", error=("No certificates have been added, " "invalid PEM document: %s" % e)) # Extract the certificate from the pem file cert = x509.load_pem_x509_certificate(pem_contents, default_backend()) msg = self._check_cert_validity(cert) if msg is not True: return dict(success="", error=msg) if mode == constants.CERT_MODE_OPENSTACK: domain, msg = _check_endpoint_domain_exists() if domain: msg = _check_cert_dns_name(cert, domain) if msg is not True: return dict(success="", error=msg.message) elif msg: return dict(success="", error=msg) if mode == constants.CERT_MODE_TPM: try: tpm = pecan.request.dbapi.tpmconfig_get_one() except exception.NotFound: tpm = None pass if tpm: tpmdevices = pecan.request.dbapi.tpmdevice_get_list() # if any of the tpm devices are in APPLYING state # then disallow a modification until previous config # either applies or fails for device in tpmdevices: if device.state == constants.TPMCONFIG_APPLYING: msg = ("TPM Device %s is still in APPLYING state. " "Wait for the configuration to finish " "before attempting a modification." % device.uuid) LOG.info(msg) return dict(success="", error=msg) try: config_dict = { 'passphrase': passphrase, 'mode': mode, } signature = pecan.request.rpcapi.config_certificate( pecan.request.context, pem_contents, config_dict) except Exception as e: msg = "Exception occurred e={}".format(e) LOG.info(msg) return dict(success="", error=str(e), body="", certificates={}) # Update with installed certificate information values = { 'certtype': mode, # TODO(jkung) 'issuer': cert.issuer, 'signature': signature, 'start_date': cert.not_valid_before, 'expiry_date': cert.not_valid_after, } LOG.info("config_certificate values=%s" % values) if mode in [constants.CERT_MODE_SSL, constants.CERT_MODE_TPM]: if mode == constants.CERT_MODE_SSL: remove_certtype = constants.CERT_MODE_TPM else: remove_certtype = constants.CERT_MODE_SSL try: remove_certificate = \ pecan.request.dbapi.certificate_get_by_certtype( remove_certtype) LOG.info("remove certificate certtype=%s uuid`=%s" % (remove_certtype, remove_certificate.uuid)) pecan.request.dbapi.certificate_destroy( remove_certificate.uuid) except exception.CertificateTypeNotFound: pass try: certificate = \ pecan.request.dbapi.certificate_get_by_certtype( mode) certificate = \ pecan.request.dbapi.certificate_update(certificate.uuid, values) except exception.CertificateTypeNotFound: certificate = pecan.request.dbapi.certificate_create(values) pass sp_certificates_dict = certificate.as_dict() LOG.debug("certificate_install sp_certificates={}".format( sp_certificates_dict)) log_end = cutils.timestamped("certificate_do_post_end") LOG.info("certificate %s" % log_end) return dict(success="", error="", body="", certificates=sp_certificates_dict)