xref: /unit/test/test_forwarded_header.py (revision 2134:f576f11a58b4)
1*2134Sz.hong@f5.comfrom unit.applications.lang.python import TestApplicationPython
2*2134Sz.hong@f5.com
3*2134Sz.hong@f5.com
4*2134Sz.hong@f5.comclass TestForwardedHeader(TestApplicationPython):
5*2134Sz.hong@f5.com    prerequisites = {'modules': {'python': 'any'}}
6*2134Sz.hong@f5.com
7*2134Sz.hong@f5.com    def forwarded_header(self, forwarded):
8*2134Sz.hong@f5.com        assert 'success' in self.conf(
9*2134Sz.hong@f5.com            {
10*2134Sz.hong@f5.com                "127.0.0.1:7081": {
11*2134Sz.hong@f5.com                    "forwarded": forwarded,
12*2134Sz.hong@f5.com                    "pass": "applications/forwarded_header",
13*2134Sz.hong@f5.com                },
14*2134Sz.hong@f5.com                "[::1]:7082": {
15*2134Sz.hong@f5.com                    "forwarded": forwarded,
16*2134Sz.hong@f5.com                    "pass": "applications/forwarded_header",
17*2134Sz.hong@f5.com                },
18*2134Sz.hong@f5.com            },
19*2134Sz.hong@f5.com            'listeners',
20*2134Sz.hong@f5.com        ), 'listeners configure'
21*2134Sz.hong@f5.com
22*2134Sz.hong@f5.com    def get_fwd(self, sock_type='ipv4', xff=None, xfp=None):
23*2134Sz.hong@f5.com        port = 7081 if sock_type == 'ipv4' else 7082
24*2134Sz.hong@f5.com
25*2134Sz.hong@f5.com        headers = {'Connection': 'close'}
26*2134Sz.hong@f5.com
27*2134Sz.hong@f5.com        if xff is not None:
28*2134Sz.hong@f5.com            headers['X-Forwarded-For'] = xff
29*2134Sz.hong@f5.com
30*2134Sz.hong@f5.com        if xfp is not None:
31*2134Sz.hong@f5.com            headers['X-Forwarded-Proto'] = xfp
32*2134Sz.hong@f5.com
33*2134Sz.hong@f5.com        return self.get(sock_type=sock_type, port=port, headers=headers)[
34*2134Sz.hong@f5.com            'headers'
35*2134Sz.hong@f5.com        ]
36*2134Sz.hong@f5.com
37*2134Sz.hong@f5.com    def get_addr(self, *args, **kwargs):
38*2134Sz.hong@f5.com        return self.get_fwd(*args, **kwargs)['Remote-Addr']
39*2134Sz.hong@f5.com
40*2134Sz.hong@f5.com    def get_scheme(self, *args, **kwargs):
41*2134Sz.hong@f5.com        return self.get_fwd(*args, **kwargs)['Url-Scheme']
42*2134Sz.hong@f5.com
43*2134Sz.hong@f5.com    def setup_method(self):
44*2134Sz.hong@f5.com        self.load('forwarded_header')
45*2134Sz.hong@f5.com
46*2134Sz.hong@f5.com    def test_forwarded_header_single_ip(self):
47*2134Sz.hong@f5.com        self.forwarded_header(
48*2134Sz.hong@f5.com            {
49*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
50*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
51*2134Sz.hong@f5.com                'source': '123.123.123.123',
52*2134Sz.hong@f5.com            }
53*2134Sz.hong@f5.com        )
54*2134Sz.hong@f5.com
55*2134Sz.hong@f5.com        resp = self.get_fwd(xff='1.1.1.1', xfp='https')
56*2134Sz.hong@f5.com        assert resp['Remote-Addr'] == '127.0.0.1', 'both headers addr'
57*2134Sz.hong@f5.com        assert resp['Url-Scheme'] == 'http', 'both headers proto'
58*2134Sz.hong@f5.com
59*2134Sz.hong@f5.com        assert self.get_addr() == '127.0.0.1', 'ipv4 default addr'
60*2134Sz.hong@f5.com        assert self.get_addr('ipv6') == '::1', 'ipv6 default addr'
61*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '127.0.0.1', 'bad source'
62*2134Sz.hong@f5.com        assert self.get_addr(xff='blah') == '127.0.0.1', 'bad xff'
63*2134Sz.hong@f5.com        assert self.get_addr('ipv6', '1.1.1.1') == '::1', 'bad source ipv6'
64*2134Sz.hong@f5.com
65*2134Sz.hong@f5.com        assert self.get_scheme() == 'http', 'ipv4 default proto'
66*2134Sz.hong@f5.com        assert self.get_scheme('ipv6') == 'http', 'ipv6 default proto'
67*2134Sz.hong@f5.com        assert self.get_scheme(xfp='https') == 'http', 'bad proto'
68*2134Sz.hong@f5.com        assert self.get_scheme(xfp='blah') == 'http', 'bad xfp'
69*2134Sz.hong@f5.com        assert self.get_scheme('ipv6', xfp='https') == 'http', 'bad proto ipv6'
70*2134Sz.hong@f5.com
71*2134Sz.hong@f5.com        self.forwarded_header(
72*2134Sz.hong@f5.com            {
73*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
74*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
75*2134Sz.hong@f5.com                'source': '127.0.0.1',
76*2134Sz.hong@f5.com            }
77*2134Sz.hong@f5.com        )
78*2134Sz.hong@f5.com
79*2134Sz.hong@f5.com        resp = self.get_fwd(xff='1.1.1.1', xfp='https')
80*2134Sz.hong@f5.com        assert resp['Remote-Addr'] == '1.1.1.1', 'both headers addr 2'
81*2134Sz.hong@f5.com        assert resp['Url-Scheme'] == 'https', 'both headers proto 2'
82*2134Sz.hong@f5.com
83*2134Sz.hong@f5.com        assert self.get_addr() == '127.0.0.1', 'ipv4 default addr 2'
84*2134Sz.hong@f5.com        assert self.get_addr('ipv6') == '::1', 'ipv6 default addr 2'
85*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '1.1.1.1', 'xff replace'
86*2134Sz.hong@f5.com        assert self.get_addr('ipv6', '1.1.1.1') == '::1', 'bad source ipv6 2'
87*2134Sz.hong@f5.com
88*2134Sz.hong@f5.com        assert self.get_scheme() == 'http', 'ipv4 default proto 2'
89*2134Sz.hong@f5.com        assert self.get_scheme('ipv6') == 'http', 'ipv6 default proto 2'
90*2134Sz.hong@f5.com        assert self.get_scheme(xfp='https') == 'https', 'xfp replace'
91*2134Sz.hong@f5.com        assert self.get_scheme(xfp='on') == 'https', 'xfp replace 2'
92*2134Sz.hong@f5.com        assert (
93*2134Sz.hong@f5.com            self.get_scheme('ipv6', xfp='https') == 'http'
94*2134Sz.hong@f5.com        ), 'bad proto ipv6 2'
95*2134Sz.hong@f5.com
96*2134Sz.hong@f5.com        self.forwarded_header(
97*2134Sz.hong@f5.com            {
98*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
99*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
100*2134Sz.hong@f5.com                'source': '!127.0.0.1',
101*2134Sz.hong@f5.com            }
102*2134Sz.hong@f5.com        )
103*2134Sz.hong@f5.com
104*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '127.0.0.1', 'bad source 3'
105*2134Sz.hong@f5.com        assert self.get_addr('ipv6', '1.1.1.1') == '1.1.1.1', 'xff replace 2'
106*2134Sz.hong@f5.com        assert self.get_scheme(xfp='https') == 'http', 'bad proto 2'
107*2134Sz.hong@f5.com        assert self.get_scheme('ipv6', xfp='https') == 'https', 'xfp replace 3'
108*2134Sz.hong@f5.com
109*2134Sz.hong@f5.com    def test_forwarded_header_ipv4(self):
110*2134Sz.hong@f5.com        self.forwarded_header(
111*2134Sz.hong@f5.com            {
112*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
113*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
114*2134Sz.hong@f5.com                'source': '127.0.0.1',
115*2134Sz.hong@f5.com            }
116*2134Sz.hong@f5.com        )
117*2134Sz.hong@f5.com
118*2134Sz.hong@f5.com        assert (
119*2134Sz.hong@f5.com            self.get_addr(xff='8.8.8.8, 84.23.23.11') == '84.23.23.11'
120*2134Sz.hong@f5.com        ), 'xff replace'
121*2134Sz.hong@f5.com        assert (
122*2134Sz.hong@f5.com            self.get_addr(xff='8.8.8.8, 84.23.23.11, 127.0.0.1') == '127.0.0.1'
123*2134Sz.hong@f5.com        ), 'xff replace 2'
124*2134Sz.hong@f5.com        assert (
125*2134Sz.hong@f5.com            self.get_addr(xff=['8.8.8.8', '127.0.0.1, 10.0.1.1']) == '10.0.1.1'
126*2134Sz.hong@f5.com        ), 'xff replace multi'
127*2134Sz.hong@f5.com
128*2134Sz.hong@f5.com        assert self.get_scheme(xfp='http, https') == 'http', 'xfp replace'
129*2134Sz.hong@f5.com        assert (
130*2134Sz.hong@f5.com            self.get_scheme(xfp='http, https, http') == 'http'
131*2134Sz.hong@f5.com        ), 'xfp replace 2'
132*2134Sz.hong@f5.com        assert (
133*2134Sz.hong@f5.com            self.get_scheme(xfp=['http, https', 'http', 'https']) == 'http'
134*2134Sz.hong@f5.com        ), 'xfp replace multi'
135*2134Sz.hong@f5.com
136*2134Sz.hong@f5.com    def test_forwarded_header_ipv6(self):
137*2134Sz.hong@f5.com        self.forwarded_header(
138*2134Sz.hong@f5.com            {
139*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
140*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
141*2134Sz.hong@f5.com                'source': '::1',
142*2134Sz.hong@f5.com            }
143*2134Sz.hong@f5.com        )
144*2134Sz.hong@f5.com
145*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '127.0.0.1', 'bad source ipv4'
146*2134Sz.hong@f5.com
147*2134Sz.hong@f5.com        for ip in [
148*2134Sz.hong@f5.com            'f607:7403:1e4b:6c66:33b2:843f:2517:da27',
149*2134Sz.hong@f5.com            '2001:db8:3c4d:15::1a2f:1a2b',
150*2134Sz.hong@f5.com            '2001::3c4d:15:1a2f:1a2b',
151*2134Sz.hong@f5.com            '::11.22.33.44',
152*2134Sz.hong@f5.com        ]:
153*2134Sz.hong@f5.com            assert self.get_addr('ipv6', ip) == ip, 'replace'
154*2134Sz.hong@f5.com
155*2134Sz.hong@f5.com        assert self.get_scheme(xfp='https') == 'http', 'bad source ipv4'
156*2134Sz.hong@f5.com
157*2134Sz.hong@f5.com        for proto in ['http', 'https']:
158*2134Sz.hong@f5.com            assert self.get_scheme('ipv6', xfp=proto) == proto, 'replace'
159*2134Sz.hong@f5.com
160*2134Sz.hong@f5.com    def test_forwarded_header_recursive(self):
161*2134Sz.hong@f5.com        self.forwarded_header(
162*2134Sz.hong@f5.com            {
163*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
164*2134Sz.hong@f5.com                'recursive': True,
165*2134Sz.hong@f5.com                'source': ['127.0.0.1', '10.50.0.17', '10.5.2.1'],
166*2134Sz.hong@f5.com            }
167*2134Sz.hong@f5.com        )
168*2134Sz.hong@f5.com
169*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '1.1.1.1', 'xff chain'
170*2134Sz.hong@f5.com        assert (
171*2134Sz.hong@f5.com            self.get_addr(xff='1.1.1.1, 10.5.2.1') == '1.1.1.1'
172*2134Sz.hong@f5.com        ), 'xff chain 2'
173*2134Sz.hong@f5.com        assert (
174*2134Sz.hong@f5.com            self.get_addr(xff='8.8.8.8, 1.1.1.1, 10.5.2.1') == '1.1.1.1'
175*2134Sz.hong@f5.com        ), 'xff chain 3'
176*2134Sz.hong@f5.com        assert (
177*2134Sz.hong@f5.com            self.get_addr(xff='10.50.0.17, 10.5.2.1, 10.5.2.1') == '10.50.0.17'
178*2134Sz.hong@f5.com        ), 'xff chain 4'
179*2134Sz.hong@f5.com        assert (
180*2134Sz.hong@f5.com            self.get_addr(xff=['8.8.8.8', '1.1.1.1, 127.0.0.1']) == '1.1.1.1'
181*2134Sz.hong@f5.com        ), 'xff replace multi'
182*2134Sz.hong@f5.com        assert (
183*2134Sz.hong@f5.com            self.get_addr(xff=['8.8.8.8', '1.1.1.1, 127.0.0.1', '10.5.2.1'])
184*2134Sz.hong@f5.com            == '1.1.1.1'
185*2134Sz.hong@f5.com        ), 'xff replace multi 2'
186*2134Sz.hong@f5.com        assert (
187*2134Sz.hong@f5.com            self.get_addr(xff=['10.5.2.1', '10.50.0.17, 1.1.1.1', '10.5.2.1'])
188*2134Sz.hong@f5.com            == '1.1.1.1'
189*2134Sz.hong@f5.com        ), 'xff replace multi 3'
190*2134Sz.hong@f5.com        assert (
191*2134Sz.hong@f5.com            self.get_addr(
192*2134Sz.hong@f5.com                xff='8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1'
193*2134Sz.hong@f5.com            )
194*2134Sz.hong@f5.com            == '2001:db8:3c4d:15::1a2f:1a2b'
195*2134Sz.hong@f5.com        ), 'xff chain ipv6'
196*2134Sz.hong@f5.com
197*2134Sz.hong@f5.com    def test_forwarded_header_case_insensitive(self):
198*2134Sz.hong@f5.com        self.forwarded_header(
199*2134Sz.hong@f5.com            {
200*2134Sz.hong@f5.com                'client_ip': 'x-forwarded-for',
201*2134Sz.hong@f5.com                'protocol': 'x-forwarded-proto',
202*2134Sz.hong@f5.com                'source': '127.0.0.1',
203*2134Sz.hong@f5.com            }
204*2134Sz.hong@f5.com        )
205*2134Sz.hong@f5.com
206*2134Sz.hong@f5.com        assert self.get_addr() == '127.0.0.1', 'ipv4 default addr'
207*2134Sz.hong@f5.com        assert self.get_addr('ipv6') == '::1', 'ipv6 default addr'
208*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '1.1.1.1', 'replace'
209*2134Sz.hong@f5.com
210*2134Sz.hong@f5.com        assert self.get_scheme() == 'http', 'ipv4 default proto'
211*2134Sz.hong@f5.com        assert self.get_scheme('ipv6') == 'http', 'ipv6 default proto'
212*2134Sz.hong@f5.com        assert self.get_scheme(xfp='https') == 'https', 'replace 1'
213*2134Sz.hong@f5.com        assert self.get_scheme(xfp='oN') == 'https', 'replace 2'
214*2134Sz.hong@f5.com
215*2134Sz.hong@f5.com    def test_forwarded_header_source_empty(self):
216*2134Sz.hong@f5.com        self.forwarded_header(
217*2134Sz.hong@f5.com            {
218*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
219*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
220*2134Sz.hong@f5.com                'source': [],
221*2134Sz.hong@f5.com            }
222*2134Sz.hong@f5.com        )
223*2134Sz.hong@f5.com
224*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '127.0.0.1', 'empty source xff'
225*2134Sz.hong@f5.com        assert self.get_scheme(xfp='https') == 'http', 'empty source xfp'
226*2134Sz.hong@f5.com
227*2134Sz.hong@f5.com    def test_forwarded_header_source_range(self):
228*2134Sz.hong@f5.com        self.forwarded_header(
229*2134Sz.hong@f5.com            {
230*2134Sz.hong@f5.com                'client_ip': 'X-Forwarded-For',
231*2134Sz.hong@f5.com                'protocol': 'X-Forwarded-Proto',
232*2134Sz.hong@f5.com                'source': '127.0.0.0-127.0.0.1',
233*2134Sz.hong@f5.com            }
234*2134Sz.hong@f5.com        )
235*2134Sz.hong@f5.com
236*2134Sz.hong@f5.com        assert self.get_addr(xff='1.1.1.1') == '1.1.1.1', 'source range'
237*2134Sz.hong@f5.com        assert self.get_addr('ipv6', '1.1.1.1') == '::1', 'source range 2'
238*2134Sz.hong@f5.com
239*2134Sz.hong@f5.com    def test_forwarded_header_invalid(self):
240*2134Sz.hong@f5.com        assert 'error' in self.conf(
241*2134Sz.hong@f5.com            {
242*2134Sz.hong@f5.com                "127.0.0.1:7081": {
243*2134Sz.hong@f5.com                    "forwarded": {"source": '127.0.0.1'},
244*2134Sz.hong@f5.com                    "pass": "applications/forwarded_header",
245*2134Sz.hong@f5.com                }
246*2134Sz.hong@f5.com            },
247*2134Sz.hong@f5.com            'listeners',
248*2134Sz.hong@f5.com        ), 'invalid forward'
249*2134Sz.hong@f5.com
250*2134Sz.hong@f5.com        def check_invalid_source(source):
251*2134Sz.hong@f5.com            assert 'error' in self.conf(
252*2134Sz.hong@f5.com                {
253*2134Sz.hong@f5.com                    "127.0.0.1:7081": {
254*2134Sz.hong@f5.com                        "forwarded": {
255*2134Sz.hong@f5.com                            "client_ip": "X-Forwarded-For",
256*2134Sz.hong@f5.com                            "source": source,
257*2134Sz.hong@f5.com                        },
258*2134Sz.hong@f5.com                        "pass": "applications/forwarded_header",
259*2134Sz.hong@f5.com                    }
260*2134Sz.hong@f5.com                },
261*2134Sz.hong@f5.com                'listeners',
262*2134Sz.hong@f5.com            ), 'invalid source'
263*2134Sz.hong@f5.com
264*2134Sz.hong@f5.com        check_invalid_source(None)
265*2134Sz.hong@f5.com        check_invalid_source('a')
266*2134Sz.hong@f5.com        check_invalid_source(['a'])
267