Hi Jason

As far as using nginx as the front end, you may be interested in some new 
features in mod_wsgi that got added in the last few versions.

What has been added is directives for mod_wsgi which allow it to handle all the 
possible proxy headers that might get passed through to indicate real client 
IP, whether HTTPS used in front end etc.

The mechanism implemented in mod_wsgi is probably going to be a lot more robust 
than any WSGI middleware out there as you can specify trust relationships so 
that headers for requests coming via something other than your trusted proxy 
can be ignored.

So if using nginx and you have:

    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host $host;

In mod_wsgi you could then use either:

    WSGITrustedProxyHeaders X-Forwarded-For

or:

    WSGITrustedProxyHeaders X-Real-IP

The code in mod_wsgi is smart enough to know that since you are flagging these 
that your intent is that REMOTE_ADDR in WSGI environ be overridden.

Since two headers were supplied that can be used for the same thing, mod_wsgi 
will remove the header that wasn't used so that if the value were different, 
that a WSGI middleware in some framework wouldn't then override things with 
potentially wrong information.

For example, if instead you had said just:

    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header Host $host;

and:

    WSGITrustedProxyHeaders X-Real-IP

any X-Forwarded-For header would be removed so that couldn't be forged by a 
client and be overridden by a WSGI framework.

The other thing you can do is:

    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;

In this case if use:

    WSGITrustedProxyHeaders X-Forwarded-For

with, the value of X-Forwarded-For being a potentially comma separated list of 
IPs, normally the first in that list would be used.

That value is technically completely un-trustable though.

To combat that and the whole issue of the request coming via a proxy or not, 
you can also give a list of IP or IP network masks:

    WSGITrustedProxies 127.0.0.1 10.0.0.0/24

for the trusted location of any proxies.

Any proxy type headers will only be trusted and removed otherwise if not coming 
from a trusted proxy.

In the case of getting a value like:

    1.2.3.4, 5.6.7.8, 10.0.0.255

where it can from proxy at 127.0.0.1, then the headers will be trusted, and 
when looking at the value of X-Forwarded-For in particular, it will use the 
last value appearing in the list prior to a listed trusted proxy.

Thus in this case for REMOTE_ADDR it would use 5.6.7.8 and not 1.2.3.4. So the 
IP for the immediate client before your trusted proxy is used and not the very 
first in the list which can be forged.

As well as headers used in setting up REMOTE_ADDR, there is also code for 
trusting values from headers corresponding to WSGI equivalents of:

static const char *wsgi_proxy_client_headers[] = {
    "HTTP_X_FORWARDED_FOR",
    "HTTP_X_REAL_IP",
    NULL,
};

static const char *wsgi_proxy_scheme_headers[] = {
    "HTTP_X_FORWARDED_HTTPS",
    "HTTP_X_FORWARDED_PROTO",
    "HTTP_X_FORWARDED_SCHEME",
    "HTTP_X_FORWARDED_SSL",
    "HTTP_X_HTTPS",
    "HTTP_X_SCHEME",
    NULL,
};

static const char *wsgi_proxy_host_headers[] = {
    "HTTP_X_FORWARDED_HOST",
    "HTTP_X_HOST",
    NULL,
};

static const char *wsgi_proxy_script_name_headers[] = {
    "HTTP_X_SCRIPT_NAME",
    "HTTP_X_FORWARDED_SCRIPT_NAME",
    NULL,
};

There are also some standalone one as well where usually only one header used 
for it:

            else if (!strcmp(name, "HTTP_X_FORWARDED_SERVER")) {
                if (value) {
                    /* Use the value as is. */

                    apr_table_setn(r->subprocess_env, "SERVER_NAME", value);
                }
            }
            else if (!strcmp(name, "HTTP_X_FORWARDED_PORT")) {
                if (value) {
                    /* Use the value as is. */

                    apr_table_setn(r->subprocess_env, "SERVER_PORT", value);
                }
            }

So if using nginx from SSL termination, can use:

    WSGITrustedProxyHeaders X-Fowarded-Scheme

if that is what you are using to let backend know that front end is actually 
accepting HTTPS and so wsgi.url_scheme needs to be updated.

The code in mod_wsgi knows the types of values to expect based on what header 
you said to use:

            else if (!strcmp(name, "HTTP_X_FORWARDED_PROTO") ||
                !strcmp(name, "HTTP_X_FORWARDED_SCHEME") ||
                !strcmp(name, "HTTP_X_SCHEME")) {

                match_scheme_header = 1;

                if (value) {
                    trusted_scheme_header = name;

                    /* Value can be either 'http' or 'https'. */

                    if (!strcasecmp(value, "https"))
                        apr_table_setn(r->subprocess_env, "HTTPS", "1");
                    else if (!strcasecmp(value, "http"))
                        apr_table_unset(r->subprocess_env, "HTTPS");
                }
            }
            else if (!strcmp(name, "HTTP_X_FORWARDED_HTTPS") ||
                     !strcmp(name, "HTTP_X_FORWARDED_SSL") ||
                     !strcmp(name, "HTTP_X_HTTPS")) {

                match_scheme_header = 1;

                if (value) {
                    trusted_scheme_header = name;

                    /*
                     * Value can be a boolean like flag such as 'On',
                     * 'Off', 'true', 'false', '1' or '0'.
                     */

                    if (!strcasecmp(value, "On") ||
                        !strcasecmp(value, "true") ||
                        !strcasecmp(value, "1")) {

                        apr_table_setn(r->subprocess_env, "HTTPS", "1");
                    }
                    else if (!strcasecmp(value, "Off") ||
                        !strcasecmp(value, "false") ||
                        !strcasecmp(value, "0")) {

                        apr_table_unset(r->subprocess_env, "HTTPS");
                    }
                }
            }

As before, any equivalent header which wasn't the one you said to trust is 
removed from WSGI environ so some WSGI middleware doesn't decide to use it and 
so pick up an untrusted header.

Anyway, thought this may interest you. You can find comments about it in:

    http://modwsgi.readthedocs.org/en/master/release-notes/version-4.4.9.html
    http://modwsgi.readthedocs.org/en/master/release-notes/version-4.4.10.html

There are options in mod_wsgi-express exposing the feature there as well.

Graham

On 23/03/2015, at 10:31 AM, Jason Garber <[email protected]> wrote:

> nginx is a very easy to configure and very reliable web server that is 
> perfectly suited for ssl termination, serving static assets, and proxing the 
> dynamic requests to mod_wsgi.
> 
> We use it with a great deal of convenience and success.  While this isn't an 
> answer to your question it is a way to solve the same issues and get a lot in 
> return.
> 
> Thanks!
> 
> On Fri, Mar 20, 2015 at 7:13 PM, Kyle Handy <[email protected]> wrote:
> Hello,
> 
> I've been developing a Django REST API that supports an iPhone application's 
> data needs and user tracking. We have been using mod_wsgi express to host the 
> API. When working over HTTP the API works great, but we have recently 
> configured the HTTPS portion of the server and it we cannot reach the server 
> over 3G/4G internet connections. 
> 
> The server responds to the following HTTPS requests:
> 
> PC browser request on Wi-Fi
> PC browser request on landline.
> iPhone device request over Wi-Fi
> 
> The server DOES NOT even RECEIVE the following HTTPS requests:
> 
> iPhone device request over 3G
> iPhone device request over 4G
> 
> Is there any special configuration or extra accommodation that is required by 
> Django or the mod_wsgi module in order to support requests over 3G/4G via 
> HTTPS?
> 
> Any help is appreciated.
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "modwsgi" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/modwsgi.
> For more options, visit https://groups.google.com/d/optout.
> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "modwsgi" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/modwsgi.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"modwsgi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/modwsgi.
For more options, visit https://groups.google.com/d/optout.

Reply via email to