From 564f5f7cd7d4771a48b53ec4d8c626650f3da4da Mon Sep 17 00:00:00 2001 From: Anton Arefiev Date: Thu, 29 Oct 2015 13:00:51 +0200 Subject: [PATCH] Add SSL support to the Ironic API Add posibility to configure the API to service requests via HTTPS instead of HTTP using native ssl from oslo.service wsgi. New options was added: * enable_ssl_api - turn on ssl support; Options defined in oslo.service for configure certs: * ca_file - ca certificate file to use to verify connecting clients; * cert_file - certificate file to use when starting the server securely; * key_file - private key file to use when starting the server securely; Closes-bug: #1430213 Change-Id: Id4b84d83f9aa6c7f898b3b9b59158d5b1a00e159 --- etc/ironic/ironic.conf.sample | 29 ++++++++++++++++++++++++ ironic/api/__init__.py | 8 +++++++ ironic/cmd/api.py | 2 +- ironic/common/service.py | 6 +++-- ironic/tests/unit/common/test_service.py | 13 +++++++++++ tools/config/oslo.config.generator.rc | 2 +- 6 files changed, 56 insertions(+), 4 deletions(-) diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index c2d0b93e63..435b802e3b 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -504,6 +504,14 @@ # (integer value) #api_workers= +# Enable the integrated stand-alone API to service requests +# via HTTPS instead of HTTP. If there is a front-end service +# performing HTTPS offloading from the service, this option +# should be False; note, you will want to change public API +# endpoint to represent SSL termination URL with +# 'public_endpoint' option. (boolean value) +#enable_ssl_api=false + [cimc] @@ -1897,6 +1905,27 @@ #get_vm_name_retry_interval=3 +[ssl] + +# +# Options defined in oslo.service.sslutils +# + +# CA certificate file to use to verify connecting clients. +# (string value) +#ca_file= + +# Certificate file to use when starting the server securely. +# (string value) +#cert_file= + +# Private key file to use when starting the server securely. +# (string value) +#key_file= + + + + [swift] # diff --git a/ironic/api/__init__.py b/ironic/api/__init__.py index aef280261a..71277b2814 100644 --- a/ironic/api/__init__.py +++ b/ironic/api/__init__.py @@ -41,6 +41,14 @@ API_SERVICE_OPTS = [ 'The default is equal to the number of CPUs available ' 'if that can be determined, else a default worker ' 'count of 1 is returned.')), + cfg.BoolOpt('enable_ssl_api', + default=False, + help=_("Enable the integrated stand-alone API to service " + "requests via HTTPS instead of HTTP. If there is a " + "front-end service performing HTTPS offloading from " + "the service, this option should be False; note, you " + "will want to change public API endpoint to represent " + "SSL termination URL with 'public_endpoint' option.")), ] CONF = cfg.CONF diff --git a/ironic/cmd/api.py b/ironic/cmd/api.py index a942ca2d3a..c205a1566b 100644 --- a/ironic/cmd/api.py +++ b/ironic/cmd/api.py @@ -36,7 +36,7 @@ def main(): # Build and start the WSGI app launcher = ironic_service.process_launcher() - server = ironic_service.WSGIService('ironic_api') + server = ironic_service.WSGIService('ironic_api', CONF.api.enable_ssl_api) launcher.launch_service(server, workers=server.workers) launcher.wait() diff --git a/ironic/common/service.py b/ironic/common/service.py index 982a8d3a35..e76c4e5b13 100644 --- a/ironic/common/service.py +++ b/ironic/common/service.py @@ -150,10 +150,11 @@ def process_launcher(): class WSGIService(service.ServiceBase): """Provides ability to launch ironic API from wsgi app.""" - def __init__(self, name): + def __init__(self, name, use_ssl=False): """Initialize, but do not start the WSGI server. :param name: The name of the WSGI server given to the loader. + :param use_ssl: Wraps the socket in an SSL context if True. :returns: None """ self.name = name @@ -167,7 +168,8 @@ class WSGIService(service.ServiceBase): self.server = wsgi.Server(CONF, name, self.app, host=CONF.api.host_ip, - port=CONF.api.port) + port=CONF.api.port, + use_ssl=use_ssl) def start(self): """Start serving this service using loaded configuration. diff --git a/ironic/tests/unit/common/test_service.py b/ironic/tests/unit/common/test_service.py index b7ae29fa36..51aaa0b6c5 100644 --- a/ironic/tests/unit/common/test_service.py +++ b/ironic/tests/unit/common/test_service.py @@ -12,11 +12,14 @@ import mock from oslo_concurrency import processutils +from oslo_config import cfg from ironic.common import exception from ironic.common import service from ironic.tests import base +CONF = cfg.CONF + class TestWSGIService(base.TestCase): @@ -60,3 +63,13 @@ class TestWSGIService(base.TestCase): service.WSGIService, 'ironic_api') self.assertFalse(wsgi_server.called) + + @mock.patch.object(service.wsgi, 'Server') + def test_wsgi_service_with_ssl_enabled(self, wsgi_server): + self.config(enable_ssl_api=True, group='api') + srv = service.WSGIService('ironic_api', CONF.api.enable_ssl_api) + wsgi_server.assert_called_once_with(CONF, 'ironic_api', + srv.app, + host='0.0.0.0', + port=6385, + use_ssl=True) diff --git a/tools/config/oslo.config.generator.rc b/tools/config/oslo.config.generator.rc index 2b977e866d..e66acd5ac7 100644 --- a/tools/config/oslo.config.generator.rc +++ b/tools/config/oslo.config.generator.rc @@ -1,2 +1,2 @@ -export IRONIC_CONFIG_GENERATOR_EXTRA_LIBRARIES='oslo.db oslo.messaging oslo.middleware.cors keystonemiddleware.auth_token oslo.concurrency oslo.policy oslo.log oslo.service.service oslo.service.periodic_task' +export IRONIC_CONFIG_GENERATOR_EXTRA_LIBRARIES='oslo.db oslo.messaging oslo.middleware.cors keystonemiddleware.auth_token oslo.concurrency oslo.policy oslo.log oslo.service.service oslo.service.periodic_task oslo.service.sslutils' export IRONIC_CONFIG_GENERATOR_EXTRA_MODULES=