Merge "If listen_tls is true, enable TLS on wsgi server"

This commit is contained in:
Zuul
2020-09-03 18:59:48 +00:00
committed by Gerrit Code Review
5 changed files with 97 additions and 13 deletions

View File

@@ -12,8 +12,16 @@
import os
import eventlet
# NOTE(TheJulia): Eventlet, when monkey patching occurs, replaces the base
# dns resolver methods. This can lead to compatability issues,
# and un-expected exceptions being raised during the process
# of monkey patching. Such as one if there are no resolvers.
os.environ['EVENTLET_NO_GREENDNS'] = "yes"
# NOTE(JayF) Without monkey_patching socket, API requests will hang with TLS
# enabled. Enabling more than just socket for monkey patching causes failures
# in image streaming. In an ideal world, we track down all those errors and
# monkey patch everything as suggested in eventlet documentation.
eventlet.monkey_patch(all=False, socket=True)

View File

@@ -130,7 +130,8 @@ class Application(object):
"""Start the API service in the background."""
self.service = wsgi.Server(self._conf, 'ironic-python-agent', app=self,
host=self.agent.listen_address.hostname,
port=self.agent.listen_address.port)
port=self.agent.listen_address.port,
use_ssl=self._conf.listen_tls)
self.service.start()
LOG.info('Started API service on port %s',
self.agent.listen_address.port)

View File

@@ -54,6 +54,18 @@ cli_opts = [
help='The port to listen on. '
'Can be supplied as "ipa-listen-port" kernel parameter.'),
# This is intentionally not settable via kernel command line, as it
# requires configuration parameters from oslo_service which are not
# configurable over the command line and require files-on-disk.
# Operators who want to use this support should configure it statically
# as part of a ramdisk build.
cfg.BoolOpt('listen_tls',
default=False,
help='When true, IPA will host API behind TLS. You will also '
'need to configure [ssl] group options for cert_file, '
'key_file, and, if desired, ca_file to validate client '
'certificates.'),
cfg.StrOpt('advertise_host',
default=APARAMS.get('ipa-advertise-host', None),
help='The host to tell Ironic to reply and send '

View File

@@ -208,7 +208,51 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
mock.call('wait_for_disks')],
mock_dispatch.call_args_list)
self.agent.heartbeater.start.assert_called_once_with()
@mock.patch(
'ironic_python_agent.hardware_managers.cna._detect_cna_card',
mock.Mock())
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(agent.IronicPythonAgent,
'_wait_for_interface', autospec=True)
@mock.patch('oslo_service.wsgi.Server', autospec=True)
@mock.patch.object(hardware, 'get_managers', autospec=True)
def test_run_with_ssl(self, mock_get_managers, mock_wsgi,
mock_wait, mock_dispatch):
CONF.set_override('inspection_callback_url', '')
CONF.set_override('listen_tls', True)
wsgi_server = mock_wsgi.return_value
def set_serve_api():
self.agent.serve_api = False
wsgi_server.start.side_effect = set_serve_api
self.agent.heartbeater = mock.Mock()
self.agent.api_client.lookup_node = mock.Mock()
self.agent.api_client.lookup_node.return_value = {
'node': {
'uuid': 'deadbeef-dabb-ad00-b105-f00d00bab10c'
},
'config': {
'heartbeat_timeout': 300
}
}
self.agent.run()
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999,
use_ssl=True)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
@@ -262,7 +306,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
@@ -320,7 +365,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
@@ -365,7 +411,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
@@ -412,7 +459,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host='2001:db8:dead:beef::cafe',
port=9998)
port=9998,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),
@@ -455,7 +503,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_dispatch.call_args_list)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.heartbeater.start.assert_called_once_with()
@@ -494,7 +543,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_inspector.assert_called_once_with()
@@ -557,7 +607,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_inspector.assert_called_once_with()
@@ -613,7 +664,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.assertFalse(mock_inspector.called)
@@ -674,7 +726,8 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
self.agent.heartbeater.start.assert_called_once_with()
@@ -827,7 +880,8 @@ class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
self.assertTrue(mock_get_managers.called)
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server_request.start.assert_called_once_with()
self.assertFalse(self.agent.heartbeater.called)
@@ -1051,7 +1105,8 @@ class TestBaseAgentVMediaToken(ironic_agent_base.IronicAgentTest):
mock_wsgi.assert_called_once_with(CONF, 'ironic-python-agent',
app=self.agent.api,
host=mock.ANY, port=9999)
host=mock.ANY, port=9999,
use_ssl=False)
wsgi_server.start.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
self.assertEqual([mock.call('list_hardware_info'),

View File

@@ -0,0 +1,8 @@
---
features:
- |
Enables support in IPA for hosting the API server over TLS. Using this
support requires setting ``[DEFAULT]listen_tls`` to True, and then setting
``[ssl]cert_file``, ``[ssl]key_file``, and optionally ``[ssl]ca_file`` to
files embedded in the ramdisk IPA runs inside.