diff options
| author | Jenkins <jenkins@review.openstack.org> | 2013-02-02 16:52:24 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2013-02-02 16:52:24 +0000 |
| commit | 6ce830b0e4e8a9c00d3cfb51f643dec3e1286cee (patch) | |
| tree | d2263f2476ec6b1f465fc2187333cb1a2d8eb78f /openstack | |
| parent | 864e934cfa7003767dabb4f9a51ebe1e493f6027 (diff) | |
| parent | e50b68c0d28cb00fa627525f23bb0c0f614b9312 (diff) | |
| download | oslo-6ce830b0e4e8a9c00d3cfb51f643dec3e1286cee.tar.gz oslo-6ce830b0e4e8a9c00d3cfb51f643dec3e1286cee.tar.xz oslo-6ce830b0e4e8a9c00d3cfb51f643dec3e1286cee.zip | |
Merge "Support for SSL in wsgi.Service"
Diffstat (limited to 'openstack')
| -rw-r--r-- | openstack/common/sslutils.py | 79 | ||||
| -rw-r--r-- | openstack/common/wsgi.py | 65 |
2 files changed, 139 insertions, 5 deletions
diff --git a/openstack/common/sslutils.py b/openstack/common/sslutils.py new file mode 100644 index 0000000..d813e7a --- /dev/null +++ b/openstack/common/sslutils.py @@ -0,0 +1,79 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 IBM +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import ssl + +from openstack.common import cfg +from openstack.common.gettextutils import _ + + +ssl_opts = [ + cfg.StrOpt('ca_file', + default=None, + help="CA certificate file to use to verify " + "connecting clients"), + cfg.StrOpt('cert_file', + default=None, + help="Certificate file to use when starting " + "the server securely"), + cfg.StrOpt('key_file', + default=None, + help="Private key file to use when starting " + "the server securely"), +] + + +CONF = cfg.CONF +CONF.register_opts(ssl_opts, "ssl") + + +def is_enabled(): + cert_file = CONF.ssl.cert_file + key_file = CONF.ssl.key_file + ca_file = CONF.ssl.ca_file + use_ssl = cert_file or key_file + + if cert_file and not os.path.exists(cert_file): + raise RuntimeError(_("Unable to find cert_file : %s") % cert_file) + + if ca_file and not os.path.exists(ca_file): + raise RuntimeError(_("Unable to find ca_file : %s") % ca_file) + + if key_file and not os.path.exists(key_file): + raise RuntimeError(_("Unable to find key_file : %s") % key_file) + + if use_ssl and (not cert_file or not key_file): + raise RuntimeError(_("When running server in SSL mode, you must " + "specify both a cert_file and key_file " + "option value in your configuration file")) + + return use_ssl + + +def wrap(sock): + ssl_kwargs = { + 'server_side': True, + 'certfile': CONF.ssl.cert_file, + 'keyfile': CONF.ssl.key_file, + 'cert_reqs': ssl.CERT_NONE, + } + + if CONF.ssl.ca_file: + ssl_kwargs['ca_certs'] = CONF.ssl.ca_file + ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED + + return ssl.wrap_socket(sock, **ssl_kwargs) diff --git a/openstack/common/wsgi.py b/openstack/common/wsgi.py index f0096d3..550ea4d 100644 --- a/openstack/common/wsgi.py +++ b/openstack/common/wsgi.py @@ -18,6 +18,7 @@ """Utility methods for working with WSGI servers.""" import datetime +import errno import eventlet import eventlet.wsgi @@ -25,18 +26,34 @@ eventlet.patcher.monkey_patch(all=False, socket=True) import routes import routes.middleware +import socket import sys +import time import webob.dec import webob.exc from xml.dom import minidom from xml.parsers import expat +from openstack.common import cfg from openstack.common import exception from openstack.common.gettextutils import _ from openstack.common import jsonutils from openstack.common import log as logging from openstack.common import service +from openstack.common import sslutils +socket_opts = [ + cfg.IntOpt('backlog', + default=4096, + help="Number of backlog requests to configure the socket with"), + cfg.IntOpt('tcp_keepidle', + default=600, + help="Sets the value of TCP_KEEPIDLE in seconds for each " + "server socket. Not supported on OS X."), +] + +CONF = cfg.CONF +CONF.register_opts(socket_opts) LOG = logging.getLogger(__name__) @@ -56,13 +73,46 @@ class Service(service.Service): """ def __init__(self, application, port, - host='0.0.0.0', backlog=128, threads=1000): + host='0.0.0.0', backlog=4096, threads=1000): self.application = application self._port = port self._host = host - self.backlog = backlog + self._backlog = backlog if backlog else CONF.backlog super(Service, self).__init__(threads) + def _get_socket(self, host, port, backlog): + + bind_addr = (host, port) + + sock = None + retry_until = time.time() + 30 + while not sock and time.time() < retry_until: + try: + sock = eventlet.listen(bind_addr, + backlog=backlog) + if sslutils.is_enabled(): + sock = sslutils.wrap(sock) + + except socket.error, err: + if err.args[0] != errno.EADDRINUSE: + raise + eventlet.sleep(0.1) + if not sock: + raise RuntimeError(_("Could not bind to %(host)s:%(port)s " + "after trying for 30 seconds") % + {'host': host, 'port': port}) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + # sockets can hang around forever without keepalive + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + + # This option isn't available in the OS X version of eventlet + if hasattr(socket, 'TCP_KEEPIDLE'): + sock.setsockopt(socket.IPPROTO_TCP, + socket.TCP_KEEPIDLE, + CONF.tcp_keepidle) + + return sock + def start(self): """Start serving this service using the provided server instance. @@ -70,11 +120,14 @@ class Service(service.Service): """ super(Service, self).start() - self._socket = eventlet.listen((self._host, self._port), - backlog=self.backlog) + self._socket = self._get_socket(self._host, self._port, self._backlog) self.tg.add_thread(self._run, self.application, self._socket) @property + def backlog(self): + return self._backlog + + @property def host(self): return self._socket.getsockname()[0] if self._socket else self._host @@ -93,7 +146,9 @@ class Service(service.Service): def _run(self, application, socket): """Start a WSGI server in a new green thread.""" logger = logging.getLogger('eventlet.wsgi') - eventlet.wsgi.server(socket, application, custom_pool=self.tg.pool, + eventlet.wsgi.server(socket, + application, + custom_pool=self.tg.pool, log=logging.WritableLogger(logger)) |
