summaryrefslogtreecommitdiff
path: root/oslo_middleware/http_proxy_to_wsgi.py
blob: 632f5cad6fc6baf30836587c198db315ff1f9b03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# -*- 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 oslo_middleware import base


OPTS = [
    cfg.BoolOpt('enable_proxy_headers_parsing',
                default=False,
                help="Whether the application is behind a proxy or not. "
                     "This determines if the middleware should parse the "
                     "headers or not.")
]


class HTTPProxyToWSGI(base.ConfigurableMiddleware):
    """HTTP proxy to WSGI termination middleware.

    This middleware overloads WSGI environment variables with the one provided
    by the remote HTTP reverse proxy.

    """

    def __init__(self, application, *args, **kwargs):
        super(HTTPProxyToWSGI, self).__init__(application, *args, **kwargs)
        self.oslo_conf.register_opts(OPTS, group='oslo_middleware')

    @staticmethod
    def _parse_rfc7239_header(header):
        """Parses RFC7239 Forward headers.

        e.g. for=192.0.2.60;proto=http, for=192.0.2.60;by=203.0.113.43

        """
        result = []
        for proxy in header.split(","):
            entry = {}
            for d in proxy.split(";"):
                key, _, value = d.partition("=")
                entry[key.lower().strip()] = value.strip()
            result.append(entry)
        return result

    def process_request(self, req):
        if not self._conf_get('enable_proxy_headers_parsing'):
            return
        fwd_hdr = req.environ.get("HTTP_FORWARDED")
        if fwd_hdr:
            proxies = self._parse_rfc7239_header(fwd_hdr)
            # Let's use the value from the first proxy
            if proxies:
                proxy = proxies[0]

                forwarded_proto = proxy.get("proto")
                if forwarded_proto:
                    req.environ['wsgi.url_scheme'] = forwarded_proto

                forwarded_host = proxy.get("host")
                if forwarded_host:
                    req.environ['HTTP_HOST'] = forwarded_host

                forwarded_for = proxy.get("for")
                if forwarded_for:
                    req.environ['REMOTE_ADDR'] = forwarded_for

        else:
            # World before RFC7239
            forwarded_proto = req.environ.get("HTTP_X_FORWARDED_PROTO")
            if forwarded_proto:
                req.environ['wsgi.url_scheme'] = forwarded_proto

            forwarded_host = req.environ.get("HTTP_X_FORWARDED_HOST")
            if forwarded_host:
                req.environ['HTTP_HOST'] = forwarded_host

            forwarded_for = req.environ.get("HTTP_X_FORWARDED_FOR")
            if forwarded_for:
                req.environ['REMOTE_ADDR'] = forwarded_for

        v = req.environ.get("HTTP_X_FORWARDED_PREFIX")
        if v:
            req.environ['SCRIPT_NAME'] = v + req.environ['SCRIPT_NAME']