Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve ProxyHeadersMiddleware #2231

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b45fafa
Allow ip ranges for FORWARDED_ALLOW_IPS
pypae Aug 22, 2022
3caf2a1
Fix host for empty x-forwarded-for header
pypae Aug 22, 2022
d721ef4
Add multi-host ip-range testcase, run scripts/lint
pypae Aug 22, 2022
271f739
Re-run black with line-length 88
pypae Aug 22, 2022
bdd7977
Wrap comments to fit in line-length
pypae Aug 22, 2022
064341d
Another missed line-length violation
pypae Aug 22, 2022
97bfc18
Fix __contains__ annotations
pypae Aug 22, 2022
e6280c9
Add support for IPv6
nhairs Jan 25, 2024
61b93d0
Fix linting
nhairs Jan 25, 2024
c5dc874
Fix test cases
nhairs Jan 25, 2024
4e69494
Update docs, comments
nhairs Jan 25, 2024
6f28102
Update index's usage
nhairs Jan 25, 2024
e64ab81
Fix more linting
nhairs Jan 25, 2024
819f72d
Better unix socket support
nhairs Jan 28, 2024
ccff426
Expand proxy documentation
nhairs Jan 29, 2024
10f1f10
More docs and code comments
nhairs Jan 29, 2024
39c794f
Refactor common test cases, add cases for UDS
nhairs Jan 29, 2024
28cf1ba
Update settings docs
nhairs Jan 29, 2024
8d25920
Merge branch 'master' into proxy-headers
nhairs Jan 29, 2024
1b3eb6f
Remove "better UDS handling"
nhairs Feb 10, 2024
fcaf9d2
Remove code leading to coverage failures
nhairs Feb 10, 2024
9d71480
Merge remote-tracking branch 'upstream/master' into proxy-headers
nhairs Feb 10, 2024
899b456
Apply suggestions from code review
nhairs Mar 5, 2024
9d095e6
Merge branch 'master' into proxy-headers
nhairs Mar 5, 2024
772ff9b
Fixes, and more tests
nhairs Mar 5, 2024
22f8554
Update uvicorn/config.py
nhairs Mar 5, 2024
a25f839
Run ruff formatter
nhairs Mar 5, 2024
b9e079d
Replace cast with type: ignore
nhairs Mar 6, 2024
b5136dd
remove make_x_headers
nhairs Apr 19, 2024
34f49b3
Merge branch 'master' into proxy-headers
nhairs Apr 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ Options:
Enable/Disable default Server header.
--date-header / --no-date-header
Enable/Disable default Date header.
--forwarded-allow-ips TEXT Comma separated list of IPs to trust with
proxy headers. Defaults to the
$FORWARDED_ALLOW_IPS environment variable if
available, or '127.0.0.1'.
--forwarded-allow-ips TEXT Comma separated list of IP Addresses, IP
Networks, or literals (e.g. UNIX Socket
path) to trust with proxy headers. Defaults
to the $FORWARDED_ALLOW_IPS environment
variable if available, or '127.0.0.1'. The
literal '*' means trust everything.
--root-path TEXT Set the ASGI 'root_path' for applications
submounted below a given URL path.
--limit-concurrency INTEGER Maximum number of concurrent connections or
Expand Down Expand Up @@ -263,12 +265,9 @@ Using Nginx as a proxy in front of your Uvicorn processes may not be necessary,

In managed environments such as `Heroku`, you won't typically need to configure Nginx, as your server processes will already be running behind load balancing proxies.

The recommended configuration for proxying from Nginx is to use a UNIX domain socket between Nginx and whatever the process manager that is being used to run Uvicorn.
Note that when doing this you will need to run Uvicorn with `--forwarded-allow-ips='*'` to ensure that the domain socket is trusted as a source from which to proxy headers.
The recommended configuration for proxying from Nginx is to use a UNIX domain socket between Nginx and whatever the process manager that is being used to run Uvicorn. If using Uvicorn directly you can bind it to a UNIX domain socket using `uvicorn --uds /path/to/socket.sock <...>`.

When fronting the application with a proxy server you want to make sure that the proxy sets headers to ensure that the application can properly determine the client address of the incoming connection, and if the connection was over `http` or `https`.

You should ensure that the `X-Forwarded-For` and `X-Forwarded-Proto` headers are set by the proxy, and that Uvicorn is run using the `--proxy-headers` setting. This ensures that the ASGI scope includes correct `client` and `scheme` information.
When running your application behind one or more proxies you will want to make sure that each proxy sets appropriate headers to ensure that your application can properly determine the client address of the incoming connection, and if the connection was over `http` or `https`. For more information see [Proxies and Forwarded Headers][#proxies-and-forwarded-headers] below.

Here's how a simple Nginx configuration might look. This example includes setting proxy headers, and using a UNIX domain socket to communicate with the application server.

Expand Down Expand Up @@ -346,3 +345,37 @@ $ gunicorn --keyfile=./key.pem --certfile=./cert.pem -k uvicorn.workers.UvicornW
[nginx_websocket]: https://nginx.org/en/docs/http/websocket.html
[letsencrypt]: https://letsencrypt.org/
[mkcert]: https://github.com/FiloSottile/mkcert

## Proxies and Forwarded Headers

When running an application behind one or more proxies, certain information about the request is lost. To avoid this most proxies will add headers containing this information for downstream servers to read.

Uvicorn currently supports the following headers:

- `X-Forwarded-For` ([MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For))
- `X-Forwarded-Proto`([MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto))

Uvicorn can use these headers to correctly set the client and protocol in the request. However as anyone can set these headers you must configure which "clients" you will trust to have set them correctly.

Uvicorn can be configured to trust IP Addresses (e.g. `127.0.0.1`), IP Networks (e.g. `10.100.0.0/16`), or Literals (e.g. `/path/to/socket.sock`). When running from CLI these are configured using `--forwarded-trust-ips`.

!!! Warning: Only trust clients you can actually trust
Incorrectly trusting other clients can lead to malicious actors spoofing their apparent client address to your application.

For more informations see [`ProxyHeadersMiddleware`](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
### Client Port

Currently if the `ProxyHeadersMiddleware` is able to retrieve a trusted client value then the client's port will be set to `0`. This is because port information is lost when using these headers.

### UNIX Domain Sockets (UDS)

Although it is common for UNIX Domain Sockets to be used for communicating between various HTTP servers, they can mess with some of the expected received values as they will be various non-address strings or missing values.

For example:
- when NGINX itself is running behind a UDS it will add the literal `unix:` as the client in the `X-Forwarded-For` header.
- When Uvicorn is running behind a UDS the initial client will be `None`.


nhairs marked this conversation as resolved.
Show resolved Hide resolved
### Trust Everything

Rather than specifying what to trust, you can instruct Uvicorn to trust all clients using the literal `"*"`. You should only set this when you know you can trust all values within the forwarded headers (e.g. because your proxies remove the existing headers before setting their own).
10 changes: 6 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,12 @@ Options:
Enable/Disable default Server header.
--date-header / --no-date-header
Enable/Disable default Date header.
--forwarded-allow-ips TEXT Comma separated list of IPs to trust with
proxy headers. Defaults to the
$FORWARDED_ALLOW_IPS environment variable if
available, or '127.0.0.1'.
--forwarded-allow-ips TEXT Comma separated list of IP Addresses, IP
Networks, or literals (e.g. UNIX Socket
path) to trust with proxy headers. Defaults
to the $FORWARDED_ALLOW_IPS environment
variable if available, or '127.0.0.1'. The
literal '*' means trust everything.
--root-path TEXT Set the ASGI 'root_path' for applications
submounted below a given URL path.
--limit-concurrency INTEGER Maximum number of concurrent connections or
Expand Down
4 changes: 2 additions & 2 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ Note that WSGI mode always disables WebSocket support, as it is not supported by
## HTTP

* `--root-path <str>` - Set the ASGI `root_path` for applications submounted below a given URL path.
* `--proxy-headers` / `--no-proxy-headers` - Enable/Disable X-Forwarded-Proto, X-Forwarded-For, X-Forwarded-Port to populate remote address info. Defaults to enabled, but is restricted to only trusting
* `--proxy-headers` / `--no-proxy-headers` - Enable/Disable X-Forwarded-Proto, X-Forwarded-For to populate remote address info. Defaults to enabled, but is restricted to only trusting
connecting IPs in the `forwarded-allow-ips` configuration.
* `--forwarded-allow-ips` <comma-separated-list> Comma separated list of IPs to trust with proxy headers. Defaults to the `$FORWARDED_ALLOW_IPS` environment variable if available, or '127.0.0.1'. A wildcard '*' means always trust.
* `--forwarded-allow-ips` <comma-separated-list> Comma separated list of IP Addresses, IP Networks, or literals (e.g. UNIX Socket path) to trust with proxy headers. Defaults to the `$FORWARDED_ALLOW_IPS` environment variable if available, or '127.0.0.1'. The literal `'*'` means trust everything.
* `--server-header` / `--no-server-header` - Enable/Disable default `Server` header.
* `--date-header` / `--no-date-header` - Enable/Disable default `Date` header.

Expand Down
Loading