d6fcf74594
Why we need this: Some middlewares want to keep HTTP Location header as relative path (e.g. using Load balancer in front of proxy). What is the problem in current Swift: Current Swift already has the flag to keep it as relative when returning the reponse using swift.common.swob.Response. However, auth_token middleware, that is from keystonemiddleware, unfortunately can change the relative path to absolute because of using webob instead of swob. What this patch is doing: Make gate_keeper able to re-transform the location header from absolute path to relative path if 'swift.leave_relative_location' is explicitely set because gate_keeper should be the most left side middleware except catch_errors middleware in the pipeline. Change-Id: Ic634c3f1b1e26635206d5a54df8b15354e8df163
133 lines
5.3 KiB
Python
133 lines
5.3 KiB
Python
# Copyright (c) 2010-2012 OpenStack Foundation
|
|
#
|
|
# 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.
|
|
"""
|
|
The ``gatekeeper`` middleware imposes restrictions on the headers that
|
|
may be included with requests and responses. Request headers are filtered
|
|
to remove headers that should never be generated by a client. Similarly,
|
|
response headers are filtered to remove private headers that should
|
|
never be passed to a client.
|
|
|
|
The ``gatekeeper`` middleware must always be present in the proxy server
|
|
wsgi pipeline. It should be configured close to the start of the pipeline
|
|
specified in ``/etc/swift/proxy-server.conf``, immediately after catch_errors
|
|
and before any other middleware. It is essential that it is configured ahead
|
|
of all middlewares using system metadata in order that they function
|
|
correctly.
|
|
|
|
If ``gatekeeper`` middleware is not configured in the pipeline then it will be
|
|
automatically inserted close to the start of the pipeline by the proxy server.
|
|
"""
|
|
|
|
|
|
from swift.common.swob import Request
|
|
from swift.common.utils import get_logger, config_true_value
|
|
from swift.common.request_helpers import (
|
|
remove_items, get_sys_meta_prefix, OBJECT_TRANSIENT_SYSMETA_PREFIX
|
|
)
|
|
from six.moves.urllib.parse import urlsplit
|
|
import re
|
|
|
|
#: A list of python regular expressions that will be used to
|
|
#: match against inbound request headers. Matching headers will
|
|
#: be removed from the request.
|
|
# Exclude headers starting with a sysmeta prefix.
|
|
# Exclude headers starting with object transient system metadata prefix.
|
|
# Exclude headers starting with an internal backend header prefix.
|
|
# If adding to this list, note that these are regex patterns,
|
|
# so use a trailing $ to constrain to an exact header match
|
|
# rather than prefix match.
|
|
inbound_exclusions = [get_sys_meta_prefix('account'),
|
|
get_sys_meta_prefix('container'),
|
|
get_sys_meta_prefix('object'),
|
|
OBJECT_TRANSIENT_SYSMETA_PREFIX,
|
|
'x-backend']
|
|
|
|
|
|
#: A list of python regular expressions that will be used to
|
|
#: match against outbound response headers. Matching headers will
|
|
#: be removed from the response.
|
|
outbound_exclusions = inbound_exclusions
|
|
|
|
|
|
def make_exclusion_test(exclusions):
|
|
expr = '|'.join(exclusions)
|
|
test = re.compile(expr, re.IGNORECASE)
|
|
return test.match
|
|
|
|
|
|
class GatekeeperMiddleware(object):
|
|
def __init__(self, app, conf):
|
|
self.app = app
|
|
self.logger = get_logger(conf, log_route='gatekeeper')
|
|
self.inbound_condition = make_exclusion_test(inbound_exclusions)
|
|
self.outbound_condition = make_exclusion_test(outbound_exclusions)
|
|
self.shunt_x_timestamp = config_true_value(
|
|
conf.get('shunt_inbound_x_timestamp', 'true'))
|
|
|
|
def __call__(self, env, start_response):
|
|
req = Request(env)
|
|
removed = remove_items(req.headers, self.inbound_condition)
|
|
if removed:
|
|
self.logger.debug('removed request headers: %s' % removed)
|
|
|
|
if 'X-Timestamp' in req.headers and self.shunt_x_timestamp:
|
|
ts = req.headers.pop('X-Timestamp')
|
|
req.headers['X-Backend-Inbound-X-Timestamp'] = ts
|
|
# log in a similar format as the removed headers
|
|
self.logger.debug('shunted request headers: %s' %
|
|
[('X-Timestamp', ts)])
|
|
|
|
def gatekeeper_response(status, response_headers, exc_info=None):
|
|
def fixed_response_headers():
|
|
def relative_path(value):
|
|
parsed = urlsplit(v)
|
|
new_path = parsed.path
|
|
if parsed.query:
|
|
new_path += ('?%s' % parsed.query)
|
|
if parsed.fragment:
|
|
new_path += ('#%s' % parsed.fragment)
|
|
return new_path
|
|
|
|
if not env.get('swift.leave_relative_location'):
|
|
return response_headers
|
|
else:
|
|
return [
|
|
(k, v) if k.lower() != 'location' else
|
|
(k, relative_path(v)) for (k, v) in response_headers
|
|
]
|
|
|
|
response_headers = fixed_response_headers()
|
|
removed = filter(
|
|
lambda h: self.outbound_condition(h[0]),
|
|
response_headers)
|
|
|
|
if removed:
|
|
self.logger.debug('removed response headers: %s' % removed)
|
|
new_headers = filter(
|
|
lambda h: not self.outbound_condition(h[0]),
|
|
response_headers)
|
|
return start_response(status, new_headers, exc_info)
|
|
return start_response(status, response_headers, exc_info)
|
|
return self.app(env, gatekeeper_response)
|
|
|
|
|
|
def filter_factory(global_conf, **local_conf):
|
|
conf = global_conf.copy()
|
|
conf.update(local_conf)
|
|
|
|
def gatekeeper_filter(app):
|
|
return GatekeeperMiddleware(app, conf)
|
|
return gatekeeper_filter
|