question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using ProxyHeadersMiddleware with client on trusted proxy results in invalid client information

See original GitHub issue

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

Using ProxyHeadersMiddleware results in the client being [None, 0] when the original client runs on a trusted proxy.

In our setup, we have a client service which runs on one of our proxy servers. The proxy servers run on HAProxy with option forwardfor to add the X-Forwarded-For header. With today’s upgrade to uvicorn 0.14.0, the client fields are no longer present for requests coming from the mentioned server.

To reproduce

This addition to the tests should illustrate the problem:

diff --git a/tests/middleware/test_proxy_headers.py b/tests/middleware/test_proxy_headers.py
index aacf789..b453892 100644
--- a/tests/middleware/test_proxy_headers.py
+++ b/tests/middleware/test_proxy_headers.py
@@ -27,6 +27,8 @@ async def app(scope, receive, send):
         ("127.0.0.1, 10.0.0.1", "Remote: https://1.2.3.4:0"),
         # request from untrusted proxy
         ("192.168.0.1", "Remote: http://127.0.0.1:123"),
+        # request from client running on proxy server
+        (["127.0.0.1", "1.2.3.4"], "Remote: https://1.2.3.4:0"),
     ],
 )
 async def test_proxy_headers_trusted_hosts(trusted_hosts, response_text):

which results in:

>       assert response.text == response_text
E       AssertionError: assert 'Remote: https://None:0' == 'Remote: https://1.2.3.4:0'
E         - Remote: https://1.2.3.4:0
E         ?                 ^^^^^^^
E         + Remote: https://None:0
E         ?                 ^^^^

Expected behavior

The proper client address should be forwarded as before.

Actual behavior

The client is set to [None, 0].

Debugging material

I assume this is a regression of https://github.com/encode/uvicorn/pull/591, more specifically, see the fallthrough discussion on https://github.com/encode/uvicorn/pull/591/files#r438008416. That said, I am not entirely sure what the best way to fix this would be - based on the discussion, the original code seems to be security-related. Would returning the last entry in x_forwarded_for be sufficient?

Environment

Running uvicorn 0.14.0 with CPython 3.9.1 on Linux

I can provide proxy & detailed uvicorn configuration if needed, but I will need to sanitise it, that said, I am not sure if it’s needed for this issue.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
jalazizcommented, Jan 4, 2022

I think the current implementation has multiple errors in it:

  • It returns the next IP in the list after the last trusted, but it also should return the last trusted in case if it was the last host in the x-forwarded-for list (request came from proxy itself)
  • It hasn’t fallback to the default behavior if x-forwarded-for is an empty list for some reason
  • It doesn’t check request_addr like it is one of the proxies. It means that hacker who has direct network access to the server can impersonate IPs (example)

I will add a few tests and will try to fix implementation, but the Iack of ready implementations on Github confuses me. Maybe someone sees any other examples of parsing x-forwarded-for in this way?

Tacking onto this thread, but happy to open a new issue. Another issue I’ve come across is the inability to use CIDRs for the allowed IPs. In a k8s environment, the proxy could be a different pod(s) which forces you to trust everything which is a bit extreme.

1reaction
euri10commented, Jun 7, 2021

I will add a few tests and will try to fix implementation, but the Iack of ready implementations on Github confuses me. Maybe someone sees any other examples of parsing x-forwarded-for in this way?

that would be awesome !

Read more comments on GitHub >

github_iconTop Results From Across the Web

s:Server Invalid client data - code 204 - MSDN - Microsoft
hi,. I was trying to print out my campaigns with the api. But it's denied by the error : s:Server Invalid client data....
Read more >
26 Configuring Security Between Clients and Servers
This section provides a list of the steps that must be performed to allow Oracle Unified Directory to accept SSL-based connections using a...
Read more >
https://raw.githubusercontent.com/encode/uvicorn/m...
WindowsSelectorEventLoopPolicy()` when using multiple workers to avoid ... (#1042) 5/23/21 - Add search for a trusted host in ProxyHeadersMiddleware (#591) ...
Read more >
The perils of the “real” client IP | adam-p
Using special “true client IPs” set by reverse proxies (like ... the last proxy, which means it is the most reliable source of...
Read more >
Laravel trusted proxies - Stack Overflow
You can add a list of trusted proxies with setTrustedProxyIpAddresses function. see here. <?php namespace App\Http\Middleware; use ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found