Support proxying Heat API by using x-forwarded-for header
Adding 'X-Forwarded-For' header support to enable Heat API proxying Implements: blueprint proxying-heat-api Change-Id: I36a0d3dd3f672a485c4a9c0cd541b705033e9025
This commit is contained in:
parent
db7d45f6a4
commit
e3679217d1
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
# heat-api pipeline
|
# heat-api pipeline
|
||||||
[pipeline:heat-api]
|
[pipeline:heat-api]
|
||||||
pipeline = faultwrap ssl versionnegotiation authurl authtoken context apiv1app
|
pipeline = faultwrap ssl xforwardedfor versionnegotiation authurl authtoken context apiv1app
|
||||||
|
|
||||||
# heat-api pipeline for standalone heat
|
# heat-api pipeline for standalone heat
|
||||||
# ie. uses alternative auth backend that authenticates users against keystone
|
# ie. uses alternative auth backend that authenticates users against keystone
|
||||||
@ -12,7 +12,7 @@ pipeline = faultwrap ssl versionnegotiation authurl authtoken context apiv1app
|
|||||||
# flavor = standalone
|
# flavor = standalone
|
||||||
#
|
#
|
||||||
[pipeline:heat-api-standalone]
|
[pipeline:heat-api-standalone]
|
||||||
pipeline = faultwrap ssl versionnegotiation authurl authpassword context apiv1app
|
pipeline = faultwrap ssl xforwardedfor versionnegotiation authurl authpassword context apiv1app
|
||||||
|
|
||||||
# heat-api pipeline for custom cloud backends
|
# heat-api pipeline for custom cloud backends
|
||||||
# i.e. in heat.conf:
|
# i.e. in heat.conf:
|
||||||
@ -20,7 +20,7 @@ pipeline = faultwrap ssl versionnegotiation authurl authpassword context apiv1ap
|
|||||||
# flavor = custombackend
|
# flavor = custombackend
|
||||||
#
|
#
|
||||||
[pipeline:heat-api-custombackend]
|
[pipeline:heat-api-custombackend]
|
||||||
pipeline = faultwrap versionnegotiation context custombackendauth apiv1app
|
pipeline = faultwrap xforwardedfor versionnegotiation context custombackendauth apiv1app
|
||||||
|
|
||||||
# heat-api-cfn pipeline
|
# heat-api-cfn pipeline
|
||||||
[pipeline:heat-api-cfn]
|
[pipeline:heat-api-cfn]
|
||||||
@ -33,12 +33,12 @@ pipeline = cfnversionnegotiation ec2authtoken context apicfnv1app
|
|||||||
|
|
||||||
# heat-api-cloudwatch pipeline
|
# heat-api-cloudwatch pipeline
|
||||||
[pipeline:heat-api-cloudwatch]
|
[pipeline:heat-api-cloudwatch]
|
||||||
pipeline = versionnegotiation ec2authtoken authtoken context apicwapp
|
pipeline = xforwardedfor versionnegotiation ec2authtoken authtoken context apicwapp
|
||||||
|
|
||||||
# heat-api-cloudwatch pipeline for standalone heat
|
# heat-api-cloudwatch pipeline for standalone heat
|
||||||
# relies exclusively on authenticating with ec2 signed requests
|
# relies exclusively on authenticating with ec2 signed requests
|
||||||
[pipeline:heat-api-cloudwatch-standalone]
|
[pipeline:heat-api-cloudwatch-standalone]
|
||||||
pipeline = versionnegotiation ec2authtoken context apicwapp
|
pipeline = xforwardedfor versionnegotiation ec2authtoken context apicwapp
|
||||||
|
|
||||||
[app:apiv1app]
|
[app:apiv1app]
|
||||||
paste.app_factory = heat.common.wsgi:app_factory
|
paste.app_factory = heat.common.wsgi:app_factory
|
||||||
@ -52,6 +52,10 @@ heat.app_factory = heat.api.cfn.v1:API
|
|||||||
paste.app_factory = heat.common.wsgi:app_factory
|
paste.app_factory = heat.common.wsgi:app_factory
|
||||||
heat.app_factory = heat.api.cloudwatch:API
|
heat.app_factory = heat.api.cloudwatch:API
|
||||||
|
|
||||||
|
[filter:xforwardedfor]
|
||||||
|
paste.filter_factory = heat.common.wsgi:filter_factory
|
||||||
|
heat.filter_factory = heat.api.openstack:x_forwarded_for_middleware_filter
|
||||||
|
|
||||||
[filter:versionnegotiation]
|
[filter:versionnegotiation]
|
||||||
paste.filter_factory = heat.common.wsgi:filter_factory
|
paste.filter_factory = heat.common.wsgi:filter_factory
|
||||||
heat.filter_factory = heat.api.openstack:version_negotiation_filter
|
heat.filter_factory = heat.api.openstack:version_negotiation_filter
|
||||||
|
@ -10,6 +10,15 @@
|
|||||||
#secure_proxy_ssl_header=X-Forwarded-Proto
|
#secure_proxy_ssl_header=X-Forwarded-Proto
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options defined in heat.api.middleware.x_forwarded_for
|
||||||
|
#
|
||||||
|
|
||||||
|
# The HTTP header that will be used as remote address. (string
|
||||||
|
# value)
|
||||||
|
#forward_header_name=X-Forwarded-For
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Options defined in heat.common.config
|
# Options defined in heat.common.config
|
||||||
#
|
#
|
||||||
|
35
heat/api/middleware/x_forwarded_for.py
Normal file
35
heat/api/middleware/x_forwarded_for.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
from heat.common import wsgi
|
||||||
|
|
||||||
|
x_forwarded_middleware_opts = [
|
||||||
|
cfg.StrOpt('forward_header_name',
|
||||||
|
default='X-Forwarded-For',
|
||||||
|
help="The HTTP header that will be used as remote address.")
|
||||||
|
]
|
||||||
|
cfg.CONF.register_opts(x_forwarded_middleware_opts)
|
||||||
|
|
||||||
|
|
||||||
|
class XForwardedForMiddleware(wsgi.Middleware):
|
||||||
|
"""A middleware that replaces the request hostname with proxy hostname.
|
||||||
|
"""
|
||||||
|
def __init__(self, application):
|
||||||
|
super(XForwardedForMiddleware, self).__init__(application)
|
||||||
|
|
||||||
|
def process_request(self, req):
|
||||||
|
# If 'forward_header_name' header was not found, then do not
|
||||||
|
# change the host name
|
||||||
|
req.host = req.headers.get(cfg.CONF.forward_header_name, req.host)
|
@ -14,6 +14,7 @@
|
|||||||
from heat.api.middleware.fault import FaultWrapper
|
from heat.api.middleware.fault import FaultWrapper
|
||||||
from heat.api.middleware.ssl import SSLMiddleware
|
from heat.api.middleware.ssl import SSLMiddleware
|
||||||
from heat.api.middleware.version_negotiation import VersionNegotiationFilter
|
from heat.api.middleware.version_negotiation import VersionNegotiationFilter
|
||||||
|
from heat.api.middleware.x_forwarded_for import XForwardedForMiddleware
|
||||||
from heat.api.openstack import versions
|
from heat.api.openstack import versions
|
||||||
|
|
||||||
|
|
||||||
@ -28,3 +29,7 @@ def faultwrap_filter(app, conf, **local_conf):
|
|||||||
|
|
||||||
def sslmiddleware_filter(app, conf, **local_conf):
|
def sslmiddleware_filter(app, conf, **local_conf):
|
||||||
return SSLMiddleware(app)
|
return SSLMiddleware(app)
|
||||||
|
|
||||||
|
|
||||||
|
def x_forwarded_for_middleware_filter(app, conf, **local_conf):
|
||||||
|
return XForwardedForMiddleware(app)
|
||||||
|
53
heat/tests/test_x_forwarded_for_middleware.py
Normal file
53
heat/tests/test_x_forwarded_for_middleware.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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 webob
|
||||||
|
|
||||||
|
from heat.api.middleware import x_forwarded_for
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from heat.tests.common import HeatTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class XForwardedForMiddlewareTest(HeatTestCase):
|
||||||
|
|
||||||
|
def test_default_header_found(self):
|
||||||
|
headers = {'X-Forwarded-For': 'www.test.com'}
|
||||||
|
middleware = x_forwarded_for.XForwardedForMiddleware(None)
|
||||||
|
request = webob.Request.blank('/stacks', headers=headers)
|
||||||
|
self.assertIsNone(middleware.process_request(request))
|
||||||
|
self.assertEqual('www.test.com', request.host)
|
||||||
|
|
||||||
|
def test_custom_header_found(self):
|
||||||
|
cfg.CONF.set_override('forward_header_name', 'X-Fwd-Custom')
|
||||||
|
headers = {'X-Fwd-Custom': 'www.test.com'}
|
||||||
|
middleware = x_forwarded_for.XForwardedForMiddleware(None)
|
||||||
|
request = webob.Request.blank('/stacks', headers=headers)
|
||||||
|
self.assertIsNone(middleware.process_request(request))
|
||||||
|
self.assertEqual('www.test.com', request.host)
|
||||||
|
|
||||||
|
def test_default_header_not_found(self):
|
||||||
|
headers = {}
|
||||||
|
middleware = x_forwarded_for.XForwardedForMiddleware(None)
|
||||||
|
request = webob.Request.blank('/stacks', headers=headers)
|
||||||
|
self.assertIsNone(middleware.process_request(request))
|
||||||
|
self.assertEqual('localhost:80', request.host)
|
||||||
|
|
||||||
|
def test_custom_header_not_found(self):
|
||||||
|
cfg.CONF.set_override('forward_header_name', 'X-Fwd-Custom')
|
||||||
|
headers = {}
|
||||||
|
middleware = x_forwarded_for.XForwardedForMiddleware(None)
|
||||||
|
request = webob.Request.blank('/stacks', headers=headers)
|
||||||
|
self.assertIsNone(middleware.process_request(request))
|
||||||
|
self.assertEqual('localhost:80', request.host)
|
Loading…
Reference in New Issue
Block a user