diff --git a/etc/octavia.conf b/etc/octavia.conf index e7113eeb01..832312899b 100644 --- a/etc/octavia.conf +++ b/etc/octavia.conf @@ -298,6 +298,9 @@ # agent_server_network_file = # agent_request_read_timeout = 180 +# Minimum TLS protocol, eg: TLS, TLSv1.1, TLSv1.2, TLSv1.3 (if available) +# agent_tls_protocol = TLSv1.2 + # Amphora default UDP driver is keepalived_lvs # # amphora_udp_driver = keepalived_lvs diff --git a/octavia/amphorae/backends/agent/agent_jinja_cfg.py b/octavia/amphorae/backends/agent/agent_jinja_cfg.py index 970014f322..75125eb3c4 100644 --- a/octavia/amphorae/backends/agent/agent_jinja_cfg.py +++ b/octavia/amphorae/backends/agent/agent_jinja_cfg.py @@ -58,4 +58,5 @@ class AgentJinjaTemplater(object): 'respawn_count': CONF.haproxy_amphora.respawn_count, 'respawn_interval': CONF.haproxy_amphora.respawn_interval, 'amphora_udp_driver': CONF.amphora_agent.amphora_udp_driver, + 'agent_tls_protocol': CONF.amphora_agent.agent_tls_protocol, 'topology': topology}) diff --git a/octavia/amphorae/backends/agent/templates/amphora_agent_conf.template b/octavia/amphorae/backends/agent/templates/amphora_agent_conf.template index 86422aca21..2d62363ce6 100644 --- a/octavia/amphorae/backends/agent/templates/amphora_agent_conf.template +++ b/octavia/amphorae/backends/agent/templates/amphora_agent_conf.template @@ -42,6 +42,7 @@ agent_server_network_file = {{ agent_server_network_file }} agent_request_read_timeout = {{ agent_request_read_timeout }} amphora_id = {{ amphora_id }} amphora_udp_driver = {{ amphora_udp_driver }} +agent_tls_protocol = {{ agent_tls_protocol }} [controller_worker] loadbalancer_topology = {{ topology }} diff --git a/octavia/amphorae/drivers/haproxy/rest_api_driver.py b/octavia/amphorae/drivers/haproxy/rest_api_driver.py index 80bca1a5d3..5f1d6263d8 100644 --- a/octavia/amphorae/drivers/haproxy/rest_api_driver.py +++ b/octavia/amphorae/drivers/haproxy/rest_api_driver.py @@ -15,6 +15,7 @@ import functools import hashlib import os +import ssl import time import warnings @@ -403,6 +404,12 @@ class CustomHostNameCheckingAdapter(requests.adapters.HTTPAdapter): return super(CustomHostNameCheckingAdapter, self).cert_verify(conn, url, verify, cert) + def init_poolmanager(self, *pool_args, **pool_kwargs): + proto = CONF.amphora_agent.agent_tls_protocol.replace('.', '_') + pool_kwargs['ssl_version'] = getattr(ssl, "PROTOCOL_%s" % proto) + return super(CustomHostNameCheckingAdapter, + self).init_poolmanager(*pool_args, **pool_kwargs) + class AmphoraAPIClient(object): def __init__(self): diff --git a/octavia/cmd/agent.py b/octavia/cmd/agent.py index 861ad759e0..d7bec51687 100644 --- a/octavia/cmd/agent.py +++ b/octavia/cmd/agent.py @@ -15,6 +15,7 @@ # make sure PYTHONPATH includes the home directory if you didn't install import multiprocessing as multiproc +import ssl import sys import gunicorn.app.base @@ -68,6 +69,7 @@ def main(): bind_ip_port = utils.ip_port_str(CONF.haproxy_amphora.bind_host, CONF.haproxy_amphora.bind_port) + proto = CONF.amphora_agent.agent_tls_protocol.replace('.', '_') options = { 'bind': bind_ip_port, 'workers': 1, @@ -75,6 +77,7 @@ def main(): 'certfile': CONF.amphora_agent.agent_server_cert, 'ca_certs': CONF.amphora_agent.agent_server_ca, 'cert_reqs': True, + 'ssl_version': getattr(ssl, "PROTOCOL_%s" % proto), 'preload_app': True, 'accesslog': '/var/log/amphora-agent.log', 'errorlog': '/var/log/amphora-agent.log', diff --git a/octavia/common/config.py b/octavia/common/config.py index 0aa676a795..52948b8378 100644 --- a/octavia/common/config.py +++ b/octavia/common/config.py @@ -18,6 +18,7 @@ Routines for configuring Octavia """ import os +import ssl import sys from keystoneauth1 import loading as ks_loading @@ -34,6 +35,9 @@ from octavia import version LOG = logging.getLogger(__name__) +TLS_PROTOCOL_CHOICES = [ + p[9:].replace('_', '.') for p in ssl._PROTOCOL_NAMES.values()] + core_opts = [ cfg.HostnameOpt('host', default=utils.get_hostname(), @@ -110,6 +114,10 @@ amphora_agent_opts = [ cfg.IntOpt('agent_request_read_timeout', default=180, help=_("The time in seconds to allow a request from the " "controller to run before terminating the socket.")), + cfg.StrOpt('agent_tls_protocol', default='TLSv1.2', + help=_("Minimum TLS protocol for communication with the " + "amphora agent."), + choices=TLS_PROTOCOL_CHOICES), # Do not specify in octavia.conf, loaded at runtime cfg.StrOpt('amphora_id', help=_("The amphora ID.")), cfg.StrOpt('amphora_udp_driver', diff --git a/octavia/tests/unit/amphorae/backends/agent/test_agent_jinja_cfg.py b/octavia/tests/unit/amphorae/backends/agent/test_agent_jinja_cfg.py index 852e9a7138..d6e5860a75 100644 --- a/octavia/tests/unit/amphorae/backends/agent/test_agent_jinja_cfg.py +++ b/octavia/tests/unit/amphorae/backends/agent/test_agent_jinja_cfg.py @@ -81,7 +81,8 @@ class AgentJinjaTestCase(base.TestCase): '/etc/network/interfaces.d/\n' 'agent_request_read_timeout = 180\n' 'amphora_id = ' + AMP_ID + '\n' - 'amphora_udp_driver = keepalived_lvs\n\n' + 'amphora_udp_driver = keepalived_lvs\n' + 'agent_tls_protocol = TLSv1.2\n\n' '[controller_worker]\n' 'loadbalancer_topology = ' + constants.TOPOLOGY_SINGLE) @@ -119,7 +120,8 @@ class AgentJinjaTestCase(base.TestCase): '/etc/network/interfaces\n' 'agent_request_read_timeout = 180\n' 'amphora_id = ' + AMP_ID + '\n' - 'amphora_udp_driver = keepalived_lvs\n\n' + 'amphora_udp_driver = keepalived_lvs\n' + 'agent_tls_protocol = TLSv1.2\n\n' '[controller_worker]\n' 'loadbalancer_topology = ' + constants.TOPOLOGY_ACTIVE_STANDBY) @@ -157,7 +159,8 @@ class AgentJinjaTestCase(base.TestCase): '/etc/network/interfaces.d/\n' 'agent_request_read_timeout = 180\n' 'amphora_id = ' + AMP_ID + '\n' - 'amphora_udp_driver = new_udp_driver\n\n' + 'amphora_udp_driver = new_udp_driver\n' + 'agent_tls_protocol = TLSv1.2\n\n' '[controller_worker]\n' 'loadbalancer_topology = ' + constants.TOPOLOGY_SINGLE) diff --git a/releasenotes/notes/force-controlplane-amphora-communication-to-use-tls1.2-1c4adf72d2ce5a82.yaml b/releasenotes/notes/force-controlplane-amphora-communication-to-use-tls1.2-1c4adf72d2ce5a82.yaml new file mode 100644 index 0000000000..950e74871f --- /dev/null +++ b/releasenotes/notes/force-controlplane-amphora-communication-to-use-tls1.2-1c4adf72d2ce5a82.yaml @@ -0,0 +1,6 @@ +--- +security: + - | + Communication between the control-plane and the amphora-agent now uses + minimum TLSv1.2 by default, and is configurable. The previous default of + SSLv2/3 is widely considered insecure.