Monday, October 01, 2007

Chained Oracle Web Caches, Reverse Proxy, REMOTE_ADDR and CLIENT_IP

Most people today are using a DMZ. There is a great deal of variety in configuring the DMZ, from having nothing but a Reverse Proxy (RP) in the DMZ to having everything including the database in the DMZ (and possibly Dataguard or Oracle Streams to push data to the DMZ database). This post will focus on the specific instances of needing to get the browser's IP (or as close as one can get) all the way through to the database--specifically using Oracle Portal, PL/SQL and Java Portlets, Application Express or anything sitting behind the Oracle Webcache.

Scenario 1: This does not even require a DMZ configuration. If you are running Oracle Portal you may find it hard to get access to the browser's IP address in a UI Template of Page or Item Skin. The problem is that the Parallel Page Engine (PPE) calls Web Cache which in turn calls Apache to get your page. Web Cache sees the call as coming from the PPE, so you get the IP address of your middle tier, not of the browser. I have another post that covers that topic.
That post will be used in all of the scenarios below.


Scenario 2: Oracle Web Cache in the DMZ (WC1) acting as a Reverse Proxy to another Web Cache that serves your content (WC2). It looks like this:

Browser ---> WC1 ---> WC2 ---> Apache ---> mod/plsql ---> Database

The browser sends its IP address in a header variable, REMOTE_ADDR, in all requests. (Note: if it uses a forward proxy, the forward proxy may change the REMOTE_ADDR value to its own IP address. We can't do anything about that. For example, all AOL users may show up with only 3 IP addresses.) When WC1 gets the request, it moves the value of REMOTE_ADDR into a new header variable, CLIENT_IP and puts its own IP address into REMOTE_ADDR. Normally this is not an issues, because mod/plsql automatically puts the value of CLIENT_IP back into REMOTE_ADDR, but with a second web cache, see what happens. WC2 replaces ClientIP with REMOTE_ADDR and puts its own IP into REMOTE_ADDR. The table below shows what happens to these values as they progress...

REMOTE_ADDR x.x.200.10 x.x.300.5 x.x.200.10 x.x.200.10
ClientIP not set x.x.200.10 x.x.200.10 x.x.200.10

As you can see, by the time it makes it to the database the only thing available is the IP address of WC1 (unless it is a PPE call from Portal, in which case it is the IP of WC2).

The Web Cache guys recognized this could happen and put a feature into the Web Cache settings to allow WC2 to accept the ClientIP that was forwarded from a previous Web Cache. In the WC Admin, go to Properties > Security and set "Accept client IP addresses encoded in ClientIP HTTP headers" to Yes.

If you are using Portal, be sure to read about getting the value past the PPE in the link above.


Scenario 3: Apache 2.x in the DMZ (RP1) acting as a Reverse Proxy to another Web Cache that serves your content (WC2). It looks like this:

Browser ---> RP1 ---> WC2 ---> Apache ---> mod/plsql ---> Database

This is similar to scenario 2, however RP1 uses the header variable X-Forwarded-For instead of ClientIP. X-Forwarded-For makes it all the way through to modplsql, but modplsql does not know to send it on to the database. You need to add the following line to your modplsql DAD:

PlsqlCGIEnvironmentList X-Forwarded-For

Your dad will look something like the following:

SetHandler pls_handler
PlsqlDocumentTablename wwv_flow_file_objects$
PlsqlDatabaseUsername HTMLDB_PUBLIC_USER
PlsqlDefaultPage apex
PlsqlDatabasePassword xxxxx
PlsqlCGIEnvironmentList X-Forwarded-For
Allow from all

You can then use owa_util.get_cgi_env('X-Forwarded-For') to retrieve the IP Address of the client.


That covers it. I hope this helps. As always, click on the My Work link to the right if you would like in-person assistance.