Question

I have a Pyramid application which uses request.environ['REMOTE_ADDR'] in some places.

The application is served by Python Paste on port 6543 and a nginx server listening on port 80 is forwarding requests to the Paste server.

The nginx configuration is inspired by the Pyramid cookbook:

server {

    listen   80; ## listen for ipv4
    listen   [::]:80 default ipv6only=on; ## listen for ipv6

    server_name  localhost;

    access_log  /var/log/nginx/localhost.access.log;

    location / {

        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:6543;

    }

In the Pyramid application the variable request.environ['REMOTE_ADDR'] is now always equal to 127.0.0.1. I see a few strategies to solve this problem but I don't know if there is a recommended way to do that.

Here is what I'm considering:

  • add a NewRequest subscriber which replaces request.environ['REMOTE_ADDR'] if necessary:

    if 'HTTP_X_REAL_IP' in event.request.environ: event.request.environ['REMOTE_ADDR'] = event.request.environ['HTTP_X_REAL_IP']

  • use a wsgi middleware to modify request.environ before hitting the Pyramid layer.

  • something else

Which strategy do you use for deploying Pyramid applications ? What will happen if I have two nginx proxies ? (the first serving the LAN and a second one one a machine directly connected to the internet).

Was it helpful?

Solution

If you use the paste.deploy.config.PrefixMiddleware in your WSGI pipeline via use = egg:PasteDeploy#prefix, it will automatically translate X-Forwarded-For into REMOTE_ADDR. It is also great for other properties of your reverse proxy, for example it will translate X-Forwarded-Proto into wsgi.url_scheme to ensure that if the user visits with https then generated URLs are also https.

http://pythonpaste.org/deploy/class-paste.deploy.config.PrefixMiddleware.html

OTHER TIPS

I use gevent server behind nginx and I use request.client_addr to get ip address of a client.

If you do not have a WSGI pipline, or do not wish to use paste, then add an event handler:

config.add_subscriber(reverseProxyProtocolCorrection,'pyramid.events.NewRequest')

The event handler can read like:

def reverseProxyProtocolCorrection(event):
    event.request.scheme = 'https' if event.request.headers['X-Forwarded-Proto']=='https' else 'http'
    event.request.remote_addr=parseXForward(event.request.headers['X-Forwarded-For'],event.request.remote_addr)

and make sure your proxy set the headers

in nginx:

location / {
    proxy_pass http://yourapp;
        proxy_redirect     off;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $remote_addr;
        }

in php i do:

        if($_SERVER["HTTP_X_FORWARDED_FOR"]){
        $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
    }else{
        $ip = $_SERVER["REMOTE_ADDR"];
      } 

perhaps in python is similar, $ip is your real ip

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top