[Twisted-web] forwarding client information from twisted proxy to nevow monster

Andrea Arcangeli andrea at cpushare.com
Tue Dec 13 15:48:11 MST 2005


Without this patch, a nevow server behind twisted proxy can't receive
the correct client information.

It's backwards compatible, passClient = True must be explicitly passed
when creating both the proxy and monster classes for this to work (and
as usual the monster resource must be under the firewall, only the
"trusted" proxy must be allowed to connect to it, for the client info to
be trustable).

Here the Nevow part.

Index: Nevow/nevow/vhost.py
===================================================================
--- Nevow/nevow/vhost.py	(revision 3434)
+++ Nevow/nevow/vhost.py	(working copy)
@@ -134,13 +134,15 @@
     *after* it returns the (resource,segments) tuple.
     """
     implements(inevow.IResource)
+
+    def __init__(self, vhost_segments):
+        self.prepath_segments = vhost_segments+1 # +1 is for /vhost
+
     def locateChild(self, ctx, segments):
         request = inevow.IRequest(ctx)
-        request.prepath = request.prepath[3:]
+        request.prepath = request.prepath[self.prepath_segments:]
         return request.site.resource, segments
 
-_prepathCleaner = _VHostMonsterResourcePrepathCleaner()
-
 class VHostMonsterResource:
     """VHostMonster resource that helps to deploy a Nevow site behind a proxy.
     
@@ -174,17 +176,30 @@
     This also means your private server should serve the real content at
     /foo/bar, and not at the root of the tree.
 
+    If passClient is set to True it expects the client to be passed by the
+    proxy (see ReverseProxyResource passClient parameter for details). When
+    passClient is True this page should not be reacheable directly from the
+    internet if logging the IP address and port securely is needed.
+
     Warning: anyone who can access a VHostMonsterResource can fake the
     host name they are contacting. This can lead to cookie stealing or
     cross site scripting attacks. Never expose /vhost to the Internet.
     """
     implements(inevow.IResource)
 
+    vhost_segments = 2
+
+    def __init__(self, passClient = False):
+        self.passClient = passClient
+        if passClient:
+            self.vhost_segments = 3
+        self._prepathCleaner = _VHostMonsterResourcePrepathCleaner(self.vhost_segments)
+
     def locateChild(self, ctx, segments):
 
         request = inevow.IRequest(ctx)
 
-        if len(segments) < 2:
+        if len(segments) < self.vhost_segments:
             return rend.NotFound
         else:
             if segments[0] == 'http':
@@ -197,13 +212,18 @@
                 port = int(port)
             else:
                 host, port = segments[1], 80
-           
             request.setHost(host, port)
 
-            prefixLen = len('/'+'/'.join(request.prepath)+'/'+'/'.join(segments[:2]))
-            request.path = '/'+'/'.join(segments[2:])
+            if self.passClient:
+                if ':' not in segments[2]:
+                    return rend.NotFound
+                host, port = segments[2].split(':')
+                request.setClient(host, port)
+
+            prefixLen = len('/'+'/'.join(request.prepath)+'/'+'/'.join(segments[:self.vhost_segments]))
+            request.path = '/'+'/'.join(segments[self.vhost_segments:])
             request.uri = request.uri[prefixLen:]
 
-            return _prepathCleaner, segments[2:]
+            return self._prepathCleaner, segments[self.vhost_segments:]
         
 compy.backwardsCompatImplements(VHostMonsterResource)


Here the twisted part:

Index: Twisted/twisted/web/http.py
===================================================================
--- Twisted/twisted/web/http.py	(revision 15293)
+++ Twisted/twisted/web/http.py	(working copy)
@@ -876,6 +876,10 @@
         self.received_headers["host"] = host
         self.host = address.IPv4Address("TCP", host, port)
 
+    def setClient(self, host, port):
+        """Same as setHost but for the client address"""
+        self.client = address.IPv4Address("TCP", host, port)
+
     def getClientIP(self):
         if isinstance(self.client, address.IPv4Address):
             return self.client.host
Index: Twisted/twisted/web/proxy.py
===================================================================
--- Twisted/twisted/web/proxy.py	(revision 15293)
+++ Twisted/twisted/web/proxy.py	(working copy)
@@ -162,23 +162,33 @@
     to a different server.
     """
 
-    def __init__(self, host, port, path):
+    def __init__(self, host, port, path, passClient = False):
         resource.Resource.__init__(self)
         self.host = host
         self.port = port
         self.path = path
+        self.passClient = passClient
 
+    def getPath(self, request):
+        path = self.path
+        if self.passClient:
+            path += '/%s:%d' % (request.client.host, request.client.port)
+        return path
+
     def getChild(self, path, request):
-        return ReverseProxyResource(self.host, self.port, self.path+'/'+path)
+        return ReverseProxyResource(self.host, self.port, self.getPath(request)+'/'+path, False)
 
     def render(self, request):
         request.received_headers['host'] = self.host
         request.content.seek(0, 0)
+
+        path = self.getPath(request)
+
         qs = urlparse.urlparse(request.uri)[4]
         if qs:
-            rest = self.path + '?' + qs
+            rest = path + '?' + qs
         else:
-            rest = self.path
+            rest = path
         clientFactory = ProxyClientFactory(request.method, rest, 
                                      request.clientproto, 
                                      request.getAllHeaders(),



More information about the Twisted-web mailing list