summaryrefslogtreecommitdiffstats
path: root/openstack
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-02-02 16:52:24 +0000
committerGerrit Code Review <review@openstack.org>2013-02-02 16:52:24 +0000
commit6ce830b0e4e8a9c00d3cfb51f643dec3e1286cee (patch)
treed2263f2476ec6b1f465fc2187333cb1a2d8eb78f /openstack
parent864e934cfa7003767dabb4f9a51ebe1e493f6027 (diff)
parente50b68c0d28cb00fa627525f23bb0c0f614b9312 (diff)
downloadoslo-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.py79
-rw-r--r--openstack/common/wsgi.py65
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))