Add a TLS scenario using Barbican
This patch adds a TLS load balancer scenario test using Barbican. Story: 1627383 Task: 5149 Change-Id: I7013888f94261d94e1cd4c3167dc84da7125d1da
This commit is contained in:
parent
f3f25825c2
commit
0a0f9b342a
|
@ -2,7 +2,7 @@
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
sphinxcontrib-apidoc # BSD
|
sphinxcontrib-apidoc>=0.2.0 # BSD
|
||||||
sphinx>=1.6.2,!=1.6.6,!=1.6.7 # BSD
|
sphinx>=1.6.2,!=1.6.6,!=1.6.7 # BSD
|
||||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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()
|
|
@ -14,9 +14,13 @@
|
||||||
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from octavia_tempest_plugin.common import constants as const
|
from octavia_tempest_plugin.common import constants as const
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
service_available_group = cfg.OptGroup(name='service_available',
|
service_available_group = cfg.OptGroup(name='service_available',
|
||||||
title='Available OpenStack Services')
|
title='Available OpenStack Services')
|
||||||
|
|
||||||
|
@ -27,6 +31,19 @@ ServiceAvailableGroup = [
|
||||||
"to be available."),
|
"to be available."),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Pull in the service_available for barbican if it is not defined.
|
||||||
|
# If the barbican tempest plugin isn't loaded, this won't load from
|
||||||
|
# tempest.conf.
|
||||||
|
try:
|
||||||
|
if cfg.CONF.service_available.barbican is not None:
|
||||||
|
LOG.info('Barbican service_available state: {}'.format(
|
||||||
|
cfg.CONF.service_available.barbican))
|
||||||
|
except cfg.NoSuchOptError:
|
||||||
|
ServiceAvailableGroup.append(
|
||||||
|
cfg.BoolOpt('barbican', default=False,
|
||||||
|
help="Whether or not the barbican service is expected to "
|
||||||
|
"be available."))
|
||||||
|
|
||||||
octavia_group = cfg.OptGroup(name='load_balancer',
|
octavia_group = cfg.OptGroup(name='load_balancer',
|
||||||
title='load-balancer service options')
|
title='load-balancer service options')
|
||||||
|
|
||||||
|
@ -54,6 +71,9 @@ OctaviaGroup = [
|
||||||
default=300,
|
default=300,
|
||||||
help='Timeout in seconds to wait for non-load-balancer '
|
help='Timeout in seconds to wait for non-load-balancer '
|
||||||
'resources to build'),
|
'resources to build'),
|
||||||
|
cfg.StrOpt('octavia_svc_username', default='admin',
|
||||||
|
help='The service_auth username the Octavia services are using'
|
||||||
|
'to access other OpenStack services.'),
|
||||||
# load-balancer specific options
|
# load-balancer specific options
|
||||||
cfg.IntOpt('check_interval',
|
cfg.IntOpt('check_interval',
|
||||||
default=5,
|
default=5,
|
||||||
|
@ -152,4 +172,8 @@ LBFeatureEnabledGroup = [
|
||||||
default=True,
|
default=True,
|
||||||
help="Whether Health Monitor is available with provider"
|
help="Whether Health Monitor is available with provider"
|
||||||
" driver or not."),
|
" driver or not."),
|
||||||
|
cfg.BoolOpt('terminated_tls_enabled',
|
||||||
|
default=True,
|
||||||
|
help="Whether TLS termination is available with provider "
|
||||||
|
"driver or not."),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from OpenSSL.crypto import X509
|
||||||
|
from OpenSSL import SSL
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
from tempest import config
|
||||||
|
from tempest.lib.common.utils import data_utils
|
||||||
|
from tempest.lib import decorators
|
||||||
|
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
|
|
||||||
|
from octavia_tempest_plugin.common import barbican_client_mgr
|
||||||
|
from octavia_tempest_plugin.common import cert_utils
|
||||||
|
from octavia_tempest_plugin.common import constants as const
|
||||||
|
from octavia_tempest_plugin.tests import test_base
|
||||||
|
from octavia_tempest_plugin.tests import waiters
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TLSWithBarbicanTest(test_base.LoadBalancerBaseTestWithCompute):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def skip_checks(cls):
|
||||||
|
super(TLSWithBarbicanTest, cls).skip_checks()
|
||||||
|
if not CONF.loadbalancer_feature_enabled.terminated_tls_enabled:
|
||||||
|
raise cls.skipException('[loadbalancer-feature-enabled] '
|
||||||
|
'"terminated_tls_enabled" is False in '
|
||||||
|
'the tempest configuration. TLS tests '
|
||||||
|
'will be skipped.')
|
||||||
|
if not CONF.validation.run_validation:
|
||||||
|
raise cls.skipException('Traffic tests will not work without '
|
||||||
|
'run_validation enabled.')
|
||||||
|
if not CONF.service_available.barbican:
|
||||||
|
raise cls.skipException('TLS with Barbican tests require the '
|
||||||
|
'barbican service.')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
"""Setup resources needed by the tests."""
|
||||||
|
super(TLSWithBarbicanTest, cls).resource_setup()
|
||||||
|
|
||||||
|
# Create a CA self-signed cert and key
|
||||||
|
cls.ca_cert, ca_key = cert_utils.generate_ca_cert_and_key()
|
||||||
|
|
||||||
|
LOG.debug('CA Cert: %s' % cls.ca_cert.public_bytes(
|
||||||
|
serialization.Encoding.PEM))
|
||||||
|
LOG.debug('CA private Key: %s' % ca_key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.NoEncryption()))
|
||||||
|
LOG.debug('CA public Key: %s' % ca_key.public_key().public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo))
|
||||||
|
|
||||||
|
# Create a server cert and key
|
||||||
|
cls.server_uuid = uuidutils.generate_uuid()
|
||||||
|
server_cert, server_key = cert_utils.generate_server_cert_and_key(
|
||||||
|
cls.ca_cert, ca_key, cls.server_uuid)
|
||||||
|
|
||||||
|
LOG.debug('Server Cert: %s' % server_cert.public_bytes(
|
||||||
|
serialization.Encoding.PEM))
|
||||||
|
LOG.debug('Server private Key: %s' % server_key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.NoEncryption()))
|
||||||
|
server_public_key = server_key.public_key()
|
||||||
|
LOG.debug('Server public Key: %s' % server_public_key.public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo))
|
||||||
|
|
||||||
|
# Create the pkcs12 bundle
|
||||||
|
pkcs12 = cert_utils.generate_pkcs12_bundle(server_cert, server_key)
|
||||||
|
LOG.debug('Server PKCS12 bundle: %s' % base64.b64encode(pkcs12))
|
||||||
|
|
||||||
|
# Load the secret into the barbican service under the
|
||||||
|
# os_roles_lb_member tenant
|
||||||
|
barbican_mgr = barbican_client_mgr.BarbicanClientManager(
|
||||||
|
cls.os_roles_lb_member)
|
||||||
|
|
||||||
|
cls.secret_ref = barbican_mgr.store_secret(pkcs12)
|
||||||
|
cls.addClassResourceCleanup(barbican_mgr.delete_secret, cls.secret_ref)
|
||||||
|
|
||||||
|
# Set the barbican ACL if the Octavia API version doesn't do it
|
||||||
|
# automatically.
|
||||||
|
if not cls.mem_lb_client.is_version_supported(
|
||||||
|
cls.api_version, '2.1'):
|
||||||
|
user_list = cls.os_admin.users_v3_client.list_users(
|
||||||
|
name=CONF.load_balancer.octavia_svc_username)
|
||||||
|
msg = 'Only one user named "{0}" should exist, {1} found.'.format(
|
||||||
|
CONF.load_balancer.octavia_svc_username,
|
||||||
|
len(user_list['users']))
|
||||||
|
assert 1 == len(user_list['users']), msg
|
||||||
|
barbican_mgr.add_acl(cls.secret_ref, user_list['users'][0]['id'])
|
||||||
|
|
||||||
|
# Setup a load balancer for the tests to use
|
||||||
|
lb_name = data_utils.rand_name("lb_member_lb1-tls")
|
||||||
|
lb_kwargs = {const.PROVIDER: CONF.load_balancer.provider,
|
||||||
|
const.NAME: lb_name}
|
||||||
|
|
||||||
|
# TODO(johnsom) Update for IPv6
|
||||||
|
cls._setup_lb_network_kwargs(lb_kwargs, 4)
|
||||||
|
|
||||||
|
lb = cls.mem_lb_client.create_loadbalancer(**lb_kwargs)
|
||||||
|
cls.lb_id = lb[const.ID]
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
cls.mem_lb_client.cleanup_loadbalancer,
|
||||||
|
cls.lb_id)
|
||||||
|
|
||||||
|
waiters.wait_for_status(cls.mem_lb_client.show_loadbalancer,
|
||||||
|
cls.lb_id, const.PROVISIONING_STATUS,
|
||||||
|
const.ACTIVE,
|
||||||
|
CONF.load_balancer.lb_build_interval,
|
||||||
|
CONF.load_balancer.lb_build_timeout)
|
||||||
|
|
||||||
|
if CONF.validation.connect_method == 'floating':
|
||||||
|
port_id = lb[const.VIP_PORT_ID]
|
||||||
|
result = cls.lb_mem_float_ip_client.create_floatingip(
|
||||||
|
floating_network_id=CONF.network.public_network_id,
|
||||||
|
port_id=port_id)
|
||||||
|
floating_ip = result['floatingip']
|
||||||
|
LOG.info('lb1_floating_ip: {}'.format(floating_ip))
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
waiters.wait_for_not_found,
|
||||||
|
cls.lb_mem_float_ip_client.delete_floatingip,
|
||||||
|
cls.lb_mem_float_ip_client.show_floatingip,
|
||||||
|
floatingip_id=floating_ip['id'])
|
||||||
|
cls.lb_vip_address = floating_ip['floating_ip_address']
|
||||||
|
else:
|
||||||
|
cls.lb_vip_address = lb[const.VIP_ADDRESS]
|
||||||
|
|
||||||
|
pool_name = data_utils.rand_name("lb_member_pool1-tls")
|
||||||
|
pool_kwargs = {
|
||||||
|
const.NAME: pool_name,
|
||||||
|
const.PROTOCOL: const.HTTP,
|
||||||
|
const.LB_ALGORITHM: const.LB_ALGORITHM_ROUND_ROBIN,
|
||||||
|
const.LOADBALANCER_ID: cls.lb_id,
|
||||||
|
}
|
||||||
|
pool = cls.mem_pool_client.create_pool(**pool_kwargs)
|
||||||
|
cls.pool_id = pool[const.ID]
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
cls.mem_pool_client.cleanup_pool,
|
||||||
|
cls.pool_id,
|
||||||
|
lb_client=cls.mem_lb_client, lb_id=cls.lb_id)
|
||||||
|
|
||||||
|
waiters.wait_for_status(cls.mem_lb_client.show_loadbalancer,
|
||||||
|
cls.lb_id, const.PROVISIONING_STATUS,
|
||||||
|
const.ACTIVE,
|
||||||
|
CONF.load_balancer.build_interval,
|
||||||
|
CONF.load_balancer.build_timeout)
|
||||||
|
|
||||||
|
# Set up Member 1 for Webserver 1
|
||||||
|
member1_name = data_utils.rand_name("lb_member_member1-tls")
|
||||||
|
member1_kwargs = {
|
||||||
|
const.POOL_ID: cls.pool_id,
|
||||||
|
const.NAME: member1_name,
|
||||||
|
const.ADMIN_STATE_UP: True,
|
||||||
|
const.ADDRESS: cls.webserver1_ip,
|
||||||
|
const.PROTOCOL_PORT: 80,
|
||||||
|
}
|
||||||
|
if cls.lb_member_1_subnet:
|
||||||
|
member1_kwargs[const.SUBNET_ID] = cls.lb_member_1_subnet[const.ID]
|
||||||
|
|
||||||
|
member1 = cls.mem_member_client.create_member(
|
||||||
|
**member1_kwargs)
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
cls.mem_member_client.cleanup_member,
|
||||||
|
member1[const.ID], pool_id=cls.pool_id,
|
||||||
|
lb_client=cls.mem_lb_client, lb_id=cls.lb_id)
|
||||||
|
waiters.wait_for_status(
|
||||||
|
cls.mem_lb_client.show_loadbalancer, cls.lb_id,
|
||||||
|
const.PROVISIONING_STATUS, const.ACTIVE,
|
||||||
|
CONF.load_balancer.check_interval,
|
||||||
|
CONF.load_balancer.check_timeout)
|
||||||
|
|
||||||
|
# Set up Member 2 for Webserver 2
|
||||||
|
member2_name = data_utils.rand_name("lb_member_member2-tls")
|
||||||
|
member2_kwargs = {
|
||||||
|
const.POOL_ID: cls.pool_id,
|
||||||
|
const.NAME: member2_name,
|
||||||
|
const.ADMIN_STATE_UP: True,
|
||||||
|
const.ADDRESS: cls.webserver2_ip,
|
||||||
|
const.PROTOCOL_PORT: 80,
|
||||||
|
}
|
||||||
|
if cls.lb_member_2_subnet:
|
||||||
|
member2_kwargs[const.SUBNET_ID] = cls.lb_member_2_subnet[const.ID]
|
||||||
|
|
||||||
|
member2 = cls.mem_member_client.create_member(
|
||||||
|
**member2_kwargs)
|
||||||
|
cls.addClassResourceCleanup(
|
||||||
|
cls.mem_member_client.cleanup_member,
|
||||||
|
member2[const.ID], pool_id=cls.pool_id,
|
||||||
|
lb_client=cls.mem_lb_client, lb_id=cls.lb_id)
|
||||||
|
waiters.wait_for_status(
|
||||||
|
cls.mem_lb_client.show_loadbalancer, cls.lb_id,
|
||||||
|
const.PROVISIONING_STATUS, const.ACTIVE,
|
||||||
|
CONF.load_balancer.check_interval,
|
||||||
|
CONF.load_balancer.check_timeout)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('887ece26-0f7b-4933-89ab-5bb00b106ee0')
|
||||||
|
def test_basic_tls_traffic(self):
|
||||||
|
|
||||||
|
listener_name = data_utils.rand_name("lb_member_listener1-tls")
|
||||||
|
listener_kwargs = {
|
||||||
|
const.NAME: listener_name,
|
||||||
|
const.PROTOCOL: lib_consts.PROTOCOL_TERMINATED_HTTPS,
|
||||||
|
const.PROTOCOL_PORT: '443',
|
||||||
|
const.LOADBALANCER_ID: self.lb_id,
|
||||||
|
const.DEFAULT_POOL_ID: self.pool_id,
|
||||||
|
const.DEFAULT_TLS_CONTAINER_REF: self.secret_ref,
|
||||||
|
}
|
||||||
|
listener = self.mem_listener_client.create_listener(**listener_kwargs)
|
||||||
|
self.listener_id = listener[const.ID]
|
||||||
|
self.addCleanup(
|
||||||
|
self.mem_listener_client.cleanup_listener,
|
||||||
|
self.listener_id,
|
||||||
|
lb_client=self.mem_lb_client, lb_id=self.lb_id)
|
||||||
|
|
||||||
|
waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
|
||||||
|
self.lb_id, const.PROVISIONING_STATUS,
|
||||||
|
const.ACTIVE,
|
||||||
|
CONF.load_balancer.build_interval,
|
||||||
|
CONF.load_balancer.build_timeout)
|
||||||
|
|
||||||
|
# Test HTTPS listener load balancing.
|
||||||
|
# Note: certificate validation tests will follow this test
|
||||||
|
self.check_members_balanced(self.lb_vip_address, protocol='https',
|
||||||
|
verify=False)
|
||||||
|
|
||||||
|
def _verify_cb(connection, x509, errno, errdepth, retcode):
|
||||||
|
"""Callback for certificate validation."""
|
||||||
|
# don't validate names of root certificates
|
||||||
|
if errdepth != 0:
|
||||||
|
return True
|
||||||
|
if errno == 0:
|
||||||
|
# Make sure the certificate is the one we generated
|
||||||
|
self.assertEqual('{}.example.com'.format(self.server_uuid),
|
||||||
|
x509.get_subject().commonName)
|
||||||
|
else:
|
||||||
|
LOG.error('Certificate with CN: {0} failed validation with '
|
||||||
|
'OpenSSL verify errno {1}'.format(
|
||||||
|
x509.get_subject().commonName, errno))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
context = SSL.Context(SSL.SSLv23_METHOD)
|
||||||
|
context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||||
|
_verify_cb)
|
||||||
|
ca_store = context.get_cert_store()
|
||||||
|
ca_store.add_cert(X509.from_cryptography(self.ca_cert))
|
||||||
|
sock = socket.socket()
|
||||||
|
sock = SSL.Connection(context, sock)
|
||||||
|
sock.connect((self.lb_vip_address, 443))
|
||||||
|
# Validate the certificate is signed by the ca_cert we created
|
||||||
|
sock.do_handshake()
|
|
@ -766,13 +766,15 @@ class LoadBalancerBaseTestWithCompute(LoadBalancerBaseTest):
|
||||||
URL = 'http://{0}:81'.format(ip_address)
|
URL = 'http://{0}:81'.format(ip_address)
|
||||||
validators.validate_URL_response(URL, expected_body=str(start_id + 1))
|
validators.validate_URL_response(URL, expected_body=str(start_id + 1))
|
||||||
|
|
||||||
def _wait_for_lb_functional(self, vip_address):
|
def _wait_for_lb_functional(self, vip_address,
|
||||||
|
protocol='http', verify=True):
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
while time.time() - start < CONF.load_balancer.build_timeout:
|
while time.time() - start < CONF.load_balancer.build_timeout:
|
||||||
try:
|
try:
|
||||||
session.get("http://{0}".format(vip_address), timeout=2)
|
session.get("{0}://{1}".format(protocol, vip_address),
|
||||||
|
timeout=2, verify=verify)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
return
|
return
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -782,20 +784,21 @@ class LoadBalancerBaseTestWithCompute(LoadBalancerBaseTest):
|
||||||
'period. Failing test.')
|
'period. Failing test.')
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
def check_members_balanced(self, vip_address, traffic_member_count=2):
|
def check_members_balanced(self, vip_address, traffic_member_count=2,
|
||||||
|
protocol='http', verify=True):
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
response_counts = {}
|
response_counts = {}
|
||||||
|
|
||||||
if ipaddress.ip_address(vip_address).version == 6:
|
if ipaddress.ip_address(vip_address).version == 6:
|
||||||
vip_address = '[{}]'.format(vip_address)
|
vip_address = '[{}]'.format(vip_address)
|
||||||
|
|
||||||
self._wait_for_lb_functional(vip_address)
|
self._wait_for_lb_functional(vip_address, protocol, verify)
|
||||||
|
|
||||||
# Send a number requests to lb vip
|
# Send a number requests to lb vip
|
||||||
for i in range(20):
|
for i in range(20):
|
||||||
try:
|
try:
|
||||||
r = session.get('http://{0}'.format(vip_address),
|
r = session.get('{0}://{1}'.format(protocol, vip_address),
|
||||||
timeout=2)
|
timeout=2, verify=verify)
|
||||||
|
|
||||||
if r.content in response_counts:
|
if r.content in response_counts:
|
||||||
response_counts[r.content] += 1
|
response_counts[r.content] += 1
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
|
cryptography>=2.1 # BSD/Apache-2.0
|
||||||
python-dateutil>=2.5.3 # BSD
|
python-dateutil>=2.5.3 # BSD
|
||||||
ipaddress>=1.0.17;python_version<'3.3' # PSF
|
ipaddress>=1.0.17;python_version<'3.3' # PSF
|
||||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||||
|
octavia-lib>=1.0.0 # Apache-2.0
|
||||||
oslo.config>=5.2.0 # Apache-2.0
|
oslo.config>=5.2.0 # Apache-2.0
|
||||||
oslo.log>=3.36.0 # Apache-2.0
|
oslo.log>=3.36.0 # Apache-2.0
|
||||||
oslo.utils>=3.33.0 # Apache-2.0
|
oslo.utils>=3.33.0 # Apache-2.0
|
||||||
oslotest>=3.2.0 # Apache-2.0
|
oslotest>=3.2.0 # Apache-2.0
|
||||||
|
python-barbicanclient>=4.5.2 # Apache-2.0
|
||||||
|
pyOpenSSL>=17.1.0 # Apache-2.0
|
||||||
requests>=2.14.2 # Apache-2.0
|
requests>=2.14.2 # Apache-2.0
|
||||||
six>=1.10.0 # MIT
|
six>=1.10.0 # MIT
|
||||||
tempest>=17.1.0 # Apache-2.0
|
tempest>=17.1.0 # Apache-2.0
|
||||||
|
|
|
@ -87,17 +87,13 @@
|
||||||
parent: octavia-dsvm-base
|
parent: octavia-dsvm-base
|
||||||
timeout: 9000
|
timeout: 9000
|
||||||
required-projects:
|
required-projects:
|
||||||
- openstack/barbican
|
|
||||||
- openstack/diskimage-builder
|
- openstack/diskimage-builder
|
||||||
- openstack/python-barbicanclient
|
|
||||||
vars:
|
vars:
|
||||||
devstack_localrc:
|
devstack_localrc:
|
||||||
DIB_LOCAL_ELEMENTS: openstack-ci-mirrors
|
DIB_LOCAL_ELEMENTS: openstack-ci-mirrors
|
||||||
devstack_services:
|
devstack_services:
|
||||||
barbican: true
|
|
||||||
neutron-qos: true
|
neutron-qos: true
|
||||||
devstack_plugins:
|
devstack_plugins:
|
||||||
barbican: https://git.openstack.org/openstack/barbican.git
|
|
||||||
neutron: https://git.openstack.org/openstack/neutron.git
|
neutron: https://git.openstack.org/openstack/neutron.git
|
||||||
zuul_copy_output:
|
zuul_copy_output:
|
||||||
'/var/log/dib-build' : logs
|
'/var/log/dib-build' : logs
|
||||||
|
@ -108,9 +104,7 @@
|
||||||
nodeset: octavia-two-node
|
nodeset: octavia-two-node
|
||||||
timeout: 9000
|
timeout: 9000
|
||||||
required-projects:
|
required-projects:
|
||||||
- openstack/barbican
|
|
||||||
- openstack/diskimage-builder
|
- openstack/diskimage-builder
|
||||||
- openstack/python-barbicanclient
|
|
||||||
host-vars:
|
host-vars:
|
||||||
controller:
|
controller:
|
||||||
devstack_localrc:
|
devstack_localrc:
|
||||||
|
@ -143,7 +137,6 @@
|
||||||
OCTAVIA_NODES: "main:{{ hostvars['controller']['nodepool']['private_ipv4'] }},second:{{ hostvars['controller2']['nodepool']['private_ipv4'] }}"
|
OCTAVIA_NODES: "main:{{ hostvars['controller']['nodepool']['private_ipv4'] }},second:{{ hostvars['controller2']['nodepool']['private_ipv4'] }}"
|
||||||
OCTAVIA_USE_PREGENERATED_CERTS: true
|
OCTAVIA_USE_PREGENERATED_CERTS: true
|
||||||
devstack_plugins:
|
devstack_plugins:
|
||||||
barbican: https://git.openstack.org/openstack/barbican.git
|
|
||||||
neutron: https://git.openstack.org/openstack/neutron.git
|
neutron: https://git.openstack.org/openstack/neutron.git
|
||||||
octavia: https://git.openstack.org/openstack/octavia.git
|
octavia: https://git.openstack.org/openstack/octavia.git
|
||||||
controller2:
|
controller2:
|
||||||
|
@ -191,7 +184,7 @@
|
||||||
api_v1_enabled: False
|
api_v1_enabled: False
|
||||||
devstack_services:
|
devstack_services:
|
||||||
base: false
|
base: false
|
||||||
barbican: true
|
barbican: false
|
||||||
dstat: true
|
dstat: true
|
||||||
g-api: true
|
g-api: true
|
||||||
g-reg: true
|
g-reg: true
|
||||||
|
@ -362,6 +355,20 @@
|
||||||
OCTAVIA_AMP_BASE_OS: ubuntu
|
OCTAVIA_AMP_BASE_OS: ubuntu
|
||||||
OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID: bionic
|
OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID: bionic
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: octavia-v2-dsvm-tls-barbican
|
||||||
|
parent: octavia-v2-dsvm-scenario
|
||||||
|
required-projects:
|
||||||
|
- openstack/barbican
|
||||||
|
- openstack/diskimage-builder
|
||||||
|
- openstack/python-barbicanclient
|
||||||
|
vars:
|
||||||
|
tempest_test_regex: ^octavia_tempest_plugin.tests.barbican_scenario.v2
|
||||||
|
devstack_services:
|
||||||
|
barbican: true
|
||||||
|
devstack_plugins:
|
||||||
|
barbican: https://git.openstack.org/openstack/barbican.git
|
||||||
|
|
||||||
# Temporary transitional aliases for gates used in other repos
|
# Temporary transitional aliases for gates used in other repos
|
||||||
# Remove once octavia has transitioned job names
|
# Remove once octavia has transitioned job names
|
||||||
- job:
|
- job:
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
voting: false
|
voting: false
|
||||||
- octavia-v2-dsvm-py2-scenario-two-node:
|
- octavia-v2-dsvm-py2-scenario-two-node:
|
||||||
voting: false
|
voting: false
|
||||||
|
- octavia-v2-dsvm-tls-barbican:
|
||||||
|
voting: false
|
||||||
gate:
|
gate:
|
||||||
queue: octavia
|
queue: octavia
|
||||||
jobs:
|
jobs:
|
||||||
|
|
Loading…
Reference in New Issue