Merge "Add SSL/TLS Support"
This commit is contained in:
commit
be9d533280
@ -43,6 +43,15 @@
|
||||
# Deprecated group/name - [discoverd]/clean_up_period
|
||||
#clean_up_period = 60
|
||||
|
||||
# SSL Enabled/Disabled (boolean value)
|
||||
#use_ssl = false
|
||||
|
||||
# Path to SSL certificate (string value)
|
||||
#ssl_cert_path =
|
||||
|
||||
# Path to SSL key (string value)
|
||||
#ssl_key_path =
|
||||
|
||||
|
||||
[firewall]
|
||||
|
||||
|
@ -169,6 +169,15 @@ SERVICE_OPTS = [
|
||||
help='Amount of time in seconds, after which repeat clean up '
|
||||
'of timed out nodes and old nodes status information.',
|
||||
deprecated_group='discoverd'),
|
||||
cfg.BoolOpt('use_ssl',
|
||||
default=False,
|
||||
help='SSL Enabled/Disabled'),
|
||||
cfg.StrOpt('ssl_cert_path',
|
||||
default='',
|
||||
help='Path to SSL certificate'),
|
||||
cfg.StrOpt('ssl_key_path',
|
||||
default='',
|
||||
help='Path to SSL key'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ eventlet.monkey_patch()
|
||||
import functools
|
||||
import json
|
||||
import logging
|
||||
import ssl
|
||||
import sys
|
||||
|
||||
import flask
|
||||
@ -168,6 +169,37 @@ def init():
|
||||
LOG.warning(_LW('Timeout is disabled in configuration'))
|
||||
|
||||
|
||||
def create_ssl_context():
|
||||
if not CONF.use_ssl:
|
||||
return
|
||||
|
||||
MIN_VERSION = (2, 7, 9)
|
||||
|
||||
if sys.version_info < MIN_VERSION:
|
||||
LOG.warning(_LW('Unable to use SSL in this version of Python: '
|
||||
'%{current}, please ensure your version of Python is '
|
||||
'greater than %{min} to enable this feature.'),
|
||||
{'current': '.'.join(map(str, sys.version_info[:3])),
|
||||
'min': '.'.join(map(str, MIN_VERSION))})
|
||||
return
|
||||
|
||||
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
||||
if CONF.ssl_cert_path and CONF.ssl_key_path:
|
||||
try:
|
||||
context.load_cert_chain(CONF.ssl_cert_path, CONF.ssl_key_path)
|
||||
except IOError as exc:
|
||||
LOG.warning(_LW('Failed to load certificate or key from defined '
|
||||
'locations: %{cert} and %{key}, will continue to '
|
||||
'run with the default settings: %{exc}'),
|
||||
{'cert': CONF.ssl_cert_path, 'key': CONF.ssl_key_path,
|
||||
'exc': exc})
|
||||
except ssl.SSLError as exc:
|
||||
LOG.warning(_LW('There was a problem with the loaded certificate '
|
||||
'and key, will continue to run with the default '
|
||||
'settings: %s'), exc)
|
||||
return context
|
||||
|
||||
|
||||
def main(args=sys.argv[1:]): # pragma: no cover
|
||||
CONF(args, project='ironic-inspector')
|
||||
debug = CONF.debug
|
||||
@ -180,10 +212,16 @@ def main(args=sys.argv[1:]): # pragma: no cover
|
||||
logging.getLogger('ironicclient.common.http').setLevel(
|
||||
logging.INFO if debug else logging.ERROR)
|
||||
|
||||
app_kwargs = {'debug': debug,
|
||||
'host': CONF.listen_address,
|
||||
'port': CONF.listen_port}
|
||||
|
||||
context = create_ssl_context()
|
||||
if context:
|
||||
app_kwargs['ssl_context'] = context
|
||||
|
||||
init()
|
||||
try:
|
||||
app.run(debug=debug,
|
||||
host=CONF.listen_address,
|
||||
port=CONF.listen_port)
|
||||
app.run(**app_kwargs)
|
||||
finally:
|
||||
firewall.clean_up()
|
||||
|
@ -12,6 +12,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import ssl
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import eventlet
|
||||
@ -242,3 +244,86 @@ class TestInit(test_base.BaseTest):
|
||||
|
||||
self.assertRaises(SystemExit, main.init)
|
||||
mock_log.assert_called_once_with(mock.ANY, "'foo!'")
|
||||
|
||||
|
||||
class TestCreateSSLContext(test_base.BaseTest):
|
||||
|
||||
def test_use_ssl_false(self):
|
||||
CONF.set_override('use_ssl', False)
|
||||
con = main.create_ssl_context()
|
||||
self.assertIsNone(con)
|
||||
|
||||
@mock.patch.object(sys, 'version_info')
|
||||
def test_old_python_returns_none(self, mock_version_info):
|
||||
mock_version_info.__lt__.return_value = True
|
||||
CONF.set_override('use_ssl', True)
|
||||
con = main.create_ssl_context()
|
||||
self.assertIsNone(con)
|
||||
|
||||
@unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
|
||||
'This feature is unsupported in this version of python '
|
||||
'so the tests will be skipped')
|
||||
@mock.patch.object(ssl, 'create_default_context', autospec=True)
|
||||
def test_use_ssl_true(self, mock_cdc):
|
||||
CONF.set_override('use_ssl', True)
|
||||
m_con = mock_cdc()
|
||||
con = main.create_ssl_context()
|
||||
self.assertEqual(m_con, con)
|
||||
|
||||
@unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
|
||||
'This feature is unsupported in this version of python '
|
||||
'so the tests will be skipped')
|
||||
@mock.patch.object(ssl, 'create_default_context', autospec=True)
|
||||
def test_only_key_path_provided(self, mock_cdc):
|
||||
CONF.set_override('use_ssl', True)
|
||||
CONF.set_override('ssl_key_path', '/some/fake/path')
|
||||
mock_context = mock_cdc()
|
||||
con = main.create_ssl_context()
|
||||
self.assertEqual(mock_context, con)
|
||||
self.assertFalse(mock_context.load_cert_chain.called)
|
||||
|
||||
@unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
|
||||
'This feature is unsupported in this version of python '
|
||||
'so the tests will be skipped')
|
||||
@mock.patch.object(ssl, 'create_default_context', autospec=True)
|
||||
def test_only_cert_path_provided(self, mock_cdc):
|
||||
CONF.set_override('use_ssl', True)
|
||||
CONF.set_override('ssl_cert_path', '/some/fake/path')
|
||||
mock_context = mock_cdc()
|
||||
con = main.create_ssl_context()
|
||||
self.assertEqual(mock_context, con)
|
||||
self.assertFalse(mock_context.load_cert_chain.called)
|
||||
|
||||
@unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
|
||||
'This feature is unsupported in this version of python '
|
||||
'so the tests will be skipped')
|
||||
@mock.patch.object(ssl, 'create_default_context', autospec=True)
|
||||
def test_both_paths_provided(self, mock_cdc):
|
||||
key_path = '/some/fake/path/key'
|
||||
cert_path = '/some/fake/path/cert'
|
||||
CONF.set_override('use_ssl', True)
|
||||
CONF.set_override('ssl_key_path', key_path)
|
||||
CONF.set_override('ssl_cert_path', cert_path)
|
||||
mock_context = mock_cdc()
|
||||
con = main.create_ssl_context()
|
||||
self.assertEqual(mock_context, con)
|
||||
mock_context.load_cert_chain.assert_called_once_with(cert_path,
|
||||
key_path)
|
||||
|
||||
@unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
|
||||
'This feature is unsupported in this version of python '
|
||||
'so the tests will be skipped')
|
||||
@mock.patch.object(ssl, 'create_default_context', autospec=True)
|
||||
def test_load_cert_chain_fails(self, mock_cdc):
|
||||
CONF.set_override('use_ssl', True)
|
||||
key_path = '/some/fake/path/key'
|
||||
cert_path = '/some/fake/path/cert'
|
||||
CONF.set_override('use_ssl', True)
|
||||
CONF.set_override('ssl_key_path', key_path)
|
||||
CONF.set_override('ssl_cert_path', cert_path)
|
||||
mock_context = mock_cdc()
|
||||
mock_context.load_cert_chain.side_effect = IOError('Boom!')
|
||||
con = main.create_ssl_context()
|
||||
self.assertEqual(mock_context, con)
|
||||
mock_context.load_cert_chain.assert_called_once_with(cert_path,
|
||||
key_path)
|
||||
|
Loading…
Reference in New Issue
Block a user