diff --git a/oslo_middleware/healthcheck/__init__.py b/oslo_middleware/healthcheck/__init__.py index bb792c0..0e7a7b0 100644 --- a/oslo_middleware/healthcheck/__init__.py +++ b/oslo_middleware/healthcheck/__init__.py @@ -395,6 +395,8 @@ Reason self._source_ranges = [ ipaddress.ip_network(r) for r in self._conf_get('allowed_source_ranges')] + self._ignore_proxied_requests = self._conf_get( + 'ignore_proxied_requests') self._backends = stevedore.NamedExtensionManager( self.NAMESPACE, self._conf_get('backends'), name_order=True, invoke_on_load=True, @@ -565,6 +567,13 @@ Reason # the request in this middleware. return None + if self._ignore_proxied_requests: + for hdr in [ + 'FORWARDED', 'FORWARDED_PROTO', 'FORWARDED_HOST', + 'FORWARDED_FOR', 'FORWARDED_PREFIX']: + if req.environ.get("HTTP_X_%s" % hdr): + return None + results = [ext.obj.healthcheck(req.server_port) for ext in self._backends] healthy = self._are_results_healthy(results) diff --git a/oslo_middleware/healthcheck/opts.py b/oslo_middleware/healthcheck/opts.py index 3860ea0..a3f78e9 100644 --- a/oslo_middleware/healthcheck/opts.py +++ b/oslo_middleware/healthcheck/opts.py @@ -34,6 +34,9 @@ HEALTHCHECK_OPTS = [ help='A list of network addresses to limit source ip allowed ' 'to access healthcheck information. Any request from ip ' 'outside of these network addresses are ignored.'), + cfg.BoolOpt('ignore_proxied_requests', + default=False, + help='Ignore requests with proxy headers.') ] diff --git a/oslo_middleware/tests/test_healthcheck.py b/oslo_middleware/tests/test_healthcheck.py index e8e9c55..2c861dd 100644 --- a/oslo_middleware/tests/test_healthcheck.py +++ b/oslo_middleware/tests/test_healthcheck.py @@ -63,10 +63,13 @@ class HealthcheckTests(test_base.BaseTestCase): def _do_test_request(self, conf={}, path='/healthcheck', accept='text/plain', method='GET', - server_port=80, remote_addr='127.0.0.1'): + server_port=80, headers=None, + remote_addr='127.0.0.1'): self.app = healthcheck.Healthcheck(self.application, conf) req = webob.Request.blank(path, accept=accept, method=method) req.server_port = server_port + if headers: + req.headers = headers req.remote_addr = remote_addr res = req.get_response(self.app) return res @@ -74,10 +77,12 @@ class HealthcheckTests(test_base.BaseTestCase): def _do_test(self, conf={}, path='/healthcheck', expected_code=webob.exc.HTTPOk.code, expected_body=b'', accept='text/plain', - method='GET', server_port=80, remote_addr='127.0.0.1'): + method='GET', server_port=80, headers=None, + remote_addr='127.0.0.1'): res = self._do_test_request(conf=conf, path=path, accept=accept, method=method, server_port=server_port, + headers=headers, remote_addr=remote_addr) self.assertEqual(expected_code, res.status_int) self.assertEqual(expected_body, res.body) @@ -215,3 +220,28 @@ class HealthcheckTests(test_base.BaseTestCase): expected_code=webob.exc.HTTPOk.code, expected_body=b'Hello, World!!!', remote_addr='192.168.3.1') + + def test_proxied_not_ignored(self): + conf = {} + self._do_test(conf, + expected_code=webob.exc.HTTPOk.code, + headers={'Forwarded-For': 'http://localhost'}) + + def test_proxied_ignored(self): + conf = {'ignore_proxied_requests': True} + modern_headers = { + 'x-forwarded': 'https://localhost' + } + self._do_test(conf, + expected_code=webob.exc.HTTPOk.code, + expected_body=b'Hello, World!!!', + headers=modern_headers) + legacy_headers = { + 'x-forwarded-proto': 'https', + 'x-forwarded-host': 'localhost', + 'x-forwarded-for': '192.0.2.11', + } + self._do_test(conf, + expected_code=webob.exc.HTTPOk.code, + expected_body=b'Hello, World!!!', + headers=legacy_headers) diff --git a/releasenotes/notes/healthcheck-ignore_proxied_requests-d04d1661bd687bc6.yaml b/releasenotes/notes/healthcheck-ignore_proxied_requests-d04d1661bd687bc6.yaml new file mode 100644 index 0000000..be267f5 --- /dev/null +++ b/releasenotes/notes/healthcheck-ignore_proxied_requests-d04d1661bd687bc6.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + The new ``[healthcheck] ignore_proxied_requests`` option has been added. + When this option is set to true, the healthcheck middleware ignores + requests with any of the following headers, which indicates that + the requests came through a reverse proxy or a load balancer. + + - ``x-forwarded`` + - ``x-forwarded-proto`` + - ``x-forwarded-host`` + - ``x-forwarded-for`` + - ``x-forwarded-prefix``