Merge "Add SSL/TLS Support"
This commit is contained in:
commit
be9d533280
@ -43,6 +43,15 @@
|
|||||||
# Deprecated group/name - [discoverd]/clean_up_period
|
# Deprecated group/name - [discoverd]/clean_up_period
|
||||||
#clean_up_period = 60
|
#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]
|
[firewall]
|
||||||
|
|
||||||
|
@ -169,6 +169,15 @@ SERVICE_OPTS = [
|
|||||||
help='Amount of time in seconds, after which repeat clean up '
|
help='Amount of time in seconds, after which repeat clean up '
|
||||||
'of timed out nodes and old nodes status information.',
|
'of timed out nodes and old nodes status information.',
|
||||||
deprecated_group='discoverd'),
|
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 functools
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
@ -168,6 +169,37 @@ def init():
|
|||||||
LOG.warning(_LW('Timeout is disabled in configuration'))
|
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
|
def main(args=sys.argv[1:]): # pragma: no cover
|
||||||
CONF(args, project='ironic-inspector')
|
CONF(args, project='ironic-inspector')
|
||||||
debug = CONF.debug
|
debug = CONF.debug
|
||||||
@ -180,10 +212,16 @@ def main(args=sys.argv[1:]): # pragma: no cover
|
|||||||
logging.getLogger('ironicclient.common.http').setLevel(
|
logging.getLogger('ironicclient.common.http').setLevel(
|
||||||
logging.INFO if debug else logging.ERROR)
|
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()
|
init()
|
||||||
try:
|
try:
|
||||||
app.run(debug=debug,
|
app.run(**app_kwargs)
|
||||||
host=CONF.listen_address,
|
|
||||||
port=CONF.listen_port)
|
|
||||||
finally:
|
finally:
|
||||||
firewall.clean_up()
|
firewall.clean_up()
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
@ -242,3 +244,86 @@ class TestInit(test_base.BaseTest):
|
|||||||
|
|
||||||
self.assertRaises(SystemExit, main.init)
|
self.assertRaises(SystemExit, main.init)
|
||||||
mock_log.assert_called_once_with(mock.ANY, "'foo!'")
|
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…
x
Reference in New Issue
Block a user