Score:0

iptables and Nginx reverse proxy for localhost

pk flag

Summary

I use Nginx as a reverse proxy and in particular its functionality proxy_bind in order to send the real client IP to the backend. I followed this documentation from nginx.com.
It works well, both when the HTTP server is hosted on another machine or directly on the one of the reverse proxy, except in the second case if the HTTP server listens on localhost.

=> I can't get this configuration works:

# /etc/nginx/conf.d/nginx-revprox-test.conf
server {
    listen 80;
    location / {
        proxy_pass  http://127.0.0.1:8080;
        proxy_bind  $remote_addr transparent;
    }
}

Details

Hypotheses:

  • IP addresses:
    • Reverse proxy/Local backend: 192.168.1.90 (eth0) & 172.17.0.1 (eth1)
    • External backend: 172.17.0.2
    • Client: 192.168.1.91
  • Ports:
    • frontend: 80 (no TLS)
    • backends: 8080
  • Case n°1: The HTTP server is hosted on the external backend (http://172.17.0.2:8080).

    # /etc/nginx/conf.d/nginx-revprox-test.conf
    server {
        listen 192.168.1.90:80;
        location / {
            proxy_pass  http://172.17.0.2:8080;
            proxy_bind  $remote_addr transparent;
        }
    }
    

    The manual says we have to configure iptables consequently:

    iptables -t mangle -A PREROUTING -p tcp -s 172.17.0.2 --sport 8080 -j MARK --set-xmark 0x1/0xffffffff
    ip rule add fwmark 1 lookup 100
    ip route add local 0.0.0.0/0 dev lo table 100
    

    => It works fine.

  • Case n°2: The HTTP server is hosted on the same machine than the reverse proxy and listens on eth1 (http://172.17.0.1:8080).

    # /etc/nginx/conf.d/nginx-revprox-test.conf
    server {
        listen 192.168.1.90:80;
        location / {
            proxy_pass  http://172.17.0.1:8080;
            proxy_bind  $remote_addr transparent;
        }
    }
    

    According to this answer and this diagram, only OUTPUT and POSTROUTING apply to locally generated packets. We need to change our iptables rule consequently:

    iptables -t mangle -A OUTPUT -p tcp -s 172.17.0.2 --sport 8080 -j MARK --set-xmark 0x1/0xffffffff
    

    => It works too.

  • Case n°3: The HTTP server is hosted locally but listen on lo (http://127.0.0.1:8080).

    # /etc/nginx/conf.d/nginx-revprox-test.conf
    server {
        listen 192.168.1.90:80;
        location / {
            proxy_pass  http://127.0.0.1:8080;
            proxy_bind  $remote_addr transparent;
        }
    }
    

    => [!] I can't get it works, even with an OUTPUT rule in iptables.
    curl http://192.168.1.90:80 does not work from the client (it only works from the server but that's not the point).

I missed something about iptables. Could you help me?

pk flag
Precision: the third case without `proxy_bind` works fine, but we of course lose IP transparency.
us flag
I would implement passing the real IP address in a header and then the backend would use that IP address. Performing routing table hacks to achieve this goal makes the solution harder to understand.
djdomi avatar
za flag
cant you also set a custom header? NGINX can this, but your app?
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.