Skip to content

Commit

Permalink
Merge branch 'release/4.9.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
GrahamDumpleton committed Jul 18, 2022
2 parents e98650f + bd5ad01 commit a376ffc
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 10 deletions.
21 changes: 15 additions & 6 deletions README-standalone.rst
Expand Up @@ -10,21 +10,30 @@ The primary package for mod_wsgi is available on the Python package index
version of Apache pre-installed on your target system, and if you don't,
installation of the package will fail.

If you are on a UNIX like system (Linux, macOS) and need a version of
Apache to be installed for you, you can use the ``mod_wsgi-standalone``
If you are on a UNIX like system (Linux) and need a version of Apache
to be installed for you, you can use the ``mod_wsgi-standalone``
package on PyPi instead. When installing the ``mod_wsgi-standalone``
package it will first trigger the installation of the ``mod_wsgi-httpd``
package, which will result in a version of Apache being installed as
part of your Python installation. Next the ``mod_wsgi`` package will be
installed, with it using the version of Apache installed by the
``mod_wsgi-httpd`` package rather than any system package for Apache.

Note that this method of installation is only suitable for where you want
to use ``mod_wsgi-express``. It cannot be used to build mod_wsgi for use
with your system Apache installation. This installation method will also
not work on Windows.
This method of installation is only suitable for where you want to use
``mod_wsgi-express``. It cannot be used to build mod_wsgi for use with
your system Apache installation. This installation method will not
work on Windows, and also currently fails on macOS because the Apache
Runtime (APR) library, has not been updated to latest macOS versions.

When installing mod_wsgi using this method, except that you will install
the ``mod_wsgi-standalone`` package instead of the ``mod_wsgi`` package,
you should follow installation and usage instructions outlined on the
PyPi page for the ``mod_wsgi`` package.

**NOTE: Although this package may allow you to install a standalone Apache
version, it is only really recommended that you use this package if you
have absolutely no other choice for getting the Apache httpd server
installed. Always use the Apache httpd server supplied with the operating
system if you can. Building this package if you do choose to do so, will
take some time. So if you you think the install is hanging, it is probably
still busy compiling everything.**
15 changes: 15 additions & 0 deletions docs/configuration-directives/WSGITrustedProxies.rst
@@ -0,0 +1,15 @@
==================
WSGITrustedProxies
==================

:Description: Specify a list of trusted proxies.
:Syntax: ``WSGITrustedProxies`` *ipaddr|(ipaddr-1 ipaddr-2 ...)*
:Context: server config, virtual host, directory, .htaccess
:Override: ``FileInfo``

Used to specify a list of IP addresses for proxies placed in front of the
Apache instance which are trusted.

This directive only has effect when used in conjunction with the
``WSGITrustedProxyHeaders`` directive. For more details see the documentation
for the ``WSGITrustedProxyHeaders`` directive.
231 changes: 231 additions & 0 deletions docs/configuration-directives/WSGITrustedProxyHeaders.rst
@@ -0,0 +1,231 @@
=======================
WSGITrustedProxyHeaders
=======================

:Description: Specify a list of trusted proxy headers.
:Syntax: ``WSGITrustedProxyHeaders`` *header|(header-1 header-2 ...)*
:Context: server config, virtual host, directory, .htaccess
:Override: ``FileInfo``

When trusted proxies are designated, this is used to specify the headers
which are used to convey information from a proxy to a web server behind the
proxy that are to be trusted.

The IP addresses of the proxies to be trusted should be specified using the
``WSGITrustedProxies`` directive.

As there are multiple conventions for what headers are used to convey
information from the proxy to the web server you need to specify the specific
header from a supported list of headers for a particular purpose that you want
to trust using the ``WSGITrustedProxyHeaders`` directive.

When a request is then received from a trusted proxy, only the header from
the set of headers for that particular purpose is passed through to the WSGI
application and all others will be dropped. If a request was instead from an
IP address which isn't a trusted proxy, then all headers in that set of headers
will be dropped and not passed through.

Depending on the purpose of the header, modifications will be made to other
special variables passed through to the WSGI application. It is these other
variables which is what the WSGI application should consult and the original
header should never be consulted, with it only being provided as an indication
of which header was used to set the special variable.

The different sets of supported headers used by proxies are as follows.

For passing through the IP address of the remote HTTP client the supported
headers are:

* X-Forwarded-For
* X-Client-IP
* X-Real-IP

You should select only one of these headers as the authoritative source for
the IP address of the remote HTTP client as sent by the proxy. Never select
multiple headers because if you do which will be used is indeterminate.

The de-facto standard for this type of header is ``X-Forwarded-For`` and it
is recommended that it be used if your proxy supports it.

The configuration might therefore be::

WSGITrustedProxies 1.2.3.4
WSGITrustedProxyHeaders X-Forwarded-For

With this configuration, when a request is received from the trusted proxy only
the ``X-Forwarded-For`` header will be passed through to the WSGI application.
This will be done following CGI convention as used by WSGI, namely in the
``HTTP_X_FORWARDED_FOR`` variable.

For this set of headers, the ``REMOTE_ADDR`` CGI variable as used by WSGI will
be modified and set to the IP address of the remote HTTP client. A WSGI
application in this case should always use ``REMOTE_ADDR`` and never consult
the original header files.

For passing through the protocol of the original request received by the
trusted proxy the supported headers are:

* X-Forwarded-HTTPS
* X-Forwarded-Proto
* X-Forwarded-Scheme
* X-Forwarded-SSL
* X-HTTPS
* X-Scheme

You should select only one of these headers as the authoritative source for what
protocol was used by the remote HTTP client as sent by the proxy. Never select
multiple headers because if you do which will be used is indeterminate.

The de-facto standard for this type of header is ``X-Forwarded-Proto`` and it
is recommended that it be used if your proxy supports it.

The configuration might therefore be::

WSGITrustedProxies 1.2.3.4
WSGITrustedProxyHeaders X-Forwarded-Proto

With this configuration, when a request is received from the trusted proxy only
the ``X-Forwarded-Proto`` header will be passed through to the WSGI application.
This will be done following CGI convention as used by WSGI, namely in the
``HTTP_X_FORWARDED_PROTO`` variable.

For this set of headers, the ``wsgi.url_scheme`` variable passed to the WSGI
application will be modified to indicate whether the original request used the
``https`` protocol. Note that although it is a convention when using CGI
scripts with Apache, the mod_wsgi module removes the ``HTTPS`` variable from
the set of variables passed to the WSGI application. You should always use
the ``wsgi.url_scheme`` variable in a WSGI application.

For passing through the host name targeted by the original request received by
the trusted proxy the supported headers are:

* X-Forwarded-Host
* X-Host

You should select only one of these headers as the authoritative source for the
host targeted by the original request as sent by the proxy. Never select
multiple headers because if you do which will be used is indeterminate.

The de-facto standard for this type of header is ``X-Forwarded-Host`` and it
is recommended that it be used if your proxy supports it.

The configuration might therefore be::

WSGITrustedProxies 1.2.3.4
WSGITrustedProxyHeaders X-Forwarded-Host

With this configuration, when a request is received from the trusted proxy only
the ``X-Forwarded-Host`` header will be passed through to the WSGI application.
This will be done following CGI convention as used by WSGI, namely in the
``HTTP_X_FORWARDED_HOST`` variable.

For this set of headers, the ``HTTP_HOST`` variable passed to the WSGI
application will be overridden with the value from the header supplied by the
proxy. That is, the value from the proxy for the original request will even
override any explicit ``Host`` header supplied in the request from the proxy,
which in normal cases would be the host of the web server. A WSGI application
should always consult the ``HTTP_HOST`` variable and not the separate header
supplied by the proxy.

For passing through the port targeted by the original request received by the
trusted proxy, the only supported header is:

* X-Forwarded-Port

Although it is the only supported header, you still must select if as a trusted
header to have it processed in the same way as other trusted headers.

The configuration might therefore be::

WSGITrustedProxies 1.2.3.4
WSGITrustedProxyHeaders X-Forwarded-Port

With this configuration, when a request is received from the trusted proxy only
the ``X-Forwarded-Port`` header will be passed through to the WSGI application.
This will be done following CGI convention as used by WSGI, namely in the
``HTTP_X_FORWARDED_PORT`` variable.

For this header, the ``SERVER_PORT`` variable passed to the WSGI application
will be overridden with the value from the header supplied by the proxy. A WSGI
application should always consult the ``SERVER_PORT`` variable and not the
separate header supplied by the proxy.

For passing through the host name of any proxy, to use in overriding the host
name of the web server, the only supported header is:

* X-Forwarded-Server

Although it is the only supported header, you still must select if as a trusted
header to have it processed in the same way as other trusted headers.

The configuration might therefore be::

WSGITrustedProxies 1.2.3.4
WSGITrustedProxyHeaders X-Forwarded-Server

With this configuration, when a request is received from the trusted proxy only
the ``X-Forwarded-Server`` header will be passed through to the WSGI application.
This will be done following CGI convention as used by WSGI, namely in the
``HTTP_X_FORWARDED_SERVER`` variable.

For this header, the ``SERVER_NAME`` variable passed to the WSGI application
will be overridden with the value from the header supplied by the proxy. A WSGI
application should always consult the ``SERVER_NAME`` variable and not the
separate header supplied by the proxy.

For passing through the apparent URL sub path of a web application, as mapped
by the trusted proxy, the supported headers are:

* X-Script-Name
* X-Forwarded-Script-Name

You should select only one of these headers as the authoritative source for the
host targeted by the original request as sent by the proxy. Never select
multiple headers because if you do which will be used is indeterminate.

The configuration might therefore be::

WSGITrustedProxies 1.2.3.4
WSGITrustedProxyHeaders X-Script-Name

With this configuration, when a request is received from the trusted proxy only
the ``X-Script-Name`` header will be passed through to the WSGI application.
This will be done following CGI convention as used by WSGI, namely in the
``HTTP_X_SCRIPT_NAME`` variable.

For this header, the ``SCRIPT_NAME`` variable passed to the WSGI application
will be overridden with the value from the header supplied by the proxy. A WSGI
application should always consult the ``SCRIPT_NAME`` variable and not the
separate header supplied by the proxy.

Examples above show using a single header of a specific purpose at one time.
When you need to trust multiple headers for different purposes, you can list
them separated by spaces using one instance of ``WSGITrustedProxyHeaders``::

WSGITrustedProxyHeaders X-Forwarded-For X-Forwarded-Host X-Forwarded-Port

or in separate directives::

WSGITrustedProxyHeaders X-Forwarded-For
WSGITrustedProxyHeaders X-Forwarded-Host
WSGITrustedProxyHeaders X-Forwarded-Port

As already highlighted you should only list one header for a specific purpose
when there are multiple conventions for what header to use. Which you use will
depend on the configuration of your proxy. You should only trust headers which
are always set by the proxy, never trust headers which are optionally set by
proxies because if not overridden by a proxy, a remote client could still
supply the header.

Also remember that in general you should not consult the proxied headers
themselves, but instead consult the special variables set from those headers
which are passed to the WSGI application and which are defined as being special
to WSGI. As illustration of how such special variables are used, consider
for example the notes in the WSGI specification around URL reconstruction.

* https://peps.python.org/pep-3333/#url-reconstruction

Finally, if using this feature to trust proxies and designated headers, do not
enable in any WSGI framework or application separate functionality it may have
for also processing the proxy headers. You should only rely on what mod_wsgi
has done to update variables special to WSGI.
2 changes: 2 additions & 0 deletions docs/configuration.rst
Expand Up @@ -31,3 +31,5 @@ Configuration
configuration-directives/WSGIScriptAliasMatch
configuration-directives/WSGIScriptReloading
configuration-directives/WSGISocketPrefix
configuration-directives/WSGITrustedProxies
configuration-directives/WSGITrustedProxyHeaders
1 change: 1 addition & 0 deletions docs/release-notes.rst
Expand Up @@ -5,6 +5,7 @@ Release Notes
.. toctree::
:maxdepth: 2

release-notes/version-4.9.3
release-notes/version-4.9.2
release-notes/version-4.9.1
release-notes/version-4.9.0
Expand Down
95 changes: 95 additions & 0 deletions docs/release-notes/version-4.9.3.rst
@@ -0,0 +1,95 @@
=============
Version 4.9.3
=============

Version 4.9.3 of mod_wsgi can be obtained from:

https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.9.3

Bugs Fixed
----------

* When using ``WSGITrustedProxies`` and ``WSGITrustedProxyHeaders`` in the
Apache configuration, or ``--trust-proxy`` and ``--trust-proxy-header``
options with ``mod_wsgi-express``, if you trusted the ``X-Client-IP``
header and a request was received from an untrusted client, the header
was not being correctly removed from the set of headers passed through to
the WSGI application.

This only occurred with the ``X-Client-IP`` header and the same problem was
not present if trusting the ``X-Real-IP`` or ``X-Forwarded-For`` headers.

The purpose of this feature for trusting a front end proxy was in this
case for the headers:

* ``X-Client-IP``
* ``X-Real-IP``
* ``X-Forwarded-For``

and was designed to allow the value of ``REMOTE_ADDR`` passed to the WSGI
application to be rewritten to the IP address that a trusted proxy said
was the real remote address of the client.

In other words, if a request was received from a proxy the IP address
of which was trusted, ``REMOTE_ADDR`` would be set to the value of the
single designated header out of those listed above which was to be
trusted.

In the case where the proxy was trusted, in addition to ``REMOTE_ADDR``
being rewritten, only the trusted header would be passed through. That is,
if ``X-Real-IP`` was the trusted header, then ``HTTP_X_REAL_IP`` would
be passed to the WSGI application, but ``HTTP_X_CLIENT_IP`` and
``HTTP_X_FORWARDED_FOR`` would be dropped if corresponding headers had
also been supplied. That the header used to rewrite ``REMOTE_ADDR`` was
passed through still was only intended for the purpose of documenting
where the value of ``REMOTE_ADDR`` came from. A WSGI application when
relying on this feature should only ever use the value of ``REMOTE_ADDR``
and should ignore the header passed through.

The behaviour as described was therefore based on a WSGI application
not at the same time enabling any WSGI or web framework middleware to
try and process any proxy headers a second time and ``REMOTE_ADDR``
should be the single source of truth. Albeit the headers which were
passed through should have resulted in the same result for ``REMOTE_ADDR``
if the proxy headers were processed a second time.

Now in the case of the client a request was received from not being a
trusted proxy, then ``REMOTE_ADDR`` would not be rewritten, and would
be left as the IP of the client, and none of the headers listed above
were supposed to be passed through.

That ``REMOTE_ADDR`` is not rewritten is implemented correctly when the
client is not a trusted proxy, but of the three headers listed above,
``HTTP_X_CLIENT_ID`` was not being dropped if the corresponding header
was supplied.

If the WSGI application followed best practice and only relied on the
value of ``REMOTE_ADDR`` as the source of truth for the remote client
address, then that ``HTTP_X_CLIENT_ID`` was not being dropped should
pose no security risk. There would however be a problem if a WSGI
application was still enabling a WSGI or web framework specific middleware
to process the proxy headers a second time even though not required. In this
case, the middleware used by the WSGI application may still trust the
``X-Client-IP`` header and rewrite ``REMOTE_ADDR`` allowing a malicious
client to pretend to have a different IP address.

In addition to the WSGI application having redundant checks for the proxy
headers, to take advantage of this, a client would also need direct access
to the Apache/mod_wsgi server instance.

In the case that only clients on your private network behind your proxy
could access the Apache/mod_wsgi server instance, that would imply any
malicious actor already had access to your private network and had access
to hosts in that private network or could attach their own device to that
private network.

In the case where your Apache/mod_wsgi server instance could be accessed
from the same external networks as a proxy forwarding requests to it, such
as may occur if making use of a CDN proxy cache, a client would still need
to know the direct address used by the Apache/mod_wsgi server instance.

Note that only one proxy header for designating the IP of a client should
ever be trusted. If you trust more than one, then which will be used if
both are present is undefined as it is dependent on the order that Apache
processes headers. This hasn't changed and as before to avoid ambiguity you
should only trust one of the proxy headers recognised for this purpose.
2 changes: 1 addition & 1 deletion pyproject.toml.in
@@ -1,3 +1,3 @@
[build-system]
requires = ["setuptools>=40.8.0", "wheel", "mod_wsgi-httpd==2.4.48.1"]
requires = ["setuptools>=40.8.0", "wheel", "mod_wsgi-httpd==2.4.54.1"]
build-backend = "setuptools.build_meta:__legacy__"

0 comments on commit a376ffc

Please sign in to comment.