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:
- A request from an external IP like 184.108.40.206 comes to my external, static IP address 220.127.116.11, which is the eth1 WAN interface of my OpenWRT router.
- 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
- 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
- In that web server’s access log, I want to see 18.104.22.168 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:
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:
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.