Merge "Add TLS version configuration for listeners"
This commit is contained in:
commit
16b96baff9
@ -1567,6 +1567,22 @@ tls_enabled-optional:
|
|||||||
min_version: 2.8
|
min_version: 2.8
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
|
tls_versions:
|
||||||
|
description: |
|
||||||
|
A list of TLS protocol versions.
|
||||||
|
Available versions: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3
|
||||||
|
in: body
|
||||||
|
min_version: 2.17
|
||||||
|
required: true
|
||||||
|
type: array
|
||||||
|
tls_versions-optional:
|
||||||
|
description: |
|
||||||
|
A list of TLS protocol versions.
|
||||||
|
Available versions: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3
|
||||||
|
in: body
|
||||||
|
min_version: 2.17
|
||||||
|
required: false
|
||||||
|
type: array
|
||||||
total_connections:
|
total_connections:
|
||||||
description: |
|
description: |
|
||||||
The total connections handled.
|
The total connections handled.
|
||||||
|
@ -1 +1 @@
|
|||||||
curl -X POST -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"listener": {"protocol": "TERMINATED_HTTPS", "description": "A great TLS listener", "admin_state_up": true, "connection_limit": 200, "protocol_port": "443", "loadbalancer_id": "607226db-27ef-4d41-ae89-f2a800e9c2db", "name": "great_tls_listener", "insert_headers": {"X-Forwarded-For": "true", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"], "timeout_client_data": 50000, "timeout_member_connect": 5000, "timeout_member_data": 50000, "timeout_tcp_inspect": 0, "tags": ["test_tag"], "client_ca_tls_container_ref": "http://198.51.100.10:9311/v1/containers/35649991-49f3-4625-81ce-2465fe8932e5", "client_authentication": "MANDATORY", "client_crl_container_ref": "http://198.51.100.10:9311/v1/containers/e222b065-b93b-4e2a-9a02-804b7a118c3c", "allowed_cidrs": ["192.0.2.0/24", "198.51.100.0/24"], "tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"}}' http://198.51.100.10:9876/v2/lbaas/listeners
|
curl -X POST -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"listener": {"protocol": "TERMINATED_HTTPS", "description": "A great TLS listener", "admin_state_up": true, "connection_limit": 200, "protocol_port": "443", "loadbalancer_id": "607226db-27ef-4d41-ae89-f2a800e9c2db", "name": "great_tls_listener", "insert_headers": {"X-Forwarded-For": "true", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"], "timeout_client_data": 50000, "timeout_member_connect": 5000, "timeout_member_data": 50000, "timeout_tcp_inspect": 0, "tags": ["test_tag"], "client_ca_tls_container_ref": "http://198.51.100.10:9311/v1/containers/35649991-49f3-4625-81ce-2465fe8932e5", "client_authentication": "MANDATORY", "client_crl_container_ref": "http://198.51.100.10:9311/v1/containers/e222b065-b93b-4e2a-9a02-804b7a118c3c", "allowed_cidrs": ["192.0.2.0/24", "198.51.100.0/24"], "tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256", "tls_versions": ["TLSv1.2", "TLSv1.3"]}}' http://198.51.100.10:9876/v2/lbaas/listeners
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"192.0.2.0/24",
|
"192.0.2.0/24",
|
||||||
"198.51.100.0/24"
|
"198.51.100.0/24"
|
||||||
],
|
],
|
||||||
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
|
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||||
|
"tls_versions": ["TLSv1.2", "TLSv1.3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"192.0.2.0/24",
|
"192.0.2.0/24",
|
||||||
"198.51.100.0/24"
|
"198.51.100.0/24"
|
||||||
],
|
],
|
||||||
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
|
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||||
|
"tls_versions": ["TLSv1.2", "TLSv1.3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"192.0.2.0/24",
|
"192.0.2.0/24",
|
||||||
"198.51.100.0/24"
|
"198.51.100.0/24"
|
||||||
],
|
],
|
||||||
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
|
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||||
|
"tls_versions": ["TLSv1.2", "TLSv1.3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"listener": {"description": "An updated great TLS listener", "admin_state_up": true, "connection_limit": 200, "name": "great_updated_tls_listener", "insert_headers": {"X-Forwarded-For": "false", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"], "timeout_client_data": 100000, "timeout_member_connect": 1000, "timeout_member_data": 100000, "timeout_tcp_inspect": 5, "tags": ["updated_tag"], "client_ca_tls_container_ref": null, "allowed_cidrs": ["192.0.2.0/24", "198.51.100.0/24"], "tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"}}' http://198.51.100.10:9876/v2/lbaas/listeners/023f2e34-7806-443b-bfae-16c324569a3d
|
curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"listener": {"description": "An updated great TLS listener", "admin_state_up": true, "connection_limit": 200, "name": "great_updated_tls_listener", "insert_headers": {"X-Forwarded-For": "false", "X-Forwarded-Port": "true"}, "default_tls_container_ref": "http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "sni_container_refs": ["http://198.51.100.10:9311/v1/containers/a570068c-d295-4780-91d4-3046a325db51", "http://198.51.100.10:9311/v1/containers/aaebb31e-7761-4826-8cb4-2b829caca3ee"], "timeout_client_data": 100000, "timeout_member_connect": 1000, "timeout_member_data": 100000, "timeout_tcp_inspect": 5, "tags": ["updated_tag"], "client_ca_tls_container_ref": null, "allowed_cidrs": ["192.0.2.0/24", "198.51.100.0/24"], "tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256", "tls_versions": ["TLSv1.2", "TLSv1.3"]}}' http://198.51.100.10:9876/v2/lbaas/listeners/023f2e34-7806-443b-bfae-16c324569a3d
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"192.0.2.0/24",
|
"192.0.2.0/24",
|
||||||
"198.51.100.0/24"
|
"198.51.100.0/24"
|
||||||
],
|
],
|
||||||
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
|
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||||
|
"tls_versions": ["TLSv1.2", "TLSv1.3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"192.0.2.0/24",
|
"192.0.2.0/24",
|
||||||
"198.51.100.0/24"
|
"198.51.100.0/24"
|
||||||
],
|
],
|
||||||
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
|
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||||
|
"tls_versions": ["TLSv1.2", "TLSv1.3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,8 @@
|
|||||||
"192.0.2.0/24",
|
"192.0.2.0/24",
|
||||||
"198.51.100.0/24"
|
"198.51.100.0/24"
|
||||||
],
|
],
|
||||||
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
|
"tls_ciphers": "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||||
|
"tls_versions": ["TLSv1.2", "TLSv1.3"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ Response Parameters
|
|||||||
- timeout_member_data: timeout_member_data
|
- timeout_member_data: timeout_member_data
|
||||||
- timeout_tcp_inspect: timeout_tcp_inspect
|
- timeout_tcp_inspect: timeout_tcp_inspect
|
||||||
- tls_ciphers: tls_ciphers
|
- tls_ciphers: tls_ciphers
|
||||||
|
- tls_versions: tls_versions
|
||||||
- updated_at: updated_at
|
- updated_at: updated_at
|
||||||
|
|
||||||
Response Example
|
Response Example
|
||||||
@ -165,6 +166,7 @@ Request
|
|||||||
- timeout_member_data: timeout_member_data-optional
|
- timeout_member_data: timeout_member_data-optional
|
||||||
- timeout_tcp_inspect: timeout_tcp_inspect-optional
|
- timeout_tcp_inspect: timeout_tcp_inspect-optional
|
||||||
- tls_ciphers: tls_ciphers-optional
|
- tls_ciphers: tls_ciphers-optional
|
||||||
|
- tls_versions: tls_versions-optional
|
||||||
|
|
||||||
.. _header_insertions:
|
.. _header_insertions:
|
||||||
|
|
||||||
@ -290,6 +292,7 @@ Response Parameters
|
|||||||
- timeout_member_data: timeout_member_data
|
- timeout_member_data: timeout_member_data
|
||||||
- timeout_tcp_inspect: timeout_tcp_inspect
|
- timeout_tcp_inspect: timeout_tcp_inspect
|
||||||
- tls_ciphers: tls_ciphers
|
- tls_ciphers: tls_ciphers
|
||||||
|
- tls_versions: tls_versions
|
||||||
- updated_at: updated_at
|
- updated_at: updated_at
|
||||||
|
|
||||||
Response Example
|
Response Example
|
||||||
@ -369,6 +372,7 @@ Response Parameters
|
|||||||
- timeout_member_data: timeout_member_data
|
- timeout_member_data: timeout_member_data
|
||||||
- timeout_tcp_inspect: timeout_tcp_inspect
|
- timeout_tcp_inspect: timeout_tcp_inspect
|
||||||
- tls_ciphers: tls_ciphers
|
- tls_ciphers: tls_ciphers
|
||||||
|
- tls_versions: tls_versions
|
||||||
- updated_at: updated_at
|
- updated_at: updated_at
|
||||||
|
|
||||||
Response Example
|
Response Example
|
||||||
@ -429,6 +433,7 @@ Request
|
|||||||
- timeout_member_data: timeout_member_data-optional
|
- timeout_member_data: timeout_member_data-optional
|
||||||
- timeout_tcp_inspect: timeout_tcp_inspect-optional
|
- timeout_tcp_inspect: timeout_tcp_inspect-optional
|
||||||
- tls_ciphers: tls_ciphers-optional
|
- tls_ciphers: tls_ciphers-optional
|
||||||
|
- tls_versions: tls_versions-optional
|
||||||
|
|
||||||
Request Example
|
Request Example
|
||||||
---------------
|
---------------
|
||||||
@ -475,6 +480,7 @@ Response Parameters
|
|||||||
- timeout_member_data: timeout_member_data
|
- timeout_member_data: timeout_member_data
|
||||||
- timeout_tcp_inspect: timeout_tcp_inspect
|
- timeout_tcp_inspect: timeout_tcp_inspect
|
||||||
- tls_ciphers: tls_ciphers
|
- tls_ciphers: tls_ciphers
|
||||||
|
- tls_versions: tls_versions
|
||||||
- updated_at: updated_at
|
- updated_at: updated_at
|
||||||
|
|
||||||
Response Example
|
Response Example
|
||||||
|
@ -72,6 +72,11 @@
|
|||||||
# allowed on listeners, pools, or the default values for either.
|
# allowed on listeners, pools, or the default values for either.
|
||||||
# tls_cipher_blacklist =
|
# tls_cipher_blacklist =
|
||||||
|
|
||||||
|
# List of default TLS versions to be used on new TLS-terminated
|
||||||
|
# listeners. Available versions: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3
|
||||||
|
# default_listener_tls_versions = TLSv1.2, TLSv1.3
|
||||||
|
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
# This line MUST be changed to actually run the plugin.
|
# This line MUST be changed to actually run the plugin.
|
||||||
# Example:
|
# Example:
|
||||||
|
@ -109,10 +109,13 @@ class RootController(object):
|
|||||||
# Availability Zones
|
# Availability Zones
|
||||||
self._add_a_version(versions, 'v2.14', 'v2', 'SUPPORTED',
|
self._add_a_version(versions, 'v2.14', 'v2', 'SUPPORTED',
|
||||||
'2019-11-10T00:00:00Z', host_url)
|
'2019-11-10T00:00:00Z', host_url)
|
||||||
# TLS version and cipher options
|
# TLS cipher options
|
||||||
self._add_a_version(versions, 'v2.15', 'v2', 'SUPPORTED',
|
self._add_a_version(versions, 'v2.15', 'v2', 'SUPPORTED',
|
||||||
'2020-03-10T00:00:00Z', host_url)
|
'2020-03-10T00:00:00Z', host_url)
|
||||||
# Additional UDP Healthcheck Types (HTTP/TCP)
|
# Additional UDP Healthcheck Types (HTTP/TCP)
|
||||||
self._add_a_version(versions, 'v2.16', 'v2', 'CURRENT',
|
self._add_a_version(versions, 'v2.16', 'v2', 'SUPPORTED',
|
||||||
'2020-03-15T00:00:00Z', host_url)
|
'2020-03-15T00:00:00Z', host_url)
|
||||||
|
# Listener TLS versions
|
||||||
|
self._add_a_version(versions, 'v2.17', 'v2', 'CURRENT',
|
||||||
|
'2020-04-29T00:00:00Z', host_url)
|
||||||
return {'versions': versions}
|
return {'versions': versions}
|
||||||
|
@ -288,6 +288,10 @@ 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:
|
||||||
|
validate.check_tls_version_list(listener_dict['tls_versions'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db_listener = self.repositories.listener.create(
|
db_listener = self.repositories.listener.create(
|
||||||
lock_session, **listener_dict)
|
lock_session, **listener_dict)
|
||||||
@ -494,6 +498,10 @@ 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:
|
||||||
|
validate.check_tls_version_list(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
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ class ListenerResponse(BaseListenerType):
|
|||||||
client_crl_container_ref = wtypes.wsattr(wtypes.StringType())
|
client_crl_container_ref = wtypes.wsattr(wtypes.StringType())
|
||||||
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
||||||
tls_ciphers = wtypes.StringType()
|
tls_ciphers = wtypes.StringType()
|
||||||
|
tls_versions = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType()))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_data_model(cls, data_model, children=False):
|
def from_data_model(cls, data_model, children=False):
|
||||||
@ -84,6 +85,8 @@ class ListenerResponse(BaseListenerType):
|
|||||||
listener.l7policies = [
|
listener.l7policies = [
|
||||||
l7policy_type.from_data_model(i) for i in data_model.l7policies]
|
l7policy_type.from_data_model(i) for i in data_model.l7policies]
|
||||||
|
|
||||||
|
listener.tls_versions = data_model.tls_versions
|
||||||
|
|
||||||
return listener
|
return listener
|
||||||
|
|
||||||
|
|
||||||
@ -152,6 +155,8 @@ class ListenerPOST(BaseListenerType):
|
|||||||
client_crl_container_ref = wtypes.StringType(max_length=255)
|
client_crl_container_ref = wtypes.StringType(max_length=255)
|
||||||
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
||||||
tls_ciphers = wtypes.StringType(max_length=2048)
|
tls_ciphers = wtypes.StringType(max_length=2048)
|
||||||
|
tls_versions = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(
|
||||||
|
max_length=32)))
|
||||||
|
|
||||||
|
|
||||||
class ListenerRootPOST(types.BaseType):
|
class ListenerRootPOST(types.BaseType):
|
||||||
@ -190,6 +195,8 @@ class ListenerPUT(BaseListenerType):
|
|||||||
client_crl_container_ref = wtypes.StringType(max_length=255)
|
client_crl_container_ref = wtypes.StringType(max_length=255)
|
||||||
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
||||||
tls_ciphers = wtypes.StringType(max_length=2048)
|
tls_ciphers = wtypes.StringType(max_length=2048)
|
||||||
|
tls_versions = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(
|
||||||
|
max_length=32)))
|
||||||
|
|
||||||
|
|
||||||
class ListenerRootPUT(types.BaseType):
|
class ListenerRootPUT(types.BaseType):
|
||||||
@ -241,6 +248,8 @@ class ListenerSingleCreate(BaseListenerType):
|
|||||||
client_crl_container_ref = wtypes.StringType(max_length=255)
|
client_crl_container_ref = wtypes.StringType(max_length=255)
|
||||||
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
allowed_cidrs = wtypes.wsattr([types.CidrType()])
|
||||||
tls_ciphers = wtypes.StringType(max_length=2048)
|
tls_ciphers = wtypes.StringType(max_length=2048)
|
||||||
|
tls_versions = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(
|
||||||
|
max_length=32)))
|
||||||
|
|
||||||
|
|
||||||
class ListenerStatusResponse(BaseListenerType):
|
class ListenerStatusResponse(BaseListenerType):
|
||||||
|
@ -115,7 +115,11 @@ api_opts = [
|
|||||||
"new TLS-enabled pools.")),
|
"new TLS-enabled pools.")),
|
||||||
cfg.StrOpt('tls_cipher_blacklist', default='',
|
cfg.StrOpt('tls_cipher_blacklist', default='',
|
||||||
help=_("Colon separated list of OpenSSL ciphers. "
|
help=_("Colon separated list of OpenSSL ciphers. "
|
||||||
"Usage of these ciphers will be blocked."))
|
"Usage of these ciphers will be blocked.")),
|
||||||
|
cfg.ListOpt('default_listener_tls_versions',
|
||||||
|
default=constants.TLS_VERSIONS_OWASP_SUITE_B,
|
||||||
|
help=_('List of TLS versions to use for new TLS-enabled '
|
||||||
|
'listeners.'))
|
||||||
]
|
]
|
||||||
|
|
||||||
# Options only used by the amphora agent
|
# Options only used by the amphora agent
|
||||||
|
@ -785,3 +785,14 @@ CIPHERS_OWASP_SUITE_B = ('TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:'
|
|||||||
'ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:'
|
'ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:'
|
||||||
'DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:'
|
'DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:'
|
||||||
'ECDHE-RSA-AES128-SHA256')
|
'ECDHE-RSA-AES128-SHA256')
|
||||||
|
TLS_VERSIONS_OWASP_SUITE_B = [lib_consts.TLS_VERSION_1_2,
|
||||||
|
lib_consts.TLS_VERSION_1_3]
|
||||||
|
|
||||||
|
# All supported TLS versions in ascending order (oldest to newest)
|
||||||
|
TLS_ALL_VERSIONS = [
|
||||||
|
lib_consts.SSL_VERSION_3,
|
||||||
|
lib_consts.TLS_VERSION_1,
|
||||||
|
lib_consts.TLS_VERSION_1_1,
|
||||||
|
lib_consts.TLS_VERSION_1_2,
|
||||||
|
lib_consts.TLS_VERSION_1_3
|
||||||
|
]
|
||||||
|
@ -390,7 +390,7 @@ class Listener(BaseDataModel):
|
|||||||
timeout_member_data=None, timeout_tcp_inspect=None,
|
timeout_member_data=None, timeout_tcp_inspect=None,
|
||||||
tags=None, client_ca_tls_certificate_id=None,
|
tags=None, client_ca_tls_certificate_id=None,
|
||||||
client_authentication=None, client_crl_container_id=None,
|
client_authentication=None, client_crl_container_id=None,
|
||||||
allowed_cidrs=None, tls_ciphers=None):
|
allowed_cidrs=None, tls_ciphers=None, tls_versions=None):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.project_id = project_id
|
self.project_id = project_id
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -424,6 +424,7 @@ class Listener(BaseDataModel):
|
|||||||
self.client_crl_container_id = client_crl_container_id
|
self.client_crl_container_id = client_crl_container_id
|
||||||
self.allowed_cidrs = allowed_cidrs or []
|
self.allowed_cidrs = allowed_cidrs or []
|
||||||
self.tls_ciphers = tls_ciphers
|
self.tls_ciphers = tls_ciphers
|
||||||
|
self.tls_versions = tls_versions
|
||||||
|
|
||||||
def update(self, update_dict):
|
def update(self, update_dict):
|
||||||
for key, value in update_dict.items():
|
for key, value in update_dict.items():
|
||||||
|
@ -16,6 +16,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
|
|
||||||
from octavia.common.config import cfg
|
from octavia.common.config import cfg
|
||||||
from octavia.common import constants
|
from octavia.common import constants
|
||||||
@ -167,7 +168,7 @@ class JinjaTemplater(object):
|
|||||||
CONF.amphora_agent.administrative_log_facility,
|
CONF.amphora_agent.administrative_log_facility,
|
||||||
'user_log_facility': CONF.amphora_agent.user_log_facility,
|
'user_log_facility': CONF.amphora_agent.user_log_facility,
|
||||||
'connection_logging': self.connection_logging},
|
'connection_logging': self.connection_logging},
|
||||||
constants=constants)
|
constants=constants, lib_consts=lib_consts)
|
||||||
|
|
||||||
def _transform_loadbalancer(self, host_amphora, loadbalancer, listeners,
|
def _transform_loadbalancer(self, host_amphora, loadbalancer, listeners,
|
||||||
tls_certs, feature_compatibility):
|
tls_certs, feature_compatibility):
|
||||||
@ -282,9 +283,11 @@ class JinjaTemplater(object):
|
|||||||
os.path.join(self.base_crt_dir, loadbalancer.id,
|
os.path.join(self.base_crt_dir, loadbalancer.id,
|
||||||
tls_certs[listener.client_crl_container_id]))
|
tls_certs[listener.client_crl_container_id]))
|
||||||
|
|
||||||
if (listener.protocol == constants.PROTOCOL_TERMINATED_HTTPS and
|
if listener.protocol == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
listener.tls_ciphers is not None):
|
if listener.tls_ciphers is not None:
|
||||||
ret_value['tls_ciphers'] = listener.tls_ciphers
|
ret_value['tls_ciphers'] = listener.tls_ciphers
|
||||||
|
if listener.tls_versions is not None:
|
||||||
|
ret_value['tls_versions'] = listener.tls_versions
|
||||||
|
|
||||||
pools = []
|
pools = []
|
||||||
pool_gen = (pool for pool in listener.pools if
|
pool_gen = (pool for pool in listener.pools if
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
{% block proxies %}
|
{% block proxies %}
|
||||||
{% if loadbalancer.enabled %}
|
{% if loadbalancer.enabled %}
|
||||||
{% for listener in loadbalancer.listeners if listener.enabled %}
|
{% for listener in loadbalancer.listeners if listener.enabled %}
|
||||||
{{- frontend_macro(constants, listener, loadbalancer.vip_address) }}
|
{{- frontend_macro(constants, lib_consts, listener, loadbalancer.vip_address) }}
|
||||||
{% for pool in listener.pools if pool.enabled %}
|
{% for pool in listener.pools if pool.enabled %}
|
||||||
{{- backend_macro(constants, listener, pool, loadbalancer) }}
|
{{- backend_macro(constants, listener, pool, loadbalancer) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -26,7 +26,7 @@ peers {{ "%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% macro bind_macro(constants, listener, lb_vip_address) %}
|
{% macro bind_macro(constants, lib_consts, listener, lb_vip_address) %}
|
||||||
{% if listener.crt_list_filename is defined %}
|
{% if listener.crt_list_filename is defined %}
|
||||||
{% set def_crt_opt = ("ssl crt-list %s"|format(
|
{% set def_crt_opt = ("ssl crt-list %s"|format(
|
||||||
listener.crt_list_filename)|trim()) %}
|
listener.crt_list_filename)|trim()) %}
|
||||||
@ -48,8 +48,26 @@ peers {{ "%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
|
|||||||
{% else %}
|
{% else %}
|
||||||
{% set ciphers_opt = "" %}
|
{% set ciphers_opt = "" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% set tls_versions_opt = "" %}
|
||||||
|
{% if listener.tls_versions is defined %}
|
||||||
|
{% if lib_consts.SSL_VERSION_3 not in listener.tls_versions %}
|
||||||
|
{% set tls_versions_opt = tls_versions_opt + " no-sslv3" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if lib_consts.TLS_VERSION_1 not in listener.tls_versions %}
|
||||||
|
{% set tls_versions_opt = tls_versions_opt + " no-tlsv10" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if lib_consts.TLS_VERSION_1_1 not in listener.tls_versions %}
|
||||||
|
{% set tls_versions_opt = tls_versions_opt + " no-tlsv11" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if lib_consts.TLS_VERSION_1_2 not in listener.tls_versions %}
|
||||||
|
{% set tls_versions_opt = tls_versions_opt + " no-tlsv12" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if lib_consts.TLS_VERSION_1_3 not in listener.tls_versions %}
|
||||||
|
{% set tls_versions_opt = tls_versions_opt + " no-tlsv13" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
bind {{ lb_vip_address }}:{{ listener.protocol_port }} {{
|
bind {{ lb_vip_address }}:{{ listener.protocol_port }} {{
|
||||||
"%s %s %s %s"|format(def_crt_opt, client_ca_opt, ca_crl_opt, ciphers_opt)|trim() }}
|
"%s %s %s %s%s"|format(def_crt_opt, client_ca_opt, ca_crl_opt, ciphers_opt, tls_versions_opt)|trim() }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
@ -134,7 +152,7 @@ bind {{ lb_vip_address }}:{{ listener.protocol_port }} {{
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% macro frontend_macro(constants, listener, lb_vip_address) %}
|
{% macro frontend_macro(constants, lib_consts, listener, lb_vip_address) %}
|
||||||
frontend {{ listener.id }}
|
frontend {{ listener.id }}
|
||||||
{% if listener.connection_limit is defined %}
|
{% if listener.connection_limit is defined %}
|
||||||
maxconn {{ listener.connection_limit }}
|
maxconn {{ listener.connection_limit }}
|
||||||
@ -143,7 +161,7 @@ frontend {{ listener.id }}
|
|||||||
constants.PROTOCOL_TERMINATED_HTTPS.lower()) %}
|
constants.PROTOCOL_TERMINATED_HTTPS.lower()) %}
|
||||||
redirect scheme https if !{ ssl_fc }
|
redirect scheme https if !{ ssl_fc }
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ bind_macro(constants, listener, lb_vip_address)|trim() }}
|
{{ bind_macro(constants, lib_consts, listener, lb_vip_address)|trim() }}
|
||||||
mode {{ listener.protocol_mode }}
|
mode {{ listener.protocol_mode }}
|
||||||
{% for l7policy in listener.l7policies if (l7policy.enabled and
|
{% for l7policy in listener.l7policies if (l7policy.enabled and
|
||||||
l7policy.l7rules|length > 0) %}
|
l7policy.l7rules|length > 0) %}
|
||||||
|
@ -459,3 +459,15 @@ def check_default_ciphers_blacklist_conflict():
|
|||||||
raise exceptions.ValidationException(
|
raise exceptions.ValidationException(
|
||||||
detail=_('Default pool ciphers conflict with blacklist. '
|
detail=_('Default pool ciphers conflict with blacklist. '
|
||||||
'Conflicting ciphers: ' + ', '.join(pool_rejected)))
|
'Conflicting ciphers: ' + ', '.join(pool_rejected)))
|
||||||
|
|
||||||
|
|
||||||
|
def check_tls_version_list(versions):
|
||||||
|
if versions == []:
|
||||||
|
raise exceptions.ValidationException(
|
||||||
|
detail=_('Empty TLS version list. Either specify at least one TLS '
|
||||||
|
'version or remove this parameter to use the default.'))
|
||||||
|
invalid_versions = [v for v in versions
|
||||||
|
if v not in constants.TLS_ALL_VERSIONS]
|
||||||
|
if invalid_versions:
|
||||||
|
raise exceptions.ValidationException(
|
||||||
|
detail=_('Invalid TLS versions: ' + ', '.join(invalid_versions)))
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
# Copyright 2020 Dawson Coleman
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""add listener tls versions column
|
||||||
|
|
||||||
|
Revision ID: e5493ae5f9a7
|
||||||
|
Revises: fbd705961c3a
|
||||||
|
Create Date: 2020-04-19 02:35:28.502424
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'e5493ae5f9a7'
|
||||||
|
down_revision = 'fbd705961c3a'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column(
|
||||||
|
'listener',
|
||||||
|
sa.Column('tls_versions', sa.String(512), nullable=True)
|
||||||
|
)
|
@ -21,6 +21,7 @@ from sqlalchemy.ext import orderinglist
|
|||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
from sqlalchemy.orm import validates
|
from sqlalchemy.orm import validates
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
from sqlalchemy_utils import ScalarListType
|
||||||
|
|
||||||
from octavia.api.v2.types import amphora
|
from octavia.api.v2.types import amphora
|
||||||
from octavia.api.v2.types import availability_zone_profile
|
from octavia.api.v2.types import availability_zone_profile
|
||||||
@ -522,6 +523,7 @@ class Listener(base_models.BASE, base_models.IdMixin,
|
|||||||
nullable=False, default=constants.CLIENT_AUTH_NONE)
|
nullable=False, default=constants.CLIENT_AUTH_NONE)
|
||||||
client_crl_container_id = sa.Column(sa.String(255), nullable=True)
|
client_crl_container_id = sa.Column(sa.String(255), nullable=True)
|
||||||
tls_ciphers = sa.Column(sa.String(2048), nullable=True)
|
tls_ciphers = sa.Column(sa.String(2048), nullable=True)
|
||||||
|
tls_versions = sa.Column(ScalarListType(), nullable=True)
|
||||||
|
|
||||||
_tags = orm.relationship(
|
_tags = orm.relationship(
|
||||||
'Tags',
|
'Tags',
|
||||||
|
@ -104,11 +104,16 @@ def create_listener(listener_dict, lb_id):
|
|||||||
if 'client_authentication' not in listener_dict:
|
if 'client_authentication' not in listener_dict:
|
||||||
listener_dict['client_authentication'] = constants.CLIENT_AUTH_NONE
|
listener_dict['client_authentication'] = constants.CLIENT_AUTH_NONE
|
||||||
|
|
||||||
if (listener_dict['protocol'] == constants.PROTOCOL_TERMINATED_HTTPS and
|
if listener_dict['protocol'] == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
('tls_ciphers' not in listener_dict or
|
if ('tls_ciphers' not in listener_dict or
|
||||||
listener_dict['tls_ciphers'] is None)):
|
listener_dict['tls_ciphers'] is None):
|
||||||
listener_dict['tls_ciphers'] = (
|
listener_dict['tls_ciphers'] = (
|
||||||
CONF.api_settings.default_listener_ciphers)
|
CONF.api_settings.default_listener_ciphers)
|
||||||
|
if ('tls_versions' not in listener_dict or
|
||||||
|
listener_dict['tls_versions'] is None):
|
||||||
|
listener_dict['tls_versions'] = (
|
||||||
|
CONF.api_settings.default_listener_tls_versions)
|
||||||
|
|
||||||
return listener_dict
|
return listener_dict
|
||||||
|
|
||||||
|
|
||||||
|
@ -467,7 +467,8 @@ class SampleDriverDataModels(object):
|
|||||||
lib_consts.CLIENT_AUTHENTICATION: constants.CLIENT_AUTH_NONE,
|
lib_consts.CLIENT_AUTHENTICATION: constants.CLIENT_AUTH_NONE,
|
||||||
constants.CLIENT_CRL_CONTAINER_ID: self.client_crl_container_ref,
|
constants.CLIENT_CRL_CONTAINER_ID: self.client_crl_container_ref,
|
||||||
lib_consts.ALLOWED_CIDRS: ['192.0.2.0/24', '198.51.100.0/24'],
|
lib_consts.ALLOWED_CIDRS: ['192.0.2.0/24', '198.51.100.0/24'],
|
||||||
lib_consts.TLS_CIPHERS: constants.CIPHERS_OWASP_SUITE_B
|
lib_consts.TLS_CIPHERS: constants.CIPHERS_OWASP_SUITE_B,
|
||||||
|
lib_consts.TLS_VERSIONS: constants.TLS_VERSIONS_OWASP_SUITE_B
|
||||||
}
|
}
|
||||||
|
|
||||||
self.test_listener1_dict.update(self._common_test_dict)
|
self.test_listener1_dict.update(self._common_test_dict)
|
||||||
@ -536,7 +537,7 @@ class SampleDriverDataModels(object):
|
|||||||
lib_consts.CLIENT_CRL_CONTAINER_REF: self.client_crl_container_ref,
|
lib_consts.CLIENT_CRL_CONTAINER_REF: self.client_crl_container_ref,
|
||||||
lib_consts.CLIENT_CRL_CONTAINER_DATA: crl_file_content,
|
lib_consts.CLIENT_CRL_CONTAINER_DATA: crl_file_content,
|
||||||
lib_consts.TLS_CIPHERS: constants.CIPHERS_OWASP_SUITE_B,
|
lib_consts.TLS_CIPHERS: constants.CIPHERS_OWASP_SUITE_B,
|
||||||
lib_consts.TLS_VERSIONS: None
|
lib_consts.TLS_VERSIONS: constants.TLS_VERSIONS_OWASP_SUITE_B
|
||||||
}
|
}
|
||||||
|
|
||||||
self.provider_listener2_dict = copy.deepcopy(
|
self.provider_listener2_dict = copy.deepcopy(
|
||||||
|
@ -45,7 +45,7 @@ class TestRootController(base_db_test.OctaviaDBTestBase):
|
|||||||
def test_api_versions(self):
|
def test_api_versions(self):
|
||||||
versions = self._get_versions_with_config()
|
versions = self._get_versions_with_config()
|
||||||
version_ids = tuple(v.get('id') for v in versions)
|
version_ids = tuple(v.get('id') for v in versions)
|
||||||
self.assertEqual(17, len(version_ids))
|
self.assertEqual(18, len(version_ids))
|
||||||
self.assertIn('v2.0', version_ids)
|
self.assertIn('v2.0', version_ids)
|
||||||
self.assertIn('v2.1', version_ids)
|
self.assertIn('v2.1', version_ids)
|
||||||
self.assertIn('v2.2', version_ids)
|
self.assertIn('v2.2', version_ids)
|
||||||
@ -63,6 +63,7 @@ class TestRootController(base_db_test.OctaviaDBTestBase):
|
|||||||
self.assertIn('v2.14', version_ids)
|
self.assertIn('v2.14', version_ids)
|
||||||
self.assertIn('v2.15', version_ids)
|
self.assertIn('v2.15', version_ids)
|
||||||
self.assertIn('v2.16', version_ids)
|
self.assertIn('v2.16', version_ids)
|
||||||
|
self.assertIn('v2.17', version_ids)
|
||||||
|
|
||||||
# Each version should have a 'self' 'href' to the API version URL
|
# Each version should have a 'self' 'href' to the API version URL
|
||||||
# [{u'rel': u'self', u'href': u'http://localhost/v2'}]
|
# [{u'rel': u'self', u'href': u'http://localhost/v2'}]
|
||||||
|
@ -2622,7 +2622,8 @@ class TestLoadBalancerGraph(base.BaseAPITest):
|
|||||||
'client_authentication': constants.CLIENT_AUTH_NONE,
|
'client_authentication': constants.CLIENT_AUTH_NONE,
|
||||||
'client_crl_container_ref': None,
|
'client_crl_container_ref': None,
|
||||||
'allowed_cidrs': None,
|
'allowed_cidrs': None,
|
||||||
'tls_ciphers': None
|
'tls_ciphers': None,
|
||||||
|
'tls_versions': None
|
||||||
}
|
}
|
||||||
if create_sni_containers:
|
if create_sni_containers:
|
||||||
create_listener['sni_container_refs'] = create_sni_containers
|
create_listener['sni_container_refs'] = create_sni_containers
|
||||||
@ -2670,6 +2671,8 @@ class TestLoadBalancerGraph(base.BaseAPITest):
|
|||||||
expected_listener['allowed_cidrs'] = expected_allowed_cidrs
|
expected_listener['allowed_cidrs'] = expected_allowed_cidrs
|
||||||
if create_protocol == constants.PROTOCOL_TERMINATED_HTTPS:
|
if create_protocol == constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
expected_listener['tls_ciphers'] = constants.CIPHERS_OWASP_SUITE_B
|
expected_listener['tls_ciphers'] = constants.CIPHERS_OWASP_SUITE_B
|
||||||
|
expected_listener['tls_versions'] = (
|
||||||
|
constants.TLS_VERSIONS_OWASP_SUITE_B)
|
||||||
|
|
||||||
return create_listener, expected_listener
|
return create_listener, expected_listener
|
||||||
|
|
||||||
|
@ -127,8 +127,6 @@ class TestUtils(base.TestCase):
|
|||||||
'provider': 'noop_driver'}
|
'provider': 'noop_driver'}
|
||||||
ref_listeners = copy.deepcopy(self.sample_data.provider_listeners)
|
ref_listeners = copy.deepcopy(self.sample_data.provider_listeners)
|
||||||
# TODO(johnsom) Remove when versions implemented
|
# TODO(johnsom) Remove when versions implemented
|
||||||
for listener in ref_listeners:
|
|
||||||
delattr(listener, lib_constants.TLS_VERSIONS)
|
|
||||||
expect_pools = copy.deepcopy(self.sample_data.provider_pools,)
|
expect_pools = copy.deepcopy(self.sample_data.provider_pools,)
|
||||||
for pool in expect_pools:
|
for pool in expect_pools:
|
||||||
delattr(pool, lib_constants.TLS_VERSIONS)
|
delattr(pool, lib_constants.TLS_VERSIONS)
|
||||||
@ -204,9 +202,6 @@ class TestUtils(base.TestCase):
|
|||||||
provider_listeners = utils.db_listeners_to_provider_listeners(
|
provider_listeners = utils.db_listeners_to_provider_listeners(
|
||||||
self.sample_data.test_db_listeners)
|
self.sample_data.test_db_listeners)
|
||||||
ref_listeners = copy.deepcopy(self.sample_data.provider_listeners)
|
ref_listeners = copy.deepcopy(self.sample_data.provider_listeners)
|
||||||
# TODO(johnsom) Remove when versions implemented
|
|
||||||
for listener in ref_listeners:
|
|
||||||
delattr(listener, lib_constants.TLS_VERSIONS)
|
|
||||||
self.assertEqual(ref_listeners, provider_listeners)
|
self.assertEqual(ref_listeners, provider_listeners)
|
||||||
|
|
||||||
@mock.patch('oslo_context.context.RequestContext', return_value=None)
|
@mock.patch('oslo_context.context.RequestContext', return_value=None)
|
||||||
@ -248,7 +243,6 @@ class TestUtils(base.TestCase):
|
|||||||
expect_pool_prov = copy.deepcopy(self.sample_data.provider_pool1_dict)
|
expect_pool_prov = copy.deepcopy(self.sample_data.provider_pool1_dict)
|
||||||
# TODO(johnsom) Remove when versions and ciphers are implemented
|
# TODO(johnsom) Remove when versions and ciphers are implemented
|
||||||
expect_pool_prov.pop(lib_constants.TLS_VERSIONS)
|
expect_pool_prov.pop(lib_constants.TLS_VERSIONS)
|
||||||
expect_prov.pop(lib_constants.TLS_VERSIONS)
|
|
||||||
expect_prov['default_pool'] = expect_pool_prov
|
expect_prov['default_pool'] = expect_pool_prov
|
||||||
provider_listener = utils.listener_dict_to_provider_dict(
|
provider_listener = utils.listener_dict_to_provider_dict(
|
||||||
self.sample_data.test_listener1_dict)
|
self.sample_data.test_listener1_dict)
|
||||||
@ -284,7 +278,6 @@ class TestUtils(base.TestCase):
|
|||||||
del expect_pool_prov['tls_container_data']
|
del expect_pool_prov['tls_container_data']
|
||||||
# TODO(johnsom) Remove when versions and ciphers are implemented
|
# TODO(johnsom) Remove when versions and ciphers are implemented
|
||||||
expect_pool_prov.pop(lib_constants.TLS_VERSIONS)
|
expect_pool_prov.pop(lib_constants.TLS_VERSIONS)
|
||||||
expect_prov.pop(lib_constants.TLS_VERSIONS)
|
|
||||||
expect_prov['default_pool'] = expect_pool_prov
|
expect_prov['default_pool'] = expect_pool_prov
|
||||||
del expect_prov['default_tls_container_data']
|
del expect_prov['default_tls_container_data']
|
||||||
del expect_prov['sni_container_data']
|
del expect_prov['sni_container_data']
|
||||||
|
@ -51,7 +51,8 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
"ssl crt-list {crt_list} "
|
"ssl crt-list {crt_list} "
|
||||||
"ca-file /var/lib/octavia/certs/sample_loadbalancer_id_1/"
|
"ca-file /var/lib/octavia/certs/sample_loadbalancer_id_1/"
|
||||||
"client_ca.pem verify required crl-file /var/lib/octavia/"
|
"client_ca.pem verify required crl-file /var/lib/octavia/"
|
||||||
"certs/sample_loadbalancer_id_1/SHA_ID.pem ciphers {ciphers}\n"
|
"certs/sample_loadbalancer_id_1/SHA_ID.pem ciphers {ciphers} "
|
||||||
|
"no-sslv3 no-tlsv10 no-tlsv11\n"
|
||||||
" mode http\n"
|
" mode http\n"
|
||||||
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
" timeout client 50000\n").format(
|
" timeout client 50000\n").format(
|
||||||
@ -104,7 +105,7 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
" maxconn {maxconn}\n"
|
" maxconn {maxconn}\n"
|
||||||
" redirect scheme https if !{{ ssl_fc }}\n"
|
" redirect scheme https if !{{ ssl_fc }}\n"
|
||||||
" bind 10.0.0.2:443 ssl crt-list {crt_list}"
|
" bind 10.0.0.2:443 ssl crt-list {crt_list}"
|
||||||
" ciphers {ciphers}\n"
|
" ciphers {ciphers} no-sslv3 no-tlsv10 no-tlsv11\n"
|
||||||
" mode http\n"
|
" mode http\n"
|
||||||
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
" timeout client 50000\n").format(
|
" timeout client 50000\n").format(
|
||||||
@ -153,7 +154,8 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
fe = ("frontend sample_listener_id_1\n"
|
fe = ("frontend sample_listener_id_1\n"
|
||||||
" maxconn {maxconn}\n"
|
" maxconn {maxconn}\n"
|
||||||
" redirect scheme https if !{{ ssl_fc }}\n"
|
" redirect scheme https if !{{ ssl_fc }}\n"
|
||||||
" bind 10.0.0.2:443 ssl crt-list {crt_list}\n"
|
" bind 10.0.0.2:443 ssl crt-list {crt_list} "
|
||||||
|
"no-sslv3 no-tlsv10 no-tlsv11\n"
|
||||||
" mode http\n"
|
" mode http\n"
|
||||||
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
" default_backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
" timeout client 50000\n").format(
|
" timeout client 50000\n").format(
|
||||||
@ -192,6 +194,111 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
frontend=fe, backend=be),
|
frontend=fe, backend=be),
|
||||||
rendered_obj)
|
rendered_obj)
|
||||||
|
|
||||||
|
def test_render_template_tls_no_versions(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-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 ciphers {ciphers}\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,
|
||||||
|
crt_list=FAKE_CRT_LIST_FILENAME,
|
||||||
|
ciphers=constants.CIPHERS_OWASP_SUITE_B)
|
||||||
|
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
|
" mode http\n"
|
||||||
|
" balance roundrobin\n"
|
||||||
|
" cookie SRV insert indirect nocache\n"
|
||||||
|
" timeout check 31s\n"
|
||||||
|
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
|
||||||
|
" http-check expect rstatus 418\n"
|
||||||
|
" fullconn {maxconn}\n"
|
||||||
|
" option allbackups\n"
|
||||||
|
" timeout connect 5000\n"
|
||||||
|
" timeout server 50000\n"
|
||||||
|
" server sample_member_id_1 10.0.0.99:82 "
|
||||||
|
"weight 13 check inter 30s fall 3 rise 2 "
|
||||||
|
"cookie sample_member_id_1\n"
|
||||||
|
" server sample_member_id_2 10.0.0.98:82 "
|
||||||
|
"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'),
|
||||||
|
'cont_id_ca': 'client_ca.pem',
|
||||||
|
'cont_id_crl': 'SHA_ID.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,
|
||||||
|
client_ca_cert=True, client_crl_cert=True, tls_versions=None)],
|
||||||
|
tls_tupe)
|
||||||
|
self.assertEqual(
|
||||||
|
sample_configs_combined.sample_base_expected_config(
|
||||||
|
frontend=fe, backend=be),
|
||||||
|
rendered_obj)
|
||||||
|
|
||||||
|
def test_render_template_tls_no_ciphers_or_versions(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-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,
|
||||||
|
crt_list=FAKE_CRT_LIST_FILENAME)
|
||||||
|
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
|
" mode http\n"
|
||||||
|
" balance roundrobin\n"
|
||||||
|
" cookie SRV insert indirect nocache\n"
|
||||||
|
" timeout check 31s\n"
|
||||||
|
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
|
||||||
|
" http-check expect rstatus 418\n"
|
||||||
|
" fullconn {maxconn}\n"
|
||||||
|
" option allbackups\n"
|
||||||
|
" timeout connect 5000\n"
|
||||||
|
" timeout server 50000\n"
|
||||||
|
" server sample_member_id_1 10.0.0.99:82 "
|
||||||
|
"weight 13 check inter 30s fall 3 rise 2 "
|
||||||
|
"cookie sample_member_id_1\n"
|
||||||
|
" server sample_member_id_2 10.0.0.98:82 "
|
||||||
|
"weight 13 check inter 30s fall 3 rise 2 "
|
||||||
|
"cookie sample_member_id_2\n\n").format(
|
||||||
|
maxconn=constants.HAPROXY_MAX_MAXCONN)
|
||||||
|
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_ciphers=None,
|
||||||
|
tls_versions=None)],
|
||||||
|
tls_certs={'cont_id_1':
|
||||||
|
sample_configs_combined.sample_tls_container_tuple(
|
||||||
|
id='tls_container_id',
|
||||||
|
certificate='ImAalsdkfjCert',
|
||||||
|
private_key='ImAsdlfksdjPrivateKey',
|
||||||
|
primary_cn="FakeCN")})
|
||||||
|
self.assertEqual(
|
||||||
|
sample_configs_combined.sample_base_expected_config(
|
||||||
|
frontend=fe, backend=be),
|
||||||
|
rendered_obj)
|
||||||
|
|
||||||
def test_render_template_http(self):
|
def test_render_template_http(self):
|
||||||
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
" mode http\n"
|
" mode http\n"
|
||||||
@ -1180,7 +1287,8 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
" maxconn 1000000\n"
|
" maxconn 1000000\n"
|
||||||
" redirect scheme https if !{ ssl_fc }\n"
|
" redirect scheme https if !{ ssl_fc }\n"
|
||||||
" bind 10.0.0.2:443 ciphers " +
|
" bind 10.0.0.2:443 ciphers " +
|
||||||
constants.CIPHERS_OWASP_SUITE_B + "\n"
|
constants.CIPHERS_OWASP_SUITE_B +
|
||||||
|
" no-sslv3 no-tlsv10 no-tlsv11\n"
|
||||||
" mode http\n"
|
" mode http\n"
|
||||||
" acl sample_l7rule_id_1 path -m beg /api\n"
|
" acl sample_l7rule_id_1 path -m beg /api\n"
|
||||||
" use_backend sample_pool_id_2:sample_listener_id_1"
|
" use_backend sample_pool_id_2:sample_listener_id_1"
|
||||||
|
@ -602,12 +602,14 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
|
|||||||
id='sample_listener_id_1', recursive_nest=False,
|
id='sample_listener_id_1', recursive_nest=False,
|
||||||
provisioning_status=constants.ACTIVE,
|
provisioning_status=constants.ACTIVE,
|
||||||
tls_ciphers=constants.CIPHERS_OWASP_SUITE_B,
|
tls_ciphers=constants.CIPHERS_OWASP_SUITE_B,
|
||||||
backend_tls_ciphers=None):
|
backend_tls_ciphers=None,
|
||||||
|
tls_versions=constants.TLS_VERSIONS_OWASP_SUITE_B):
|
||||||
proto = 'HTTP' if proto is None else proto
|
proto = 'HTTP' if proto is None else proto
|
||||||
if be_proto is None:
|
if be_proto is None:
|
||||||
be_proto = 'HTTP' if proto == 'TERMINATED_HTTPS' else proto
|
be_proto = 'HTTP' if proto == 'TERMINATED_HTTPS' else proto
|
||||||
if proto != constants.PROTOCOL_TERMINATED_HTTPS:
|
if proto != constants.PROTOCOL_TERMINATED_HTTPS:
|
||||||
tls_ciphers = None
|
tls_ciphers = None
|
||||||
|
tls_versions = None
|
||||||
topology = 'SINGLE' if topology is None else topology
|
topology = 'SINGLE' if topology is None else topology
|
||||||
port = '443' if proto in ['HTTPS', 'TERMINATED_HTTPS'] else '80'
|
port = '443' if proto in ['HTTPS', 'TERMINATED_HTTPS'] else '80'
|
||||||
peer_port = 1024 if peer_port is None else peer_port
|
peer_port = 1024 if peer_port is None else peer_port
|
||||||
@ -622,7 +624,7 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
|
|||||||
'timeout_tcp_inspect, client_ca_tls_certificate_id, '
|
'timeout_tcp_inspect, client_ca_tls_certificate_id, '
|
||||||
'client_ca_tls_certificate, client_authentication, '
|
'client_ca_tls_certificate, client_authentication, '
|
||||||
'client_crl_container_id, provisioning_status, '
|
'client_crl_container_id, provisioning_status, '
|
||||||
'tls_ciphers')
|
'tls_ciphers, tls_versions')
|
||||||
if l7:
|
if l7:
|
||||||
pools = [
|
pools = [
|
||||||
sample_pool_tuple(
|
sample_pool_tuple(
|
||||||
@ -737,7 +739,8 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
|
|||||||
constants.CLIENT_AUTH_NONE),
|
constants.CLIENT_AUTH_NONE),
|
||||||
client_crl_container_id='cont_id_crl' if client_crl_cert else '',
|
client_crl_container_id='cont_id_crl' if client_crl_cert else '',
|
||||||
provisioning_status=provisioning_status,
|
provisioning_status=provisioning_status,
|
||||||
tls_ciphers=tls_ciphers
|
tls_ciphers=tls_ciphers,
|
||||||
|
tls_versions=tls_versions
|
||||||
)
|
)
|
||||||
if recursive_nest:
|
if recursive_nest:
|
||||||
listener.load_balancer.listeners.append(listener)
|
listener.load_balancer.listeners.append(listener)
|
||||||
|
@ -470,3 +470,17 @@ class TestValidations(base.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(exceptions.ValidationException,
|
self.assertRaises(exceptions.ValidationException,
|
||||||
validate.check_default_ciphers_blacklist_conflict)
|
validate.check_default_ciphers_blacklist_conflict)
|
||||||
|
|
||||||
|
def test_check_tls_version_list(self):
|
||||||
|
# Test valid list
|
||||||
|
validate.check_tls_version_list(['TLSv1.1', 'TLSv1.2', 'TLSv1.3'])
|
||||||
|
# Test invalid list
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.ValidationException,
|
||||||
|
validate.check_tls_version_list,
|
||||||
|
['SSLv3', 'TLSv1.0'])
|
||||||
|
# Test empty list
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.ValidationException,
|
||||||
|
validate.check_tls_version_list,
|
||||||
|
[])
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
HTTPS-terminated listeners can now be configured to use only specified
|
||||||
|
versions of TLS. Default TLS versions for new listeners can be set with
|
||||||
|
``default_listener_tls_versions`` in ``octavia.conf``. Existing listeners
|
||||||
|
will continue to use the old defaults.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
HTTPS-terminated listeners will now only allow TLS1.2 and TLS1.3 by
|
||||||
|
default. If no TLS versions are specified at listener create time, the
|
||||||
|
listener will only accept TLS1.2 and TLS1.3 connections. Previously TLS
|
||||||
|
listeners would accept any TLS version. Existing listeners will not be
|
||||||
|
changed.
|
Loading…
Reference in New Issue
Block a user