changeset 3002:702d72f11b95

use x-forwarded-for to get right remote_addr if a proxy is used (port from 1.6)
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 06 Jan 2008 18:29:12 +0100
parents ed3e2b220dec
children f54c41b3b7ce
files MoinMoin/request/__init__.py
diffstat 1 files changed, 62 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/request/__init__.py	Sun Jan 06 17:36:51 2008 +0100
+++ b/MoinMoin/request/__init__.py	Sun Jan 06 18:29:12 2008 +0100
@@ -3,12 +3,45 @@
     MoinMoin - RequestBase Implementation
 
     @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
+                2003-2008 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
+# Support for remote IP address detection when using (reverse) proxy (or even proxies).
+# If you exactly KNOW which (reverse) proxies you can trust, put them into the list
+# below, so we can determine the "outside" IP as your trusted proxies see it.
+
+proxies_trusted = [] # trust noone!
+#proxies_trusted = ['127.0.0.1', ] # can be a list of multiple IPs
+
+import logging
+proxy_loglevel = logging.DEBUG # logging.NOTSET (never), logging.INFO (when not debugging)
+
+def find_remote_addr(addrs):
+    """ Find the last remote IP address before it hits our reverse proxies.
+        The LAST address in the <addrs> list is the remote IP as detected by the server
+        (not taken from some x-forwarded-for header).
+        The FIRST address in the <addrs> list might be the client's IP - if noone cheats
+        and everyone supports x-f-f header.
+
+        See http://bob.pythonmac.org/archives/2005/09/23/apache-x-forwarded-for-caveat/                                                          
+
+        For debug loglevel, we log all <addrs>.
+
+        TODO: refactor request code to first do some basic IP init, then load configuration,
+        TODO: then do proxy processing.
+        TODO: add wikiconfig configurability for proxies_trusted
+        TODO: later, make it possible to put multipe remote IP addrs into edit-log
+    """
+    logging.log(proxy_loglevel, "request.find_remote_addr: addrs == %r" % addrs)
+    if proxies_trusted:
+        result = [addr for addr in addrs if addr not in proxies_trusted]
+        if result:
+            return result[-1] # last IP before it hit our trusted (reverse) proxies
+    return addrs[-1] # this is a safe remote_addr, not taken from x-f-f header
+
+
 import os, re, time, sys, cgi, StringIO
-import logging
 import Cookie
 import traceback
 
@@ -72,7 +105,8 @@
     # Extra headers we support. Both standalone and twisted store
     # headers as lowercase.
     moin_location = 'x-moin-location'
-    proxy_host = 'x-forwarded-host'
+    proxy_host = 'x-forwarded-host' # original host: header as seen by the proxy (e.g. wiki.example.org)
+    proxy_xff = 'x-forwarded-for' # list of original remote_addrs as seen by the proxies (e.g. <clientip>,<proxy1>,<proxy2>,...)
 
     def __init__(self, properties={}):
 
@@ -375,6 +409,7 @@
         self.setIsSSL(env)
         self.setHost(env.get('HTTP_HOST'))
         self.fixURI(env)
+
         self.setURL(env)
         #self.debugEnvironment(env)
 
@@ -460,10 +495,10 @@
 
         @param env: dict like object containing cgi meta variables or http headers.
         """
-        # If we serve on localhost:8000 and use a proxy on
-        # example.com/wiki, our urls will be example.com/wiki/pagename
-        # Same for the wiki config - they must use the proxy url.
+        # proxy support
+        self.rewriteRemoteAddr(env)
         self.rewriteHost(env)
+
         self.rewriteURI(env)
 
         if not self.request_uri:
@@ -489,6 +524,27 @@
         if proxy_host:
             self.http_host = proxy_host
 
+    def rewriteRemoteAddr(self, env):
+        """ Rewrite remote_addr transparently
+        
+        Get the proxy remote addr using 'X-Forwarded-For' header, added by
+        Apache 2 and other proxy software.
+        
+        TODO: Will not work for Apache 1 or others that don't add this header.
+        
+        TODO: If we want to add an option to disable this feature it
+        should be in the server script, because the config is not
+        loaded at this point, and must be loaded after url is set.
+        
+        @param env: dict like object containing cgi meta variables or http headers.
+        """
+        xff = (env.get(self.proxy_xff) or
+               env.get(cgiMetaVariable(self.proxy_xff)))
+        if xff:
+            xff = [addr.strip() for addr in xff.split(',')]
+            xff.append(self.remote_addr)
+            self.remote_addr = find_remote_addr(xff)
+
     def rewriteURI(self, env):
         """ Rewrite request_uri, script_name and path_info transparently