Handle SSL termination proxies for version list
Return correct scheme in version URLs if service behind an SSL termination proxy. This is done by adding a new configuration option, secure_proxy_ssl_header, which, when defined, makes the wsgi application take the host_url scheme from that header. By default, when this option is not specified, there is no difference in behavior. The intention is to configure any ssl-decrypting proxy to set that header, so that nova-api knows which protocol to use in the URLs in response. This patch is largely based on https://review.openstack.org/#/c/132235/18 DocImpact Closes-Bug: #1384379 Change-Id: I27ba166902ecc19c9b7fff2ee7f3bf733885efe1
This commit is contained in:
parent
4a96b90623
commit
ab35779238
|
@ -83,7 +83,7 @@ def get_media_map():
|
|||
return dict(_MEDIA_TYPE_MAP.items())
|
||||
|
||||
|
||||
class Request(webob.Request):
|
||||
class Request(wsgi.Request):
|
||||
"""Add some OpenStack API-specific logic to the base webob.Request."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -23,6 +23,7 @@ from nova.api.openstack.compute import views
|
|||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit import matchers
|
||||
from nova import wsgi
|
||||
|
||||
|
||||
NS = {
|
||||
|
@ -89,6 +90,15 @@ EXP_VERSIONS = {
|
|||
}
|
||||
|
||||
|
||||
def _get_self_href(response):
|
||||
"""Extract the URL to self from response data."""
|
||||
data = jsonutils.loads(response.body)
|
||||
for link in data['versions'][0]['links']:
|
||||
if link['rel'] == 'self':
|
||||
return link['href']
|
||||
return ''
|
||||
|
||||
|
||||
class VersionsTestV20(test.NoDBTestCase):
|
||||
|
||||
def test_get_version_list(self):
|
||||
|
@ -422,3 +432,26 @@ class VersionsTestV21(test.NoDBTestCase):
|
|||
version = jsonutils.loads(res.body)
|
||||
expected = {"version": self.exp_versions['v2.1']}
|
||||
self.assertEqual(expected, version)
|
||||
|
||||
|
||||
class VersionBehindSslTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(VersionBehindSslTestCase, self).setUp()
|
||||
self.flags(secure_proxy_ssl_header='HTTP_X_FORWARDED_PROTO')
|
||||
|
||||
def test_versions_without_headers(self):
|
||||
req = wsgi.Request.blank('/')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
href = _get_self_href(res)
|
||||
self.assertTrue(href.startswith('http://'))
|
||||
|
||||
def test_versions_with_header(self):
|
||||
req = wsgi.Request.blank('/')
|
||||
req.accept = "application/json"
|
||||
req.headers['X-Forwarded-Proto'] = 'https'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
href = _get_self_href(res)
|
||||
self.assertTrue(href.startswith('https://'))
|
||||
|
|
12
nova/wsgi.py
12
nova/wsgi.py
|
@ -51,6 +51,11 @@ wsgi_opts = [
|
|||
'generate log lines. The following values can be formatted '
|
||||
'into it: client_ip, date_time, request_line, status_code, '
|
||||
'body_length, wall_seconds.'),
|
||||
cfg.StrOpt('secure_proxy_ssl_header',
|
||||
help='The HTTP header used to determine the scheme for the '
|
||||
'original request, even if it was removed by an SSL '
|
||||
'terminating proxy. Typical value is '
|
||||
'"HTTP_X_FORWARDED_PROTO".'),
|
||||
cfg.StrOpt('ssl_ca_file',
|
||||
help="CA certificate file to use to verify "
|
||||
"connecting clients"),
|
||||
|
@ -274,7 +279,12 @@ class Server(service.ServiceBase):
|
|||
|
||||
|
||||
class Request(webob.Request):
|
||||
pass
|
||||
def __init__(self, environ, *args, **kwargs):
|
||||
if CONF.secure_proxy_ssl_header:
|
||||
scheme = environ.get(CONF.secure_proxy_ssl_header)
|
||||
if scheme:
|
||||
environ['wsgi.url_scheme'] = scheme
|
||||
super(Request, self).__init__(environ, *args, **kwargs)
|
||||
|
||||
|
||||
class Application(object):
|
||||
|
|
Loading…
Reference in New Issue