Add a TLS scenario using Barbican
This patch adds a TLS load balancer scenario test using Barbican.
Story: 1627383
Task: 5149
Change-Id: I7013888f94261d94e1cd4c3167dc84da7125d1da
diff --git a/octavia_tempest_plugin/common/barbican_client_mgr.py b/octavia_tempest_plugin/common/barbican_client_mgr.py
new file mode 100644
index 0000000..e93f903
--- /dev/null
+++ b/octavia_tempest_plugin/common/barbican_client_mgr.py
@@ -0,0 +1,88 @@
+# Copyright 2019 Rackspace US Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from barbicanclient import client
+from keystoneauth1 import identity
+from keystoneauth1 import session
+from oslo_log import log as logging
+from tempest.lib.common.utils import data_utils
+
+LOG = logging.getLogger(__name__)
+
+
+class BarbicanClientManager(object):
+ """Class for interacting with the barbican service.
+
+ This class is an abstraction for interacting with the barbican service.
+ This class currently uses the barbican client code to access barbican due
+ to the following reasons:
+ 1. Octavia users typically load secrets into barbican via the client.
+ 2. The barbican-tempest-plugin is lightly tested (no py3 tests, etc.).
+ 3. barbican-tempest-plugin is not in global requirements.
+
+ This led to the decision to not use the service client in the
+ barbican-tempest-plugin.
+
+ In the future it may be better to use the barbican-tempest-plugin
+ service client or the openstacksdk.
+ """
+
+ def __init__(self, tempest_client_mgr):
+ """Setup the barbican client.
+
+ :param tempest_client_mgr: A tempest client manager object, such as
+ os_primary.
+ """
+ # Convert the tempest credential passed in into a keystone session
+ auth_provider = tempest_client_mgr.auth_provider
+ cert_validation = False
+ if not auth_provider.dscv:
+ cert_validation = auth_provider.ca_certs
+ credentials = tempest_client_mgr.credentials
+ keystone_auth = identity.v3.Token(
+ auth_url=auth_provider.auth_url,
+ token=auth_provider.get_token(),
+ project_id=credentials.project_id,
+ project_name=credentials.project_name,
+ project_domain_id=credentials.project_domain_id,
+ project_domain_name=credentials.project_domain_name)
+ id_session = session.Session(auth=keystone_auth,
+ verify=cert_validation)
+
+ # Setup the barbican client
+ self.barbican = client.Client(session=id_session)
+
+ def store_secret(self, pkcs12_secret):
+ """Store a secret in barbican.
+
+ :param pkcs12_secret: A pkcs12 secret.
+ :returns: The barbican secret_ref.
+ """
+ p12_secret = self.barbican.secrets.create()
+ p12_secret.name = data_utils.rand_name("lb_member_barbican_pkcs12")
+ p12_secret.payload = pkcs12_secret
+ secret_ref = p12_secret.store()
+ LOG.debug('Secret {0} has ref {1}'.format(p12_secret.name, secret_ref))
+ return secret_ref
+
+ def delete_secret(self, secret_ref):
+ self.barbican.secrets.delete(secret_ref)
+
+ def add_acl(self, secret_ref, user_id):
+ acl_entity = self.barbican.acls.create(entity_ref=secret_ref,
+ users=[user_id],
+ project_access=True)
+ acl_ref = acl_entity.submit()
+ LOG.debug('Secret ACL {0} added user {1}'.format(acl_ref, user_id))
+ return acl_ref
diff --git a/octavia_tempest_plugin/common/cert_utils.py b/octavia_tempest_plugin/common/cert_utils.py
new file mode 100644
index 0000000..dcdd6f0
--- /dev/null
+++ b/octavia_tempest_plugin/common/cert_utils.py
@@ -0,0 +1,130 @@
+# Copyright 2018 Rackspace US Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import datetime
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.hazmat.primitives import hashes
+from cryptography import x509
+from cryptography.x509.oid import NameOID
+import OpenSSL
+
+
+def generate_ca_cert_and_key():
+ """Creates a CA cert and key for testing.
+
+ :returns: The cryptography CA cert and CA key objects.
+ """
+
+ ca_key = rsa.generate_private_key(
+ public_exponent=65537, key_size=2048, backend=default_backend())
+
+ subject = issuer = x509.Name([
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
+ x509.NameAttribute(NameOID.COMMON_NAME, u"ca_cert.example.com"),
+ ])
+
+ ca_cert = x509.CertificateBuilder().subject_name(
+ subject
+ ).issuer_name(
+ issuer
+ ).public_key(
+ ca_key.public_key()
+ ).serial_number(
+ x509.random_serial_number()
+ ).not_valid_before(
+ datetime.datetime.utcnow()
+ ).not_valid_after(
+ datetime.datetime.utcnow() + datetime.timedelta(days=10)
+ ).add_extension(
+ x509.SubjectAlternativeName([x509.DNSName(u"ca_cert.example.com")]),
+ critical=False,
+ ).add_extension(
+ x509.BasicConstraints(ca=True, path_length=None),
+ critical=True,
+ ).sign(ca_key, hashes.SHA256(), default_backend())
+
+ return ca_cert, ca_key
+
+
+def generate_server_cert_and_key(ca_cert, ca_key, server_uuid):
+ """Creates a server cert and key for testing.
+
+ :param ca_cert: A cryptography CA certificate (x509) object.
+ :param ca_key: A cryptography CA key (x509) object.
+ :param server_uuid: A UUID identifying the server.
+ :returns: The cryptography server cert and key objects.
+ """
+
+ server_key = rsa.generate_private_key(
+ public_exponent=65537, key_size=2048, backend=default_backend())
+
+ subject = x509.Name([
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Denial"),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, u"Corvallis"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"OpenStack"),
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"Octavia"),
+ x509.NameAttribute(NameOID.COMMON_NAME, u"{}.example.com".format(
+ server_uuid)),
+ ])
+
+ server_cert = x509.CertificateBuilder().subject_name(
+ subject
+ ).issuer_name(
+ ca_cert.subject
+ ).public_key(
+ server_key.public_key()
+ ).serial_number(
+ x509.random_serial_number()
+ ).not_valid_before(
+ datetime.datetime.utcnow()
+ ).not_valid_after(
+ datetime.datetime.utcnow() + datetime.timedelta(days=10)
+ ).add_extension(
+ x509.SubjectAlternativeName(
+ [x509.DNSName(u"{}.example.com".format(server_uuid))]),
+ critical=False,
+ ).add_extension(
+ x509.BasicConstraints(ca=False, path_length=None),
+ critical=True,
+ ).sign(ca_key, hashes.SHA256(), default_backend())
+
+ return server_cert, server_key
+
+
+def generate_pkcs12_bundle(server_cert, server_key):
+ """Creates a pkcs12 formated bundle.
+
+ Note: This uses pyOpenSSL as the cryptography package does not yet
+ support creating pkcs12 bundles. The currently un-released
+ 2.5 version of cryptography supports reading pkcs12, but not
+ creation. This method should be updated to only use
+ cryptography once it supports creating pkcs12 bundles.
+
+ :param server_cert: A cryptography certificate (x509) object.
+ :param server_key: A cryptography key (x509) object.
+ :returns: A pkcs12 bundle.
+ """
+ # TODO(johnsom) Replace with cryptography once it supports creating pkcs12
+ pkcs12 = OpenSSL.crypto.PKCS12()
+ pkcs12.set_privatekey(
+ OpenSSL.crypto.PKey.from_cryptography_key(server_key))
+ pkcs12.set_certificate(OpenSSL.crypto.X509.from_cryptography(server_cert))
+ return pkcs12.export()