Forward external request IP behind reverse proxy behind OpenWRT

Summary

With the help of tommie from the #linuxger channel, I’ve finally been able to configure my port forwarding to my web server in a way that I see the actual source request IPs in the web server’s access log. So here’s the overall picture:

  1. A request from an external IP like 155.57.58.159 comes to my external, static IP address 213.221.150.68, which is the eth1 WAN interface of my OpenWRT router.
  2. The router should do a port forward of incoming requests on port 80, via its internal LAN  interface br-lan, 192.168.1.1, to my reverse proxy, 192.168.1.3
  3. The reverse proxy shall, depending on the URL that was actually called, forward the request to one of the actual web servers, say, 192.168.1.30
  4. In that web server’s access log, I want to see 155.57.58.159 as originating IP


Here’s what’s needed to set it up.

Configure the target Web Server to understand the X-Forwarded-For header

On 192.168.1.30, I had to install the rpaf module for the Apache web server that I’m using:

apt-get install libapache2-mod-rpaf

I then added three lines to the web sites VirtualHost section:

RPAFenable On
RPAFsethostname On
RPAFproxy_ips 192.168.1.3

Configure the Reverse Proxy to add the X-Forwarded-For header

On 192.168.1.3, I switched from off to on the ProxyPreserveHost directive that was already there:

    ServerName mnott.de
    ServerAlias www.mnott.de

    # Configure reverse proxy.
    ProxyRequests Off
    ProxyPreserveHost On

        ProxyPass http://mnott.de/
        ProxyPassReverse http://mnott.de/
        Order allow,deny
        Allow from all

Make sure, OpenWRT does not do NAT outside-in

This was the real bummer which finally tommie spotted. Until here, everything should just have worked, but didn’t: I still saw only 192.168.1.1 in the web server logs of both the reverse proxy and the final web server. It turned out that masquerading was switched on outside-in:

masq

This amounts to setting to 1 or 0 in the line option masq ‘0’ (where unchecked, i.e., 0, is what you want to have), the following in /etc/config/firewall of the router:

config zone
        option name 'lan'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'ACCEPT'
        option masq '0'
        option network 'lan'

If that’s switched on, then you get masquerading inwards, which will essentially hide the incoming IP address.

Finally, very simply configure the port forward either through the UI:

masq2

or, which is equivalent, adding the following to /etc/config/firewall:

config redirect
        option target 'DNAT'
        option src 'wan'              
        option src_dport '80'
        option dest 'lan' 
        option dest_ip '192.168.1.3'
        option dest_port '80' 
        option proto 'tcp'  
        option name 'HTTP'

If you want to do these forwards by hand, you can for example add the following to /etc/firewall.user:

iptables -t nat -A zone_wan_prerouting -i eth1 -p tcp --dport 80 -j DNAT --to 192.168.1.3
iptables -A forwarding_rule -i eth1 -p tcp --dport 80 -d 192.168.1.3 -j ACCEPT

Which is what actually happens when you add those entries to the UI and then restart the firewall saying “Save & Apply” (which runs /etc/init.d/firewall restart).

Thanks again tommie from #linuxger for that awesome help spotting the surplus masquerading in the iptables -t nat -v -L dump.

Share