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
WIP: Use Forwarded/X-Forwarded-{For,Host,By,Port,Proto} to fixup WSGI environ #209
Conversation
cd161dc
to
a5f205e
Compare
What is the thought process behind the tl;dr; I think there should be a Second point of note - you decided to always trust proxy headers from a unix socket, I'm not sure this is a good idea as it's still not really clear where those headers came from. You'd want to at least document this very clearly. |
I had originally planned to say that if the I wanted some option that would allow you to turn off processing of
I haven't added the security/safety feature yet.
I haven't added it to this PR.
This is false. If you set
You are talking about stuff I haven't implemented in this PR. Currently it only strips the old style headers if you are explicitly trusting
Once again, this is not yet implemented...
I figured there would be some contention on that... BUT the reason why this was done is because with the unix domain socket we don't get valid This technically means that when using a unix domain socket, we can not provide the requisite information required for a valid WSGI environ. Unfortunately PEP3333 provides 0 guidance in this case. |
Ok, I wanted to bring it up even if it isn't a goal to do it in this PR. I think my comment is not wrong, which is why it would be good to add the feature, even if it's not done in this PR. To rephrase slightly - as an app you can't trust the headers unless
Does this mean that some sort of extra support should be added to the |
Ok, this was wrong. Basically you just can't ever trust the headers as an app right now because there is no indication anywhere whether trusted_proxy matched and if it didn't match the headers are preserved. This, we both agree, should be fixed in a separate PR via some sort of |
Due to the way that waitress is architected (and before this PR), currently the |
Also, waitress does not support both unix socket AND tcp/ip in the same process. |
If there are plans to keep it that way then fine. Otherwise it'd probably be smart to think about the concern further. My understanding of the |
While technically possible, I don't think it is a good idea to mix unix socket/tcp in the same process. We'd want to have different rules depending on where we received a connection, and things would get complicated quickly. If you are behind a proxy with tcp/ip you tend to have a single host/port combination you bind to, and only allow the proxy to connect to waitress (generally by binding to 127.0.0.1), with a unix socket implementation that extends to binding to the local file system (so you can't have your proxy on a separate host from your waitress instance). Either way the configuration applies to all instances, there is no way to say "this port should accept proxy headers" and "this one shouldn't". I don't think adding that is worthwhile either. Having either tcp or unix socket makes sense to me, and is the way it has been for a long time. |
Ok. My primary complaint right now is that there's not a way to opt-out of trusting headers when using a unix socket. It really feels like they should default to untrusted and just handle |
@mmerickel The thing about waitress listening a Unix domain socket is that it is always behind a proxy, and one running on the same host -- one can't "expose" it inadvertently. In the typical setup, it is even going to be accessible only to a single user account on that host -- the same one as is running waitress. Hard to think of a case where it shouldn't be trusted. |
@tseaver trusted here is about certain http headers coming in from the proxy. In my experience basically every proxy doesn’t properly strip / validate these headers and thus should count as untrusted until the user tells us via |
If you want a precedent, nginx requires you to opt-in to this type of stuff even for unix sockets. Note that |
Not sure that using an optional module that a user has to explicitly compile into NGINX in a server that has defaults for anything is really precedent. Starting waitress without any config will start a server on a certain port, starting nginx with an empty config file will do nothing (it may even error). Also, nginx doesn't require certain variables to exist in an "environ" before passing the request off to something else. That being said, I understand the concern. User education is going to be key. I just don't know how I can make sure that waitress creates a WSGI environ that isn't one big giant lie when receiving a connection over a unix socket.
Using the url reconstruction specified in PEP 3333 you cannot generate a valid URL from this to re-create the original URL the client sent. |
I'm really only concerned here about removing the |
Another precedent if you want me to keep compiling a list for something that seems fairly uncontroversial to me is express.js where they require you to set |
a5f205e
to
0997311
Compare
ExpressJS doesn't support unix sockets... I didn't set the default on non-unix sockets to trust it either... but whatever. I really don't care. You can set |
Express does support Unix sockets. A value of localhost sounds ok to me. |
0997311
to
dec4c53
Compare
b556fca
to
a3c3bef
Compare
@mmerickel this is ready for another review of the code. I need to still fix/write up the documentation for the changes. |
a3c3bef
to
3b8ba7b
Compare
931f8a3
to
7dd363f
Compare
When facing the possibility of a proxy that is sending wacky values
f674ce7
to
8d531d6
Compare
- log_untrusted_proxy_headers (useful for debugging) | ||
|
||
Be aware that the defaults for these are currently backwards compatible with | ||
older versions of Waitress, this will change in a future release of waitress. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Waitress. This ... Waitress.
|
||
Be aware that the defaults for these are currently backwards compatible with | ||
older versions of Waitress, this will change in a future release of waitress. | ||
If you expect to need this behaviour please explicitly set these variables in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need this behavior, please...
docs/arguments.rst
Outdated
default ``None`` | ||
|
||
trusted_proxy_count | ||
How many proxies we trust when chained |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The number of trusted proxies when chained.
``Forwarded`` supports similar functionality as the different individual | ||
headers, and is mutually exclusive to using the ``X-Forwarded-*`` headers. | ||
|
||
To configure waitress to use the ``Forwarded`` header, set:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/::/: and please specify lexer for correct syntax highlighting.
raise ValueError( | ||
"The values trusted_proxy_headers and clear_untrusted_proxy_headers " | ||
"have no meaning without setting trusted_proxy. Cowardly refusing to " | ||
"continue." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest something like this:
trusted_proxy must be set when trusted_proxy_headers and clear_untrusted_proxy_headers are set.
raise ValueError( | ||
"The Forwarded proxy header and the " | ||
"X-Forwarded-{By,Host,Proto,Port,For} headers are mutually " | ||
"exclusive. Can't trust both!" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Can't trust both!/You must set zero or one of these headers, and not both.
warnings.warn( | ||
'No proxy headers were marked as trusted, but trusted_proxy was set. ' | ||
'Implicitly trusting X-Forwarded-Proto for backwards compatibility. ' | ||
'This will be removed in future versions of waitress.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Waitress
|
||
def warn_unspecified_behavior(header): | ||
self.logger.warning( | ||
"Found multiple values in %s, this has unspecified behaviour. " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%s. This has unspecified behavior.
Aw, crap! Merged while I was reviewing. |
@stevepiercy feel free to open a new PR. The waitress documentation is meh all around though, so there's a lot to do as you already noticed. |
If we are in a trusted proxy situation whereby waitress is running behind nginx/haproxy/any other reverse proxy (whose IP matches what is configured, or if the socket we receive the connection on is a unix socket) then we want to pull a variety of information from the
X-Forwarded-*
headers orForwarded
which has superseded it.The algorithm will pull
X-Forwarded-*
headers, unless there is aForwarded
header present andtrusted_proxy_headers
is set to justforwarded
, in which case it takes priority.By default we will use the proxy
Forwarded
header to set the following:wsgi.url_scheme
(Proto
)SERVER_NAME
(UsingHost
param)SERVER_PORT
(UsingProto
param to set a default, overriden byHost
param)HTTP_HOST
(UsingHost
param, adding theSERVER_PORT
as necessary)REMOTE_ADDR
(UsingFor
param, firstFor
is used as it is client IP address)REMOTE_PORT
(UsingFor
param, as above, if a port is available)This off course also means that Waitress now supports https://tools.ietf.org/html/rfc7239 's Forwarded header! Yay for being standards compliant.
Waitress also now knows how to clean up the headers and thus the WSGI environment, so that untrusted headers are no longer passed through to downstream WSGI applications using
clear_untrusted_proxy_headers
.Closes #207