Add minimum TLS version option in octavia.conf

Add new configuration option "minimum_tls_versions" to octavia.conf.
Listeners, pools, or the default values for either will be blocked from
using lower versions.

Change-Id: Ifa0d695c2227772d6b37987a7857fe58ca660dc8
Story: 2006733
Task: 37171
Depends-On: I480b7fb9756d98ba9dbcdfd1d4b193ce6868e291
This commit is contained in:
Dawson Coleman 2020-04-22 14:02:00 -05:00 committed by Michael Johnson
parent 0321c28588
commit 270b973bf9
7 changed files with 93 additions and 5 deletions

View File

@ -80,6 +80,10 @@
# pools. Available versions: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 # pools. Available versions: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3
# default_pool_tls_versions = TLSv1.2, TLSv1.3 # default_pool_tls_versions = TLSv1.2, TLSv1.3
# Minimum TLS version to allow for listeners, pools, or the defaults for
# either. Available versions: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3
# minimum_tls_version =
[database] [database]
# This line MUST be changed to actually run the plugin. # This line MUST be changed to actually run the plugin.
# Example: # Example:

View File

@ -288,9 +288,11 @@ class ListenersController(base.BaseController):
vip_address = vip_db.ip_address vip_address = vip_db.ip_address
self._validate_cidr_compatible_with_vip(vip_address, allowed_cidrs) self._validate_cidr_compatible_with_vip(vip_address, allowed_cidrs)
# Validate TLS version list
if listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS: if listener_protocol == constants.PROTOCOL_TERMINATED_HTTPS:
# Validate TLS version list
validate.check_tls_version_list(listener_dict['tls_versions']) validate.check_tls_version_list(listener_dict['tls_versions'])
# Validate TLS versions against minimum
validate.check_tls_version_min(listener_dict['tls_versions'])
try: try:
db_listener = self.repositories.listener.create( db_listener = self.repositories.listener.create(
@ -498,9 +500,11 @@ class ListenersController(base.BaseController):
'The following ciphers have been blacklisted by an ' 'The following ciphers have been blacklisted by an '
'administrator: ' + ', '.join(rejected_ciphers))) 'administrator: ' + ', '.join(rejected_ciphers)))
# Validate TLS version list
if listener.tls_versions is not wtypes.Unset: if listener.tls_versions is not wtypes.Unset:
# Validate TLS version list
validate.check_tls_version_list(listener.tls_versions) validate.check_tls_version_list(listener.tls_versions)
# Validate TLS versions against minimum
validate.check_tls_version_min(listener.tls_versions)
def _set_default_on_none(self, listener): def _set_default_on_none(self, listener):
"""Reset settings to their default values if None/null was passed in """Reset settings to their default values if None/null was passed in

View File

@ -131,9 +131,11 @@ class PoolsController(base.BaseController):
'The following ciphers have been blacklisted by an ' 'The following ciphers have been blacklisted by an '
'administrator: ' + ', '.join(rejected_ciphers))) 'administrator: ' + ', '.join(rejected_ciphers)))
# Validate TLS version list
if pool_dict['tls_enabled']: if pool_dict['tls_enabled']:
# Validate TLS version list
validate.check_tls_version_list(pool_dict['tls_versions']) validate.check_tls_version_list(pool_dict['tls_versions'])
# Validate TLS versions against minimum
validate.check_tls_version_min(pool_dict['tls_versions'])
try: try:
return self.repositories.create_pool_on_load_balancer( return self.repositories.create_pool_on_load_balancer(
@ -399,9 +401,11 @@ class PoolsController(base.BaseController):
"The following ciphers have been blacklisted by an " "The following ciphers have been blacklisted by an "
"administrator: " + ', '.join(rejected_ciphers))) "administrator: " + ', '.join(rejected_ciphers)))
# Validate TLS version list
if pool.tls_versions is not wtypes.Unset: if pool.tls_versions is not wtypes.Unset:
# Validate TLS version list
validate.check_tls_version_list(pool.tls_versions) validate.check_tls_version_list(pool.tls_versions)
# Validate TLS version against minimum
validate.check_tls_version_min(pool.tls_versions)
@wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text, @wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text,
body=pool_types.PoolRootPut, status_code=200) body=pool_types.PoolRootPut, status_code=200)

View File

@ -123,7 +123,11 @@ api_opts = [
cfg.ListOpt('default_pool_tls_versions', cfg.ListOpt('default_pool_tls_versions',
default=constants.TLS_VERSIONS_OWASP_SUITE_B, default=constants.TLS_VERSIONS_OWASP_SUITE_B,
help=_('List of TLS versions to use for new TLS-enabled ' help=_('List of TLS versions to use for new TLS-enabled '
'pools.')) 'pools.')),
cfg.StrOpt('minimum_tls_version',
default=None,
choices=constants.TLS_ALL_VERSIONS + [None],
help=_('Minimum allowed TLS version for listeners and pools.'))
] ]
# Options only used by the amphora agent # Options only used by the amphora agent
@ -833,6 +837,7 @@ def init(args, **kwargs):
version='%%prog %s' % version.version_info.release_string(), version='%%prog %s' % version.version_info.release_string(),
**kwargs) **kwargs)
handle_deprecation_compatibility() handle_deprecation_compatibility()
validate.check_default_tls_versions_min_conflict()
setup_remote_debugger() setup_remote_debugger()
validate.check_default_ciphers_blacklist_conflict() validate.check_default_ciphers_blacklist_conflict()

View File

@ -471,3 +471,40 @@ def check_tls_version_list(versions):
if invalid_versions: if invalid_versions:
raise exceptions.ValidationException( raise exceptions.ValidationException(
detail=_('Invalid TLS versions: ' + ', '.join(invalid_versions))) detail=_('Invalid TLS versions: ' + ', '.join(invalid_versions)))
def check_tls_version_min(versions, message=None):
"""Checks a TLS version string against the configured minimum."""
if not CONF.api_settings.minimum_tls_version:
return
if not message:
message = _("Requested TLS versions are less than the minimum: ")
min_ver_index = constants.TLS_ALL_VERSIONS.index(
CONF.api_settings.minimum_tls_version)
rejected = []
for ver in versions:
if constants.TLS_ALL_VERSIONS.index(ver) < min_ver_index:
rejected.append(ver)
if rejected:
raise exceptions.ValidationException(detail=(
message + ', '.join(rejected) + " < " +
CONF.api_settings.minimum_tls_version))
def check_default_tls_versions_min_conflict():
if not CONF.api_settings.minimum_tls_version:
return
listener_message = _("Default listener TLS versions are less than the "
"minimum: ")
pool_message = _("Default pool TLS versions are less than the minimum: ")
check_tls_version_min(CONF.api_settings.default_listener_tls_versions,
message=listener_message)
check_tls_version_min(CONF.api_settings.default_pool_tls_versions,
message=pool_message)

View File

@ -484,3 +484,31 @@ class TestValidations(base.TestCase):
exceptions.ValidationException, exceptions.ValidationException,
validate.check_tls_version_list, validate.check_tls_version_list,
[]) [])
def test_check_tls_version_min(self):
self.conf.config(group="api_settings", minimum_tls_version='TLSv1.2')
# Test valid list
validate.check_tls_version_min(['TLSv1.2', 'TLSv1.3'])
# Test invalid list
self.assertRaises(exceptions.ValidationException,
validate.check_tls_version_min,
['TLSv1', 'TLSv1.1', 'TLSv1.2'])
def test_check_default_tls_versions_min_conflict(self):
self.conf.config(group="api_settings", minimum_tls_version='TLSv1.2')
# Test conflict in listener default
self.conf.config(group="api_settings", default_listener_tls_versions=[
'SSLv3', 'TLSv1.2'])
self.assertRaises(exceptions.ValidationException,
validate.check_default_tls_versions_min_conflict)
# Test conflict in pool default
self.conf.config(group="api_settings", default_listener_tls_versions=[
'TLSv1.2'])
self.conf.config(group="api_settings", default_pool_tls_versions=[
'TLSv1', 'TLSv1.3'])
self.assertRaises(exceptions.ValidationException,
validate.check_default_tls_versions_min_conflict)

View File

@ -0,0 +1,6 @@
---
features:
- |
Added ``minimum_tls_version`` to ``octavia.conf``. Listeners, pools, and
the defaults for either will be blocked from using any lower TLS versions.
By default, there is no minumum version.