Fix listeners with SNI certificates
The single process patch changed the way listeners and load balancers are deployed inside the amphora. This caused listeners with SNI enabled to load all of the certificates for all of the TLS enabled listeners on a load balancer. This patch corrects that by configuring each listener with a specific list of certificates. Change-Id: I2f3c7ab4137dbd84d77a6a6b675975af406249d0 Story: 2006758 Task: 37252
This commit is contained in:
parent
6a15e14d25
commit
3c05ce1297
@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
FRONTEND_BACKEND_PATTERN = re.compile(r'\n(frontend|backend)\s+(\S+)\n')
|
||||
LISTENER_MODE_PATTERN = re.compile(r'^\s+mode\s+(.*)$', re.MULTILINE)
|
||||
TLS_CERT_PATTERN = re.compile(r'^\s+bind\s+\S+\s+ssl crt\s+(.*)$',
|
||||
TLS_CERT_PATTERN = re.compile(r'^\s+bind\s+\S+\s+ssl crt-list\s+(.*)$',
|
||||
re.MULTILINE)
|
||||
STATS_SOCKET_PATTERN = re.compile(r'stats socket\s+(\S+)')
|
||||
|
||||
|
@ -145,7 +145,6 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
'process mode.', amphora.id, loadbalancer.id)
|
||||
|
||||
has_tcp = False
|
||||
certs = {}
|
||||
for listener in loadbalancer.listeners:
|
||||
LOG.debug("%s updating listener %s on amphora %s",
|
||||
self.__class__.__name__, listener.id, amphora.id)
|
||||
@ -163,10 +162,8 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
else:
|
||||
obj_id = loadbalancer.id
|
||||
|
||||
certs.update({
|
||||
listener.tls_certificate_id:
|
||||
self._process_tls_certificates(
|
||||
listener, amphora, obj_id)['tls_cert']})
|
||||
self._process_tls_certificates(listener, amphora, obj_id)
|
||||
|
||||
client_ca_filename = self._process_secret(
|
||||
listener, listener.client_ca_tls_certificate_id,
|
||||
amphora, obj_id)
|
||||
@ -179,7 +176,6 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
if split_config:
|
||||
config = self.jinja_split.build_config(
|
||||
host_amphora=amphora, listener=listener,
|
||||
tls_cert=certs[listener.tls_certificate_id],
|
||||
haproxy_versions=haproxy_versions,
|
||||
client_ca_filename=client_ca_filename,
|
||||
client_crl=crl_filename,
|
||||
@ -194,7 +190,6 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
# Generate HaProxy configuration from listener object
|
||||
config = self.jinja_combo.build_config(
|
||||
host_amphora=amphora, listeners=loadbalancer.listeners,
|
||||
tls_certs=certs,
|
||||
haproxy_versions=haproxy_versions,
|
||||
client_ca_filename=client_ca_filename,
|
||||
client_crl=crl_filename,
|
||||
@ -414,11 +409,13 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
tls_cert = None
|
||||
sni_certs = []
|
||||
certs = []
|
||||
cert_filename_list = []
|
||||
|
||||
data = cert_parser.load_certificates_data(
|
||||
self.cert_manager, listener)
|
||||
if data['tls_cert'] is not None:
|
||||
tls_cert = data['tls_cert']
|
||||
# Note, the first cert is the TLS default cert
|
||||
certs.append(tls_cert)
|
||||
if data['sni_certs']:
|
||||
sni_certs = data['sni_certs']
|
||||
@ -429,7 +426,17 @@ class HaproxyAmphoraLoadBalancerDriver(
|
||||
pem = cert_parser.build_pem(cert)
|
||||
md5 = hashlib.md5(pem).hexdigest() # nosec
|
||||
name = '{id}.pem'.format(id=cert.id)
|
||||
cert_filename_list.append(
|
||||
os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir, obj_id, name))
|
||||
self._upload_cert(amphora, obj_id, pem, md5, name)
|
||||
|
||||
if certs:
|
||||
# Build and upload the crt-list file for haproxy
|
||||
crt_list = "\n".join(cert_filename_list).encode('utf-8')
|
||||
md5 = hashlib.md5(crt_list).hexdigest() # nosec
|
||||
name = '{id}.pem'.format(id=listener.id)
|
||||
self._upload_cert(amphora, obj_id, crt_list, md5, name)
|
||||
return {'tls_cert': tls_cert, 'sni_certs': sni_certs}
|
||||
|
||||
def _process_secret(self, listener, secret_ref, amphora=None, obj_id=None):
|
||||
|
@ -81,15 +81,13 @@ class JinjaTemplater(object):
|
||||
self.log_server = log_server
|
||||
self.connection_logging = connection_logging
|
||||
|
||||
def build_config(self, host_amphora, listeners, tls_certs,
|
||||
haproxy_versions, socket_path=None,
|
||||
client_ca_filename=None, client_crl=None,
|
||||
pool_tls_certs=None):
|
||||
def build_config(self, host_amphora, listeners, haproxy_versions,
|
||||
socket_path=None, client_ca_filename=None,
|
||||
client_crl=None, pool_tls_certs=None):
|
||||
"""Convert a logical configuration to the HAProxy version
|
||||
|
||||
:param host_amphora: The Amphora this configuration is hosted on
|
||||
:param listener: The listener configuration
|
||||
:param tls_certs: Dict of the TLS certificates for the listeners
|
||||
:param socket_path: The socket path for Haproxy process
|
||||
:return: Rendered configuration
|
||||
"""
|
||||
@ -104,8 +102,7 @@ class JinjaTemplater(object):
|
||||
feature_compatibility[constants.HTTP_REUSE] = True
|
||||
|
||||
return self.render_loadbalancer_obj(
|
||||
host_amphora, listeners, tls_certs=tls_certs,
|
||||
socket_path=socket_path,
|
||||
host_amphora, listeners, socket_path=socket_path,
|
||||
feature_compatibility=feature_compatibility,
|
||||
client_ca_filename=client_ca_filename, client_crl=client_crl,
|
||||
pool_tls_certs=pool_tls_certs)
|
||||
@ -144,15 +141,13 @@ class JinjaTemplater(object):
|
||||
return log_format
|
||||
|
||||
def render_loadbalancer_obj(self, host_amphora, listeners,
|
||||
tls_certs=None, socket_path=None,
|
||||
feature_compatibility=None,
|
||||
socket_path=None, feature_compatibility=None,
|
||||
client_ca_filename=None, client_crl=None,
|
||||
pool_tls_certs=None):
|
||||
"""Renders a templated configuration from a load balancer object
|
||||
|
||||
:param host_amphora: The Amphora this configuration is hosted on
|
||||
:param listener: The listener configuration
|
||||
:param tls_certs: Dict of the TLS certificates for the listener
|
||||
:param client_ca_filename: The CA certificate for client authorization
|
||||
:param socket_path: The socket path for Haproxy process
|
||||
:return: Rendered configuration
|
||||
@ -162,7 +157,6 @@ class JinjaTemplater(object):
|
||||
host_amphora,
|
||||
listeners[0].load_balancer,
|
||||
listeners,
|
||||
tls_certs,
|
||||
feature_compatibility,
|
||||
client_ca_filename=client_ca_filename,
|
||||
client_crl=client_crl,
|
||||
@ -182,9 +176,8 @@ class JinjaTemplater(object):
|
||||
constants=constants)
|
||||
|
||||
def _transform_loadbalancer(self, host_amphora, loadbalancer, listeners,
|
||||
tls_certs, feature_compatibility,
|
||||
client_ca_filename=None, client_crl=None,
|
||||
pool_tls_certs=None):
|
||||
feature_compatibility, client_ca_filename=None,
|
||||
client_crl=None, pool_tls_certs=None):
|
||||
"""Transforms a load balancer into an object that will
|
||||
|
||||
be processed by the templating system
|
||||
@ -194,7 +187,7 @@ class JinjaTemplater(object):
|
||||
if listener.protocol == constants.PROTOCOL_UDP:
|
||||
continue
|
||||
listener_transforms.append(self._transform_listener(
|
||||
listener, tls_certs, feature_compatibility, loadbalancer,
|
||||
listener, feature_compatibility, loadbalancer,
|
||||
client_ca_filename=client_ca_filename, client_crl=client_crl,
|
||||
pool_tls_certs=pool_tls_certs))
|
||||
|
||||
@ -246,7 +239,7 @@ class JinjaTemplater(object):
|
||||
'vrrp_priority': amphora.vrrp_priority
|
||||
}
|
||||
|
||||
def _transform_listener(self, listener, tls_certs, feature_compatibility,
|
||||
def _transform_listener(self, listener, feature_compatibility,
|
||||
loadbalancer, client_ca_filename=None,
|
||||
client_crl=None, pool_tls_certs=None):
|
||||
"""Transforms a listener into an object that will
|
||||
@ -279,14 +272,12 @@ class JinjaTemplater(object):
|
||||
ret_value['connection_limit'] = listener.connection_limit
|
||||
else:
|
||||
ret_value['connection_limit'] = constants.HAPROXY_MAX_MAXCONN
|
||||
if listener.tls_certificate_id and tls_certs is not None:
|
||||
ret_value['default_tls_path'] = '%s.pem' % (
|
||||
os.path.join(self.base_crt_dir,
|
||||
loadbalancer.id,
|
||||
tls_certs[listener.tls_certificate_id].id))
|
||||
if listener.sni_containers:
|
||||
ret_value['crt_dir'] = os.path.join(
|
||||
self.base_crt_dir, loadbalancer.id)
|
||||
|
||||
if listener.tls_certificate_id:
|
||||
ret_value['crt_list_filename'] = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
loadbalancer.id, '{}.pem'.format(listener.id))
|
||||
|
||||
if listener.client_ca_tls_certificate_id:
|
||||
ret_value['client_ca_tls_path'] = '%s' % (
|
||||
os.path.join(self.base_crt_dir, loadbalancer.id,
|
||||
|
@ -27,17 +27,12 @@ peers {{ "%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
|
||||
|
||||
|
||||
{% macro bind_macro(constants, listener, lb_vip_address) %}
|
||||
{% if listener.default_tls_path %}
|
||||
{% set def_crt_opt = ("ssl crt %s"|format(
|
||||
listener.default_tls_path)|trim()) %}
|
||||
{% if listener.crt_list_filename is defined %}
|
||||
{% set def_crt_opt = ("ssl crt-list %s"|format(
|
||||
listener.crt_list_filename)|trim()) %}
|
||||
{% else %}
|
||||
{% set def_crt_opt = "" %}
|
||||
{% endif %}
|
||||
{% if listener.crt_dir %}
|
||||
{% set crt_dir_opt = "crt %s"|format(listener.crt_dir)|trim() %}
|
||||
{% else %}
|
||||
{% set crt_dir_opt = "" %}
|
||||
{% endif %}
|
||||
{% if listener.client_ca_tls_path and listener.client_auth %}
|
||||
{% set client_ca_opt = "ca-file %s verify %s"|format(listener.client_ca_tls_path, listener.client_auth)|trim() %}
|
||||
{% else %}
|
||||
@ -49,7 +44,7 @@ peers {{ "%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
|
||||
{% set ca_crl_opt = "" %}
|
||||
{% endif %}
|
||||
bind {{ lb_vip_address }}:{{ listener.protocol_port }} {{
|
||||
"%s %s %s %s"|format(def_crt_opt, crt_dir_opt, client_ca_opt, ca_crl_opt)|trim() }}
|
||||
"%s %s %s"|format(def_crt_opt, client_ca_opt, ca_crl_opt)|trim() }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
@ -81,15 +81,13 @@ class JinjaTemplater(object):
|
||||
self.log_server = log_server
|
||||
self.connection_logging = connection_logging
|
||||
|
||||
def build_config(self, host_amphora, listener, tls_cert,
|
||||
haproxy_versions, socket_path=None,
|
||||
client_ca_filename=None, client_crl=None,
|
||||
pool_tls_certs=None):
|
||||
def build_config(self, host_amphora, listener, haproxy_versions,
|
||||
socket_path=None, client_ca_filename=None,
|
||||
client_crl=None, pool_tls_certs=None):
|
||||
"""Convert a logical configuration to the HAProxy version
|
||||
|
||||
:param host_amphora: The Amphora this configuration is hosted on
|
||||
:param listener: The listener configuration
|
||||
:param tls_cert: The TLS certificates for the listener
|
||||
:param socket_path: The socket path for Haproxy process
|
||||
:return: Rendered configuration
|
||||
"""
|
||||
@ -104,7 +102,7 @@ class JinjaTemplater(object):
|
||||
feature_compatibility[constants.HTTP_REUSE] = True
|
||||
|
||||
return self.render_loadbalancer_obj(
|
||||
host_amphora, listener, tls_cert=tls_cert, socket_path=socket_path,
|
||||
host_amphora, listener, socket_path=socket_path,
|
||||
feature_compatibility=feature_compatibility,
|
||||
client_ca_filename=client_ca_filename, client_crl=client_crl,
|
||||
pool_tls_certs=pool_tls_certs)
|
||||
@ -142,8 +140,7 @@ class JinjaTemplater(object):
|
||||
log_format = log_format.replace(' ', '\\ ')
|
||||
return log_format
|
||||
|
||||
def render_loadbalancer_obj(self, host_amphora, listener,
|
||||
tls_cert=None, socket_path=None,
|
||||
def render_loadbalancer_obj(self, host_amphora, listener, socket_path=None,
|
||||
feature_compatibility=None,
|
||||
client_ca_filename=None, client_crl=None,
|
||||
pool_tls_certs=None):
|
||||
@ -151,21 +148,15 @@ class JinjaTemplater(object):
|
||||
|
||||
:param host_amphora: The Amphora this configuration is hosted on
|
||||
:param listener: The listener configuration
|
||||
:param tls_cert: The TLS certificates for the listener
|
||||
:param client_ca_filename: The CA certificate for client authorization
|
||||
:param socket_path: The socket path for Haproxy process
|
||||
:return: Rendered configuration
|
||||
"""
|
||||
feature_compatibility = feature_compatibility or {}
|
||||
loadbalancer = self._transform_loadbalancer(
|
||||
host_amphora,
|
||||
listener.load_balancer,
|
||||
listener,
|
||||
tls_cert,
|
||||
feature_compatibility,
|
||||
client_ca_filename=client_ca_filename,
|
||||
client_crl=client_crl,
|
||||
pool_tls_certs=pool_tls_certs)
|
||||
host_amphora, listener.load_balancer, listener,
|
||||
feature_compatibility, client_ca_filename=client_ca_filename,
|
||||
client_crl=client_crl, pool_tls_certs=pool_tls_certs)
|
||||
if not socket_path:
|
||||
socket_path = '%s/%s.sock' % (self.base_amp_path, listener.id)
|
||||
return self._get_template().render(
|
||||
@ -180,15 +171,14 @@ class JinjaTemplater(object):
|
||||
constants=constants)
|
||||
|
||||
def _transform_loadbalancer(self, host_amphora, loadbalancer, listener,
|
||||
tls_cert, feature_compatibility,
|
||||
client_ca_filename=None, client_crl=None,
|
||||
pool_tls_certs=None):
|
||||
feature_compatibility, client_ca_filename=None,
|
||||
client_crl=None, pool_tls_certs=None):
|
||||
"""Transforms a load balancer into an object that will
|
||||
|
||||
be processed by the templating system
|
||||
"""
|
||||
t_listener = self._transform_listener(
|
||||
listener, tls_cert, feature_compatibility, loadbalancer,
|
||||
listener, feature_compatibility, loadbalancer,
|
||||
client_ca_filename=client_ca_filename, client_crl=client_crl,
|
||||
pool_tls_certs=pool_tls_certs)
|
||||
ret_value = {
|
||||
@ -229,7 +219,7 @@ class JinjaTemplater(object):
|
||||
'vrrp_priority': amphora.vrrp_priority
|
||||
}
|
||||
|
||||
def _transform_listener(self, listener, tls_cert, feature_compatibility,
|
||||
def _transform_listener(self, listener, feature_compatibility,
|
||||
loadbalancer, client_ca_filename=None,
|
||||
client_crl=None, pool_tls_certs=None):
|
||||
"""Transforms a listener into an object that will
|
||||
@ -265,13 +255,12 @@ class JinjaTemplater(object):
|
||||
ret_value['connection_limit'] = listener.connection_limit
|
||||
else:
|
||||
ret_value['connection_limit'] = constants.HAPROXY_MAX_MAXCONN
|
||||
|
||||
if listener.tls_certificate_id:
|
||||
ret_value['default_tls_path'] = '%s.pem' % (
|
||||
os.path.join(self.base_crt_dir,
|
||||
listener.id,
|
||||
tls_cert.id))
|
||||
if listener.sni_containers:
|
||||
ret_value['crt_dir'] = os.path.join(self.base_crt_dir, listener.id)
|
||||
ret_value['crt_list_filename'] = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
listener.id, '{}.pem'.format(listener.id))
|
||||
|
||||
if listener.client_ca_tls_certificate_id:
|
||||
ret_value['client_ca_tls_path'] = '%s' % (
|
||||
os.path.join(self.base_crt_dir, listener.id,
|
||||
|
@ -27,17 +27,12 @@ peers {{ "%s_peers"|format(listener.id.replace("-", ""))|trim() }}
|
||||
|
||||
|
||||
{% macro bind_macro(constants, listener, lb_vip_address) %}
|
||||
{% if listener.default_tls_path %}
|
||||
{% set def_crt_opt = ("ssl crt %s"|format(
|
||||
listener.default_tls_path)|trim()) %}
|
||||
{% if listener.crt_list_filename is defined %}
|
||||
{% set def_crt_opt = ("ssl crt-list %s"|format(
|
||||
listener.crt_list_filename)|trim()) %}
|
||||
{% else %}
|
||||
{% set def_crt_opt = "" %}
|
||||
{% endif %}
|
||||
{% if listener.crt_dir %}
|
||||
{% set crt_dir_opt = "crt %s"|format(listener.crt_dir)|trim() %}
|
||||
{% else %}
|
||||
{% set crt_dir_opt = "" %}
|
||||
{% endif %}
|
||||
{% if listener.client_ca_tls_path and listener.client_auth %}
|
||||
{% set client_ca_opt = "ca-file %s verify %s"|format(listener.client_ca_tls_path, listener.client_auth)|trim() %}
|
||||
{% else %}
|
||||
@ -49,7 +44,7 @@ peers {{ "%s_peers"|format(listener.id.replace("-", ""))|trim() }}
|
||||
{% set ca_crl_opt = "" %}
|
||||
{% endif %}
|
||||
bind {{ lb_vip_address }}:{{ listener.protocol_port }} {{
|
||||
"%s %s %s %s"|format(def_crt_opt, crt_dir_opt, client_ca_opt, ca_crl_opt)|trim() }}
|
||||
"%s %s %s"|format(def_crt_opt, client_ca_opt, ca_crl_opt)|trim() }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
@ -203,44 +203,33 @@ class TestUtil(base.TestCase):
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_parse_haproxy_config(self):
|
||||
# template_tls
|
||||
tls_tupe = {'cont_id_1':
|
||||
sample_configs_combined.sample_tls_container_tuple(
|
||||
id='tls_container_id',
|
||||
certificate='imaCert1', private_key='imaPrivateKey1',
|
||||
primary_cn='FakeCN')}
|
||||
self.CONF.config(group="haproxy_amphora",
|
||||
base_cert_dir='/fake_cert_dir')
|
||||
FAKE_CRT_LIST_FILENAME = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
'sample_loadbalancer_id_1/sample_listener_id_1.pem')
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs_combined.sample_amphora_tuple(),
|
||||
[sample_configs_combined.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True, sni=True)],
|
||||
tls_tupe)
|
||||
proto='TERMINATED_HTTPS', tls=True, sni=True)])
|
||||
|
||||
path = util.config_path(LISTENER_ID1)
|
||||
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
|
||||
|
||||
res = util.parse_haproxy_file(LISTENER_ID1)
|
||||
listener_dict = res[1]['sample_listener_id_1']
|
||||
# NOTE: parse_haproxy_file makes mode TERMINATED_HTTPS even though
|
||||
# the haproxy.cfg needs mode HTTP
|
||||
self.assertEqual('TERMINATED_HTTPS', listener_dict['mode'])
|
||||
self.assertEqual('/var/lib/octavia/sample_loadbalancer_id_1.sock',
|
||||
res[0])
|
||||
self.assertEqual(
|
||||
'/var/lib/octavia/certs/sample_loadbalancer_id_1/'
|
||||
'tls_container_id.pem crt /var/lib/octavia/certs/'
|
||||
'sample_loadbalancer_id_1',
|
||||
listener_dict['ssl_crt'])
|
||||
self.assertEqual(FAKE_CRT_LIST_FILENAME, listener_dict['ssl_crt'])
|
||||
|
||||
# render_template_tls_no_sni
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs_combined.sample_amphora_tuple(),
|
||||
[sample_configs_combined.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True)],
|
||||
tls_certs={'cont_id_1':
|
||||
sample_configs_combined.sample_tls_container_tuple(
|
||||
id='tls_container_id',
|
||||
certificate='ImAalsdkfjCert',
|
||||
private_key='ImAsdlfksdjPrivateKey',
|
||||
primary_cn="FakeCN")})
|
||||
|
||||
proto='TERMINATED_HTTPS', tls=True)])
|
||||
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
|
||||
|
||||
res = util.parse_haproxy_file(LISTENER_ID1)
|
||||
@ -248,9 +237,7 @@ class TestUtil(base.TestCase):
|
||||
self.assertEqual('TERMINATED_HTTPS', listener_dict['mode'])
|
||||
self.assertEqual(BASE_AMP_PATH + '/sample_loadbalancer_id_1.sock',
|
||||
res[0])
|
||||
self.assertEqual(
|
||||
BASE_CRT_PATH + '/sample_loadbalancer_id_1/tls_container_id.pem',
|
||||
listener_dict['ssl_crt'])
|
||||
self.assertEqual(FAKE_CRT_LIST_FILENAME, listener_dict['ssl_crt'])
|
||||
|
||||
# render_template_http
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
|
@ -275,7 +275,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
'sni_certs': sconts
|
||||
}
|
||||
self.driver.clients[API_VERSION].get_cert_md5sum.side_effect = [
|
||||
exc.NotFound, 'Fake_MD5', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa']
|
||||
exc.NotFound, 'Fake_MD5', 'aaaaa', 'aaaaaaaa']
|
||||
self.driver._process_tls_certificates(
|
||||
sample_listener, self.amp, sample_listener.load_balancer.id)
|
||||
gcm_calls = [
|
||||
@ -309,7 +309,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
self.driver.clients[API_VERSION].upload_cert_pem.assert_has_calls(
|
||||
ucp_calls, any_order=True)
|
||||
self.assertEqual(
|
||||
3, self.driver.clients[API_VERSION].upload_cert_pem.call_count)
|
||||
4, self.driver.clients[API_VERSION].upload_cert_pem.call_count)
|
||||
|
||||
@mock.patch('oslo_context.context.RequestContext')
|
||||
@mock.patch('octavia.amphorae.drivers.haproxy.rest_api_driver.'
|
||||
|
@ -275,7 +275,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
'sni_certs': sconts
|
||||
}
|
||||
self.driver.clients[API_VERSION].get_cert_md5sum.side_effect = [
|
||||
exc.NotFound, 'Fake_MD5', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa']
|
||||
exc.NotFound, 'Fake_MD5', 'aaaaa', 'aaaaa']
|
||||
self.driver._process_tls_certificates(
|
||||
sample_listener, self.amp, sample_listener.load_balancer.id)
|
||||
gcm_calls = [
|
||||
@ -309,7 +309,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
|
||||
self.driver.clients[API_VERSION].upload_cert_pem.assert_has_calls(
|
||||
ucp_calls, any_order=True)
|
||||
self.assertEqual(
|
||||
3, self.driver.clients[API_VERSION].upload_cert_pem.call_count)
|
||||
4, self.driver.clients[API_VERSION].upload_cert_pem.call_count)
|
||||
|
||||
@mock.patch('oslo_context.context.RequestContext')
|
||||
@mock.patch('octavia.amphorae.drivers.haproxy.rest_api_driver.'
|
||||
|
@ -16,11 +16,16 @@
|
||||
import copy
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as oslo_fixture
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common.jinja.haproxy.combined_listeners import jinja_cfg
|
||||
from octavia.tests.unit import base
|
||||
from octavia.tests.unit.common.sample_configs import sample_configs_combined
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestHaproxyCfg(base.TestCase):
|
||||
def setUp(self):
|
||||
@ -34,20 +39,24 @@ class TestHaproxyCfg(base.TestCase):
|
||||
self.assertEqual('haproxy.cfg.j2', template.name)
|
||||
|
||||
def test_render_template_tls(self):
|
||||
conf = oslo_fixture.Config(cfg.CONF)
|
||||
conf.config(group="haproxy_amphora", base_cert_dir='/fake_cert_dir')
|
||||
FAKE_CRT_LIST_FILENAME = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
'sample_loadbalancer_id_1/sample_listener_id_1.pem')
|
||||
fe = ("frontend sample_listener_id_1\n"
|
||||
" maxconn {maxconn}\n"
|
||||
" redirect scheme https if !{{ ssl_fc }}\n"
|
||||
" bind 10.0.0.2:443 "
|
||||
"ssl crt /var/lib/octavia/certs/"
|
||||
"sample_loadbalancer_id_1/tls_container_id.pem "
|
||||
"crt /var/lib/octavia/certs/sample_loadbalancer_id_1 "
|
||||
"ssl crt-list {crt_list} "
|
||||
"ca-file /var/lib/octavia/certs/sample_loadbalancer_id_1/"
|
||||
"client_ca.pem verify required crl-file /var/lib/octavia/"
|
||||
"certs/sample_loadbalancer_id_1/SHA_ID.pem\n"
|
||||
" mode http\n"
|
||||
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
||||
" timeout client 50000\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN,
|
||||
crt_list=FAKE_CRT_LIST_FILENAME)
|
||||
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
||||
" mode http\n"
|
||||
" balance roundrobin\n"
|
||||
@ -66,34 +75,32 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"weight 13 check inter 30s fall 3 rise 2 cookie "
|
||||
"sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
tls_tupe = {'cont_id_1':
|
||||
sample_configs_combined.sample_tls_container_tuple(
|
||||
id='tls_container_id',
|
||||
certificate='imaCert1', private_key='imaPrivateKey1',
|
||||
primary_cn='FakeCN')}
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs_combined.sample_amphora_tuple(),
|
||||
[sample_configs_combined.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True, sni=True,
|
||||
client_ca_cert=True, client_crl_cert=True)],
|
||||
tls_tupe, client_ca_filename='client_ca.pem',
|
||||
client_crl='SHA_ID.pem')
|
||||
client_ca_filename='client_ca.pem', client_crl='SHA_ID.pem')
|
||||
self.assertEqual(
|
||||
sample_configs_combined.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_tls_no_sni(self):
|
||||
conf = oslo_fixture.Config(cfg.CONF)
|
||||
conf.config(group="haproxy_amphora", base_cert_dir='/fake_cert_dir')
|
||||
FAKE_CRT_LIST_FILENAME = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
'sample_loadbalancer_id_1/sample_listener_id_1.pem')
|
||||
fe = ("frontend sample_listener_id_1\n"
|
||||
" maxconn {maxconn}\n"
|
||||
" redirect scheme https if !{{ ssl_fc }}\n"
|
||||
" bind 10.0.0.2:443 "
|
||||
"ssl crt /var/lib/octavia/certs/"
|
||||
"sample_loadbalancer_id_1/tls_container_id.pem\n"
|
||||
" bind 10.0.0.2:443 ssl crt-list {crt_list}\n"
|
||||
" mode http\n"
|
||||
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
||||
" timeout client 50000\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN,
|
||||
crt_list=FAKE_CRT_LIST_FILENAME)
|
||||
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
||||
" mode http\n"
|
||||
" balance roundrobin\n"
|
||||
@ -115,13 +122,7 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs_combined.sample_amphora_tuple(),
|
||||
[sample_configs_combined.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True)],
|
||||
tls_certs={'cont_id_1':
|
||||
sample_configs_combined.sample_tls_container_tuple(
|
||||
id='tls_container_id',
|
||||
certificate='ImAalsdkfjCert',
|
||||
private_key='ImAsdlfksdjPrivateKey',
|
||||
primary_cn="FakeCN")})
|
||||
proto='TERMINATED_HTTPS', tls=True)])
|
||||
self.assertEqual(
|
||||
sample_configs_combined.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
@ -922,13 +923,13 @@ class TestHaproxyCfg(base.TestCase):
|
||||
|
||||
def test_transform_listener(self):
|
||||
in_listener = sample_configs_combined.sample_listener_tuple()
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, None, {},
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, {},
|
||||
in_listener.load_balancer)
|
||||
self.assertEqual(sample_configs_combined.RET_LISTENER, ret)
|
||||
|
||||
def test_transform_listener_with_l7(self):
|
||||
in_listener = sample_configs_combined.sample_listener_tuple(l7=True)
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, None, {},
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, {},
|
||||
in_listener.load_balancer)
|
||||
self.assertEqual(sample_configs_combined.RET_LISTENER_L7, ret)
|
||||
|
||||
@ -936,7 +937,7 @@ class TestHaproxyCfg(base.TestCase):
|
||||
in_amphora = sample_configs_combined.sample_amphora_tuple()
|
||||
in_listener = sample_configs_combined.sample_listener_tuple()
|
||||
ret = self.jinja_cfg._transform_loadbalancer(
|
||||
in_amphora, in_listener.load_balancer, [in_listener], None, {})
|
||||
in_amphora, in_listener.load_balancer, [in_listener], {})
|
||||
self.assertEqual(sample_configs_combined.RET_LB, ret)
|
||||
|
||||
def test_transform_amphora(self):
|
||||
@ -948,7 +949,7 @@ class TestHaproxyCfg(base.TestCase):
|
||||
in_amphora = sample_configs_combined.sample_amphora_tuple()
|
||||
in_listener = sample_configs_combined.sample_listener_tuple(l7=True)
|
||||
ret = self.jinja_cfg._transform_loadbalancer(
|
||||
in_amphora, in_listener.load_balancer, [in_listener], None, {})
|
||||
in_amphora, in_listener.load_balancer, [in_listener], {})
|
||||
self.assertEqual(sample_configs_combined.RET_LB_L7, ret)
|
||||
|
||||
def test_transform_l7policy(self):
|
||||
@ -1066,7 +1067,6 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_amphora,
|
||||
[sample_proxy_listener],
|
||||
tls_certs=None,
|
||||
haproxy_versions=("1", "8", "1"))
|
||||
self.assertEqual(
|
||||
sample_configs_combined.sample_base_expected_config(backend=be),
|
||||
@ -1094,7 +1094,6 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_amphora,
|
||||
[sample_proxy_listener],
|
||||
tls_certs=None,
|
||||
haproxy_versions=("1", "5", "18"))
|
||||
self.assertEqual(
|
||||
sample_configs_combined.sample_base_expected_config(backend=be),
|
||||
@ -1176,7 +1175,6 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs_combined.sample_amphora_tuple(),
|
||||
[sample_listener],
|
||||
tls_certs=None,
|
||||
haproxy_versions=("1", "5", "18"))
|
||||
self.assertEqual(
|
||||
sample_configs_combined.sample_base_expected_config(
|
||||
|
@ -16,11 +16,16 @@
|
||||
import copy
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as oslo_fixture
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common.jinja.haproxy.split_listeners import jinja_cfg
|
||||
from octavia.tests.unit import base
|
||||
from octavia.tests.unit.common.sample_configs import sample_configs_split
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestHaproxyCfg(base.TestCase):
|
||||
def setUp(self):
|
||||
@ -34,20 +39,24 @@ class TestHaproxyCfg(base.TestCase):
|
||||
self.assertEqual('haproxy.cfg.j2', template.name)
|
||||
|
||||
def test_render_template_tls(self):
|
||||
conf = oslo_fixture.Config(cfg.CONF)
|
||||
conf.config(group="haproxy_amphora", base_cert_dir='/fake_cert_dir')
|
||||
FAKE_CRT_LIST_FILENAME = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
'sample_listener_id_1/sample_listener_id_1.pem')
|
||||
fe = ("frontend sample_listener_id_1\n"
|
||||
" maxconn {maxconn}\n"
|
||||
" redirect scheme https if !{{ ssl_fc }}\n"
|
||||
" bind 10.0.0.2:443 "
|
||||
"ssl crt /var/lib/octavia/certs/"
|
||||
"sample_listener_id_1/tls_container_id.pem "
|
||||
"crt /var/lib/octavia/certs/sample_listener_id_1 "
|
||||
"ssl crt-list {crt_list} "
|
||||
"ca-file /var/lib/octavia/certs/sample_listener_id_1/"
|
||||
"client_ca.pem verify required crl-file /var/lib/octavia/"
|
||||
"certs/sample_listener_id_1/SHA_ID.pem\n"
|
||||
" mode http\n"
|
||||
" default_backend sample_pool_id_1\n"
|
||||
" timeout client 50000\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN,
|
||||
crt_list=FAKE_CRT_LIST_FILENAME)
|
||||
be = ("backend sample_pool_id_1\n"
|
||||
" mode http\n"
|
||||
" balance roundrobin\n"
|
||||
@ -66,16 +75,12 @@ class TestHaproxyCfg(base.TestCase):
|
||||
"weight 13 check inter 30s fall 3 rise 2 cookie "
|
||||
"sample_member_id_2\n\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
tls_tupe = sample_configs_split.sample_tls_container_tuple(
|
||||
id='tls_container_id',
|
||||
certificate='imaCert1', private_key='imaPrivateKey1',
|
||||
primary_cn='FakeCN')
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True, sni=True,
|
||||
client_ca_cert=True, client_crl_cert=True),
|
||||
tls_tupe, client_ca_filename='client_ca.pem',
|
||||
client_ca_filename='client_ca.pem',
|
||||
client_crl='SHA_ID.pem')
|
||||
self.assertEqual(
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
@ -83,16 +88,21 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj)
|
||||
|
||||
def test_render_template_tls_no_sni(self):
|
||||
conf = oslo_fixture.Config(cfg.CONF)
|
||||
conf.config(group="haproxy_amphora", base_cert_dir='/fake_cert_dir')
|
||||
FAKE_CRT_LIST_FILENAME = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir,
|
||||
'sample_listener_id_1/sample_listener_id_1.pem')
|
||||
fe = ("frontend sample_listener_id_1\n"
|
||||
" maxconn {maxconn}\n"
|
||||
" redirect scheme https if !{{ ssl_fc }}\n"
|
||||
" bind 10.0.0.2:443 "
|
||||
"ssl crt /var/lib/octavia/certs/"
|
||||
"sample_listener_id_1/tls_container_id.pem\n"
|
||||
"ssl crt-list {crt_list}\n"
|
||||
" mode http\n"
|
||||
" default_backend sample_pool_id_1\n"
|
||||
" timeout client 50000\n").format(
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||
maxconn=constants.HAPROXY_MAX_MAXCONN,
|
||||
crt_list=FAKE_CRT_LIST_FILENAME)
|
||||
be = ("backend sample_pool_id_1\n"
|
||||
" mode http\n"
|
||||
" balance roundrobin\n"
|
||||
@ -114,12 +124,7 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(
|
||||
proto='TERMINATED_HTTPS', tls=True),
|
||||
tls_cert=sample_configs_split.sample_tls_container_tuple(
|
||||
id='tls_container_id',
|
||||
certificate='ImAalsdkfjCert',
|
||||
private_key='ImAsdlfksdjPrivateKey',
|
||||
primary_cn="FakeCN"))
|
||||
proto='TERMINATED_HTTPS', tls=True))
|
||||
self.assertEqual(
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
frontend=fe, backend=be),
|
||||
@ -913,13 +918,13 @@ class TestHaproxyCfg(base.TestCase):
|
||||
|
||||
def test_transform_listener(self):
|
||||
in_listener = sample_configs_split.sample_listener_tuple()
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, None, {},
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, {},
|
||||
in_listener.load_balancer)
|
||||
self.assertEqual(sample_configs_split.RET_LISTENER, ret)
|
||||
|
||||
def test_transform_listener_with_l7(self):
|
||||
in_listener = sample_configs_split.sample_listener_tuple(l7=True)
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, None, {},
|
||||
ret = self.jinja_cfg._transform_listener(in_listener, {},
|
||||
in_listener.load_balancer)
|
||||
self.assertEqual(sample_configs_split.RET_LISTENER_L7, ret)
|
||||
|
||||
@ -927,7 +932,7 @@ class TestHaproxyCfg(base.TestCase):
|
||||
in_amphora = sample_configs_split.sample_amphora_tuple()
|
||||
in_listener = sample_configs_split.sample_listener_tuple()
|
||||
ret = self.jinja_cfg._transform_loadbalancer(
|
||||
in_amphora, in_listener.load_balancer, in_listener, None, {})
|
||||
in_amphora, in_listener.load_balancer, in_listener, {})
|
||||
self.assertEqual(sample_configs_split.RET_LB, ret)
|
||||
|
||||
def test_transform_amphora(self):
|
||||
@ -939,7 +944,7 @@ class TestHaproxyCfg(base.TestCase):
|
||||
in_amphora = sample_configs_split.sample_amphora_tuple()
|
||||
in_listener = sample_configs_split.sample_listener_tuple(l7=True)
|
||||
ret = self.jinja_cfg._transform_loadbalancer(
|
||||
in_amphora, in_listener.load_balancer, in_listener, None, {})
|
||||
in_amphora, in_listener.load_balancer, in_listener, {})
|
||||
self.assertEqual(sample_configs_split.RET_LB_L7, ret)
|
||||
|
||||
def test_transform_l7policy(self):
|
||||
@ -1052,7 +1057,6 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(be_proto='PROXY'),
|
||||
tls_cert=None,
|
||||
haproxy_versions=("1", "8", "1"))
|
||||
self.assertEqual(
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
@ -1078,7 +1082,6 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_configs_split.sample_listener_tuple(be_proto='PROXY'),
|
||||
tls_cert=None,
|
||||
haproxy_versions=("1", "5", "18"))
|
||||
self.assertEqual(
|
||||
sample_configs_split.sample_base_expected_config(backend=be),
|
||||
@ -1159,7 +1162,6 @@ class TestHaproxyCfg(base.TestCase):
|
||||
rendered_obj = j_cfg.build_config(
|
||||
sample_configs_split.sample_amphora_tuple(),
|
||||
sample_listener,
|
||||
tls_cert=None,
|
||||
haproxy_versions=("1", "5", "18"))
|
||||
self.assertEqual(
|
||||
sample_configs_split.sample_base_expected_config(
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes an issue where load balancers with more than one TLS enabled
|
||||
listener, one or more SNI enabled, may load certificates from
|
||||
other TLS enabled listeners for SNI use.
|
Loading…
Reference in New Issue
Block a user