I recently needed to deploy a REST-based web service for use as a DynamicJSON vendordata provider to OpenStack’s nova metadata service. I settled on Oslo’s WSGI server, mainly due to the fact that the Oslo project’s goal is to provide standard libraries for all OpenStack projects. Plus it made incorporating authentication via Keystone middleware that much easier.
Given the requirement for TLS-everywhere, the next step was to enable SSL encryption of the service. This wasn’t as simple a task as I had hoped.
The Oslo WSGI service is very lightly documented, and while there are documented configuration options for Oslo service’s sslutils library, there is no documentation on how to actually enable it. So in to the source code I went.
Turns out that oslo_service.wsgi.Server accepts a boolean parameter named use_ssl.
self.server = wsgi.Server(CONF, name, self.app, host=self.host, port=self.port, use_ssl=True)
When coupled with the documented sslutils configuration file requirements, our service starts listening over HTTPS. Simple.
[ssl] cert_file = /etc/pki/tls/certs/vendordata.crt key_file = /etc/pki/tls/private/vendordata.key
Until of course you try to actually connect to it…
Traceback (most recent call last): File "/usr/lib/python2.7/site-packages/eventlet/hubs/poll.py", line 115, in wait listener.cb(fileno) File "/usr/lib/python2.7/site-packages/eventlet/greenthread.py", line 214, in main result = function(*args, **kwargs) File "/usr/lib/python2.7/site-packages/eventlet/wsgi.py", line 880, in server client_socket = sock.accept() File "/usr/lib64/python2.7/ssl.py", line 884, in accept server_side=True) File "/usr/lib64/python2.7/ssl.py", line 348, in wrap_socket _context=self) File "/usr/lib64/python2.7/ssl.py", line 603, in __init__ server_hostname, ssl_sock=self) TypeError: _wrap_socket() argument 1 must be _socket.socket, not GreenSocket Removing descriptor: 7
Enter eventlet
The Oslo WSGI service relies on several different eventlet libraries for WSGI and SSL utilities. Eventlet is a very efficient way of performing several network-related tasks, however some operations are not compatible with the built-in networking libraries. Eventlet’s solution is to patch these libraries on the fly, known as “monkey patching”. This is not enforced however, as changing the behaviour of another Python module (especially a built-in) is not ideal and, if needed, should be very explicitly requested.
Now for whatever reason (I can only assume it is the same reason), the Oslo service also does not enforce Eventlet’s requirement to monkey patch when wrapping SSL sockets. So, before initiating the WSGI server we need to invoke Eventlet’s patching module.
import eventlet eventlet.monkey_patch()
This is a little time-consuming (anecdotally it is adding around 30 seconds to server start), so if speed is a factor it is possible to limit which modules are patched.
After restarting the service everything is up and running, encrypted, and working as expected.