Merge "Allow TLS ciphers/protocols to be configurable for console proxies"
This commit is contained in:
commit
bfe681cd29
|
@ -105,6 +105,8 @@ The :program:`nova-novncproxy` service accepts the following options:
|
||||||
- :oslo.config:option:`cert`
|
- :oslo.config:option:`cert`
|
||||||
- :oslo.config:option:`key`
|
- :oslo.config:option:`key`
|
||||||
- :oslo.config:option:`web`
|
- :oslo.config:option:`web`
|
||||||
|
- :oslo.config:option:`console.ssl_ciphers`
|
||||||
|
- :oslo.config:option:`console.ssl_minimum_version`
|
||||||
- :oslo.config:option:`vnc.novncproxy_host`
|
- :oslo.config:option:`vnc.novncproxy_host`
|
||||||
- :oslo.config:option:`vnc.novncproxy_port`
|
- :oslo.config:option:`vnc.novncproxy_port`
|
||||||
|
|
||||||
|
@ -326,6 +328,8 @@ The :program:`nova-spicehtml5proxy` service accepts the following options.
|
||||||
- :oslo.config:option:`cert`
|
- :oslo.config:option:`cert`
|
||||||
- :oslo.config:option:`key`
|
- :oslo.config:option:`key`
|
||||||
- :oslo.config:option:`web`
|
- :oslo.config:option:`web`
|
||||||
|
- :oslo.config:option:`console.ssl_ciphers`
|
||||||
|
- :oslo.config:option:`console.ssl_minimum_version`
|
||||||
- :oslo.config:option:`spice.html5proxy_host`
|
- :oslo.config:option:`spice.html5proxy_host`
|
||||||
- :oslo.config:option:`spice.html5proxy_port`
|
- :oslo.config:option:`spice.html5proxy_port`
|
||||||
|
|
||||||
|
@ -407,6 +411,8 @@ The :program:`nova-serialproxy` service accepts the following options.
|
||||||
- :oslo.config:option:`cert`
|
- :oslo.config:option:`cert`
|
||||||
- :oslo.config:option:`key`
|
- :oslo.config:option:`key`
|
||||||
- :oslo.config:option:`web`
|
- :oslo.config:option:`web`
|
||||||
|
- :oslo.config:option:`console.ssl_ciphers`
|
||||||
|
- :oslo.config:option:`console.ssl_minimum_version`
|
||||||
- :oslo.config:option:`serial_console.serialproxy_host`
|
- :oslo.config:option:`serial_console.serialproxy_host`
|
||||||
- :oslo.config:option:`serial_console.serialproxy_port`
|
- :oslo.config:option:`serial_console.serialproxy_port`
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,8 @@ def proxy(host, port, security_proxy=None):
|
||||||
cert=CONF.cert,
|
cert=CONF.cert,
|
||||||
key=CONF.key,
|
key=CONF.key,
|
||||||
ssl_only=CONF.ssl_only,
|
ssl_only=CONF.ssl_only,
|
||||||
|
ssl_ciphers=CONF.console.ssl_ciphers,
|
||||||
|
ssl_minimum_version=CONF.console.ssl_minimum_version,
|
||||||
daemon=CONF.daemon,
|
daemon=CONF.daemon,
|
||||||
record=CONF.record,
|
record=CONF.record,
|
||||||
traffic=not CONF.daemon,
|
traffic=not CONF.daemon,
|
||||||
|
|
|
@ -40,6 +40,44 @@ values other than host are allowed in the origin header.
|
||||||
Possible values:
|
Possible values:
|
||||||
|
|
||||||
* A list where each element is an allowed origin hostnames, else an empty list
|
* A list where each element is an allowed origin hostnames, else an empty list
|
||||||
|
"""),
|
||||||
|
cfg.StrOpt('ssl_ciphers',
|
||||||
|
help="""
|
||||||
|
OpenSSL cipher preference string that specifies what ciphers to allow for TLS
|
||||||
|
connections from clients. For example::
|
||||||
|
|
||||||
|
ssl_ciphers = "kEECDH+aECDSA+AES:kEECDH+AES+aRSA:kEDH+aRSA+AES"
|
||||||
|
|
||||||
|
See the man page for the OpenSSL `ciphers` command for details of the cipher
|
||||||
|
preference string format and allowed values::
|
||||||
|
|
||||||
|
https://www.openssl.org/docs/man1.1.0/man1/ciphers.html
|
||||||
|
|
||||||
|
Related options:
|
||||||
|
|
||||||
|
* [DEFAULT] cert
|
||||||
|
* [DEFAULT] key
|
||||||
|
"""),
|
||||||
|
cfg.StrOpt('ssl_minimum_version',
|
||||||
|
default='default',
|
||||||
|
choices=[
|
||||||
|
# These values must align with SSL_OPTIONS in
|
||||||
|
# websockify/websocketproxy.py
|
||||||
|
('default', 'Use the underlying system OpenSSL defaults'),
|
||||||
|
('tlsv1_1',
|
||||||
|
'Require TLS v1.1 or greater for TLS connections'),
|
||||||
|
('tlsv1_2',
|
||||||
|
'Require TLS v1.2 or greater for TLS connections'),
|
||||||
|
('tlsv1_3',
|
||||||
|
'Require TLS v1.3 or greater for TLS connections'),
|
||||||
|
],
|
||||||
|
help="""
|
||||||
|
Minimum allowed SSL/TLS protocol version.
|
||||||
|
|
||||||
|
Related options:
|
||||||
|
|
||||||
|
* [DEFAULT] cert
|
||||||
|
* [DEFAULT] key
|
||||||
"""),
|
"""),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -27,15 +27,37 @@ If this is not set, no recording will be done.
|
||||||
help="Run as a background process."),
|
help="Run as a background process."),
|
||||||
cfg.BoolOpt('ssl_only',
|
cfg.BoolOpt('ssl_only',
|
||||||
default=False,
|
default=False,
|
||||||
help="Disallow non-encrypted connections."),
|
help="""
|
||||||
|
Disallow non-encrypted connections.
|
||||||
|
|
||||||
|
Related options:
|
||||||
|
|
||||||
|
* cert
|
||||||
|
* key
|
||||||
|
"""),
|
||||||
cfg.BoolOpt('source_is_ipv6',
|
cfg.BoolOpt('source_is_ipv6',
|
||||||
default=False,
|
default=False,
|
||||||
help="Set to True if source host is addressed with IPv6."),
|
help="Set to True if source host is addressed with IPv6."),
|
||||||
cfg.StrOpt('cert',
|
cfg.StrOpt('cert',
|
||||||
default='self.pem',
|
default='self.pem',
|
||||||
help="Path to SSL certificate file."),
|
help="""
|
||||||
|
Path to SSL certificate file.
|
||||||
|
|
||||||
|
Related options:
|
||||||
|
|
||||||
|
* key
|
||||||
|
* ssl_only
|
||||||
|
* [console] ssl_ciphers
|
||||||
|
* [console] ssl_minimum_version
|
||||||
|
"""),
|
||||||
cfg.StrOpt('key',
|
cfg.StrOpt('key',
|
||||||
help="SSL key file (if separate from cert)."),
|
help="""
|
||||||
|
SSL key file (if separate from cert).
|
||||||
|
|
||||||
|
Related options:
|
||||||
|
|
||||||
|
* cert
|
||||||
|
"""),
|
||||||
cfg.StrOpt('web',
|
cfg.StrOpt('web',
|
||||||
default='/usr/share/spice-html5',
|
default='/usr/share/spice-html5',
|
||||||
help="""
|
help="""
|
||||||
|
|
|
@ -299,6 +299,17 @@ class NovaWebSocketProxy(websockify.WebSocketProxy):
|
||||||
with the compute node.
|
with the compute node.
|
||||||
"""
|
"""
|
||||||
self.security_proxy = kwargs.pop('security_proxy', None)
|
self.security_proxy = kwargs.pop('security_proxy', None)
|
||||||
|
|
||||||
|
# If 'default' was specified as the ssl_minimum_version, we leave
|
||||||
|
# ssl_options unset to default to the underlying system defaults.
|
||||||
|
# We do this to avoid using websockify's behaviour for 'default'
|
||||||
|
# in select_ssl_version(), which hardcodes the versions to be
|
||||||
|
# quite relaxed and prevents us from using sytem crypto policies.
|
||||||
|
ssl_min_version = kwargs.pop('ssl_minimum_version', None)
|
||||||
|
if ssl_min_version and ssl_min_version != 'default':
|
||||||
|
kwargs['ssl_options'] = websockify.websocketproxy. \
|
||||||
|
select_ssl_version(ssl_min_version)
|
||||||
|
|
||||||
super(NovaWebSocketProxy, self).__init__(*args, **kwargs)
|
super(NovaWebSocketProxy, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -57,17 +57,20 @@ class BaseProxyTestCase(test.NoDBTestCase):
|
||||||
@mock.patch.object(logging, 'setup')
|
@mock.patch.object(logging, 'setup')
|
||||||
@mock.patch.object(gmr.TextGuruMeditation, 'setup_autorun')
|
@mock.patch.object(gmr.TextGuruMeditation, 'setup_autorun')
|
||||||
@mock.patch('nova.console.websocketproxy.NovaWebSocketProxy.__init__',
|
@mock.patch('nova.console.websocketproxy.NovaWebSocketProxy.__init__',
|
||||||
return_value=None)
|
return_value=None)
|
||||||
@mock.patch('nova.console.websocketproxy.NovaWebSocketProxy.start_server')
|
@mock.patch('nova.console.websocketproxy.NovaWebSocketProxy.start_server')
|
||||||
def test_proxy(self, mock_start, mock_init, mock_gmr, mock_log,
|
@mock.patch('websockify.websocketproxy.select_ssl_version',
|
||||||
mock_exists):
|
return_value=None)
|
||||||
|
def test_proxy(self, mock_select_ssl_version, mock_start, mock_init,
|
||||||
|
mock_gmr, mock_log, mock_exists):
|
||||||
baseproxy.proxy('0.0.0.0', '6080')
|
baseproxy.proxy('0.0.0.0', '6080')
|
||||||
mock_log.assert_called_once_with(baseproxy.CONF, 'nova')
|
mock_log.assert_called_once_with(baseproxy.CONF, 'nova')
|
||||||
mock_gmr.assert_called_once_with(version, conf=baseproxy.CONF)
|
mock_gmr.assert_called_once_with(version, conf=baseproxy.CONF)
|
||||||
mock_init.assert_called_once_with(
|
mock_init.assert_called_once_with(
|
||||||
listen_host='0.0.0.0', listen_port='6080', source_is_ipv6=False,
|
listen_host='0.0.0.0', listen_port='6080', source_is_ipv6=False,
|
||||||
cert='self.pem', key=None, ssl_only=False,
|
cert='self.pem', key=None, ssl_only=False, ssl_ciphers=None,
|
||||||
daemon=False, record=None, security_proxy=None, traffic=True,
|
ssl_minimum_version='default', daemon=False, record=None,
|
||||||
|
security_proxy=None, traffic=True,
|
||||||
web='/usr/share/spice-html5', file_only=True,
|
web='/usr/share/spice-html5', file_only=True,
|
||||||
RequestHandlerClass=websocketproxy.NovaProxyRequestHandler)
|
RequestHandlerClass=websocketproxy.NovaProxyRequestHandler)
|
||||||
mock_start.assert_called_once_with()
|
mock_start.assert_called_once_with()
|
||||||
|
@ -81,3 +84,19 @@ class BaseProxyTestCase(test.NoDBTestCase):
|
||||||
self.assertEqual(self.stderr.getvalue(),
|
self.assertEqual(self.stderr.getvalue(),
|
||||||
"SSL only and self.pem not found\n")
|
"SSL only and self.pem not found\n")
|
||||||
mock_exit.assert_called_once_with(-1)
|
mock_exit.assert_called_once_with(-1)
|
||||||
|
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
@mock.patch('nova.console.websocketproxy.NovaWebSocketProxy.__init__',
|
||||||
|
return_value=None)
|
||||||
|
@mock.patch('nova.console.websocketproxy.NovaWebSocketProxy.start_server')
|
||||||
|
def test_proxy_ssl_settings(self, mock_start, mock_init, mock_exists):
|
||||||
|
self.flags(ssl_minimum_version='tlsv1_3', group='console')
|
||||||
|
self.flags(ssl_ciphers='ALL:!aNULL', group='console')
|
||||||
|
baseproxy.proxy('0.0.0.0', '6080')
|
||||||
|
mock_init.assert_called_once_with(
|
||||||
|
listen_host='0.0.0.0', listen_port='6080', source_is_ipv6=False,
|
||||||
|
cert='self.pem', key=None, ssl_only=False,
|
||||||
|
ssl_ciphers='ALL:!aNULL', ssl_minimum_version='tlsv1_3',
|
||||||
|
daemon=False, record=None, security_proxy=None, traffic=True,
|
||||||
|
web='/usr/share/spice-html5', file_only=True,
|
||||||
|
RequestHandlerClass=websocketproxy.NovaProxyRequestHandler)
|
||||||
|
|
|
@ -626,6 +626,22 @@ class NovaProxyRequestHandlerTestCase(test.NoDBTestCase):
|
||||||
self.wh.server.top_new_client(conn, address)
|
self.wh.server.top_new_client(conn, address)
|
||||||
self.assertIsNone(self.wh._compute_rpcapi)
|
self.assertIsNone(self.wh._compute_rpcapi)
|
||||||
|
|
||||||
|
@mock.patch('websockify.websocketproxy.select_ssl_version')
|
||||||
|
def test_ssl_min_version_is_not_set(self, mock_select_ssl):
|
||||||
|
websocketproxy.NovaWebSocketProxy()
|
||||||
|
self.assertFalse(mock_select_ssl.called)
|
||||||
|
|
||||||
|
@mock.patch('websockify.websocketproxy.select_ssl_version')
|
||||||
|
def test_ssl_min_version_not_set_by_default(self, mock_select_ssl):
|
||||||
|
websocketproxy.NovaWebSocketProxy(ssl_minimum_version='default')
|
||||||
|
self.assertFalse(mock_select_ssl.called)
|
||||||
|
|
||||||
|
@mock.patch('websockify.websocketproxy.select_ssl_version')
|
||||||
|
def test_non_default_ssl_min_version_is_set(self, mock_select_ssl):
|
||||||
|
minver = 'tlsv1_3'
|
||||||
|
websocketproxy.NovaWebSocketProxy(ssl_minimum_version=minver)
|
||||||
|
mock_select_ssl.assert_called_once_with(minver)
|
||||||
|
|
||||||
|
|
||||||
class NovaWebsocketSecurityProxyTestCase(test.NoDBTestCase):
|
class NovaWebsocketSecurityProxyTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
other:
|
||||||
|
- |
|
||||||
|
A new pair of ``ssl_ciphers`` and ``ssl_minimum_version`` configuration
|
||||||
|
options have been introduced for use by the ``nova-novncproxy``,
|
||||||
|
``nova-serialproxy``, and ``nova-spicehtml5proxy`` services. These new
|
||||||
|
options allow one to configure the allowed TLS ciphers and minimum protocol
|
||||||
|
version to enforce for incoming client connections to the proxy services.
|
||||||
|
|
||||||
|
This aims to address the issues reported in `bug 1842149`_, where it
|
||||||
|
describes that the proxy services can inherit insecure TLS ciphers
|
||||||
|
and protocol versions from the compiled-in defaults of the OpenSSL
|
||||||
|
library on the underlying system. The proxy services provided no way
|
||||||
|
to override such insecure defaults with current day generally accepted
|
||||||
|
secure TLS settings.
|
||||||
|
|
||||||
|
.. _bug 1842149: https://bugs.launchpad.net/nova/+bug/1842149
|
Loading…
Reference in New Issue