10

I'm currently writing a basic dispatch model server based on the Python Eventlet library (http://eventlet.net/doc/). Having looked at the WSGI docs on Eventlet (http://eventlet.net/doc/modules/wsgi.html), I can see that the eventlet.wsgi.server function logs the x-forwarded-for header in addition to the client IP address.

However, the way to obtain this is to attach a file-like object (the default which is sys.stderr) and then have the server pipe that to that object.

I would like to be able to obtain the client IP from within the application itself (i.e. the function that has start_response and environ as parameters). Indeed, an environ key would be perfect for this. Is there a way to obtain the IP address simply (i.e. through the environ dictionary or similar), without having to resort to redirecting the log object somehow?

Prince John Wesley
  • 62,492
  • 12
  • 87
  • 94

1 Answers1

16

What you want is in the wsgi environ, specifically environ['REMOTE_ADDR'].

However, if there is a proxy involved, then REMOTE_ADDR will be the address of the proxy, and the client address will be included (most likely) in HTTP_X_FORWARDED_FOR.

Here's a function that should do what you want, for most cases (all credit to Sævar):

def get_client_address(environ):
    try:
        return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
    except KeyError:
        return environ['REMOTE_ADDR']

You can easily see what is included in the wsgi environ by writing a simple wsgi app and pointing a browser at it, for example:

from eventlet import wsgi
import eventlet

from pprint import pformat

def show_env(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['%s\r\n' % pformat(env)]

wsgi.server(eventlet.listen(('', 8090)), show_env)

And combining the two ...

from eventlet import wsgi
import eventlet

from pprint import pformat

def get_client_address(environ):
    try:
        return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
    except KeyError:
        return environ['REMOTE_ADDR']

def show_env(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['%s\r\n\r\nClient Address: %s\r\n' % (pformat(env), get_client_address(env))]

wsgi.server(eventlet.listen(('', 8090)), show_env)
Community
  • 1
  • 1
Marty
  • 7,920
  • 1
  • 20
  • 10
  • 1
    Thanks Marty, it was in fact my next move to actually do as you suggested in your second snippet. Thanks also for pointing out the proxy information, if I had put a load balancer in front, this would surely have caused some confusion. Having looked at the WSGI spec [link](http://www.python.org/dev/peps/pep-0333/#environ-variables), I see that REMOTE_ADDR is not a required variable, which was probably the source of my confusion! – Michael Halls-Moore Oct 20 '11 at 20:30
  • I was reading some documentation on this header, and it seems to me that the client IP would be the first ip in X-Forwarded-For. So that last bit on the first return should probably be `.split(',')[0].strip()`. Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For – Hugo Mota Apr 02 '17 at 15:14