1

I need forward packets from one server ("as a proxy") to another with keeping the original IP address of clients. Topology is: X.X.X.X - public IP1 ("proxy server") Y.Y.Y.Y - public IP2 (host for virtualization) 172.16.0.2 - private IP (virutal machine with web server)

some public IP ---> X.X.X.X ---> Y.Y.Y.Y ---> 172.16.0.2 (web server)

On systems with X.X.X.X and Y.Y.Y.Y forwarding is enabled.

Traffic from some public IP via X.X.X.X is on X.X.X.X routed to Y.Y.Y.Y but it never gets there. I captured it with tcpdump. If I use masquerade on "proxy server" it works OK, but the original ip address is not preserved. If I use DNAT for port 80 only form Y.Y.Y.Y to 172.16.0.2 - iptables -t nat -A PREROUTING -d Y.Y.Y.Y/32 -i venet0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.16.0.2:80 and I try Y.Y.Y.Y:80 it works ok. Problem is the most probably on "proxy server" with public IP X.X.X.X.

"Proxy server" host IP X.X.X.X

It has only one interface with connect to the internet - eth0.

iptables rule: (forwarding in filter table is allowed)

iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination Y.Y.Y.Y:443

Route table: It uses main table with:

default via X.X.X.1 dev eth0 onlink

Host IP Y.Y.Y.Y

It has only one interface with connect to the internet - venet0. For VM is used Qemu and interface br0.

iptables rule: (forwarding in filter table is allowed)

iptables -t nat -A PREROUTING -d Y.Y.Y.Y/32 -i venet0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.16.0.2:443
iptables -t nat -A POSTROUTING -s 172.16.0.0/24 ! -o br0 -j MASQUERADE

Route table: It uses main table with:

default via 255.255.255.254 dev venet0

Host IP 172.16.0.2

It has only one interface with connect to the internet - ens6 Route table: It uses main table with:

default via 172.16.0.1 dev ens6 proto static

iptables rules according to gapsf answer:

X.X.X.X iptables rules:

iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination Y.Y.Y.Y
iptables -t nat -A POSTROUTING -s Y.Y.Y.Y/32 -o eth0 -p tcp -m tcp --sport 443 -j SNAT --to-source X.X.X.X
iptables -A FORWARD -d Y.Y.Y.Y/32 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A FORWARD -s Y.Y.Y.Y/32 -p tcp -m tcp --sport 443 -j ACCEPT

Y.Y.Y.Y iptables rules:

iptables -t nat -A PREROUTING -i venet0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.16.0.2:443
iptables -t nat -A POSTROUTING -s 172.16.0.2/32 -o venet0 -p tcp -m tcp --sport 443 -j SNAT --to-source Y.Y.Y.Y
iptables -A FORWARD -d 172.16.0.2/32 -i venet0 -o br0 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A FORWARD -s 172.16.0.2/32 -i br0 -o venet0 -p tcp -m tcp --sport 443 -j ACCEPT

172.16.0.2 iptables rules:

iptables -A INPUT -i ens6 -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A OUTPUT -o ens6 -p tcp -m tcp --sport 443 -j ACCEPT

Could you help me please - where is the problem?

9
  • If you don't want SNAT/DNAT you don't need iptables. Just set up routing (network A needs to know how to get to network B, and B needs to know how to get to A, etc.) Commented Aug 8, 2022 at 9:51
  • Or, the good old rinetd....
    – K-attila-
    Commented Aug 8, 2022 at 9:54
  • @roaima Thanks, but I need web server behind X.X.X.X address...
    – Mato
    Commented Aug 8, 2022 at 10:06
  • 1
    Then you can't resolve the requirement. End of game Commented Aug 8, 2022 at 11:15
  • @roaima I need see real IP addresses in web access logs, any other possible solution?
    – Mato
    Commented Aug 8, 2022 at 12:38

2 Answers 2

1

In comments you've indicated the web servers to which you want to forward traffic do not have a route to return packets directly to the web clients. This is a very common scenario, but it's one that's too complex for iptables.

The usual solutions involve either:

  1. A hardware or software load balancer product in a kind of "DMZ" subnet that is the machine with the default route for the web servers. The load balancer has the public X.X.X.X ip address that the clients know, and via DNAT forwards the traffic to the web servers with the source address unchanged. The load balancer is the default gateway for the web servers, so their return packets can pass back through the balancer and it can perform SNAT on the return traffic. This sophisticated software and/or hardware has a cost that may put it out of reach for many organizations.

  2. Perform SNAT when forwarding traffic to the web servers, but use a proxy to add an HTTP header that informs the web server software of the client's true IP address. The header I've usually seen used for this is X-Forwarded-For:. Many HTTP server libs will automatically recognize this header and make the IP address in the header available for the app to use. Some apps expect only a single IP address in the header, some can handle a list of addresses (because the traffic may pass through a proxy before it reaches your proxy). Most web proxy software can add this header.

As with everything else, there are tradeoffs. The 2nd option of proxying the HTTP traffic and adding a header requires the HTTP traffic match the accepted standards so the proxy software can manipulate it. If your clients talk a custom protocol that's loosely based on HTTP but not fully compliant, the proxy's attempts to modify the requests can cause trouble. For example if your protocol uses URLs that are longer than 64k bytes. (I've run into that issue before, though it was a number of years ago).

Given the requirements you've mentioned, I think #2 is the most likely solution for you at first. If your web traffic grows, you'll probably outgrow it and need a more sophisticated (and costly) load balancing solution.

2
  • Thanks. But 2 things: Ad1: Why traffic from some public IP routed on X.X.X.X to Y.Y.Y.Y never gets to Y.Y.Y.Y only in one direction? And I see that it leaves correctly X.X.X.X (with tcpdump), but never gets to Y.Y.Y.Y (tcpdump on Y.Y.Y.Y)... Ad2: Could you provide me some concrete solution (some SW tools, best practises etc.) for #2 please?
    – Mato
    Commented Aug 8, 2022 at 16:06
  • @Mato I can't explain the network between your X.X.X.X machine and your Y.Y.Y.Y machine. Firewall dropping the traffic? Packet filter on the Y.Y.Y.Y machine? It's impossible to guess remotely. My answer #2 can be implemented with nginx as one example. A search with the header name turns up a number of results, including nginx.com/resources/wiki/start/topics/examples/forwarded
    – Sotto Voce
    Commented Aug 8, 2022 at 17:27
1

You dont need expensive hardware - only routing+iptables just enough I suppouse X.X.X.X host have one interface only?

on X.X.X.X

ip route add default via eth0
PREORUTING in eth0 dport 80 DNAT to Y.Y.Y.Y
POSTROUTING out eth0 src Y.Y.Y.Y sport 80 SNAT to X.X.X.X
FORWARD dst Y.Y.Y.Y dport 80 ACCEPT
FORWARD src Y.Y.Y.Y sport 80 ACCEPT

on Y.Y.Y.Y

ip route add default via venet0
ip route add 172.16.0.2 via br0
PREROUTING in venet0 dport 80 DNAT to 172.16.0.2
POSTROUTING out venet0 src 172.16.0.2 sport 80 SNAT to Y.Y.Y.Y
FORWARD in venet0 out br0 dst 172.16.0.2 dport 80 ACCEPT
FORWARD in br0 out venet0 src 172.16.0.2 sport 80 ACCEPT

on 172.16.0.2

ip route add default via 172.16.0.1
INPUT in ens6 dport 80 ACCEPT
OUTPUT out ens6 sport 80 ACCEPT

Part 2
X.X.X.X iptables rules:

iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination Y.Y.Y.Y
iptables -t nat -A POSTROUTING -s Y.Y.Y.Y/32 -o eth0 -p tcp -m tcp --sport 443 -j SNAT --to-source X.X.X.X
iptables -A FORWARD -d Y.Y.Y.Y/32 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A FORWARD -s Y.Y.Y.Y/32 -p tcp -m tcp --sport 443 -j ACCEPT

Y.Y.Y.Y iptables rules:

iptables -t nat -A PREROUTING -i venet0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.16.0.2:443
iptables -t nat -A POSTROUTING -s 172.16.0.2/32 -o venet0 -p tcp -m tcp --sport 443 -j SNAT --to-source Y.Y.Y.Y
iptables -A FORWARD -d 172.16.0.2/32 -i venet0 -o br0 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A FORWARD -s 172.16.0.2/32 -i br0 -o venet0 -p tcp -m tcp --sport 443 -j ACCEPT

172.16.0.2 iptables rules:

iptables -A INPUT -i ens6 -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A OUTPUT -o ens6 -p tcp -m tcp --sport 443 -j ACCEPT
10
  • Many thanks. Yes X.X.X.X host has only one interface. I have added iptables rules according to your instructions to main post, please see on this. But it still does not work... :-(
    – Mato
    Commented Aug 10, 2022 at 22:06
  • There is errors in you rules. I'v added and correct your rules and place them after part 2. Double check you real rules. If you wanna conntrack you must accept established also. But until it start work in first place, try without conntrack. Also i would like to see routing info from hosts. Check packets traversal with tcpdump on hosts interfaces.
    – gapsf
    Commented Aug 11, 2022 at 0:48
  • Routing info contains my main post. I changed iptables rules (3 mistakes). In tcpdump on X.X.X.X I see SYN TCP segments to X.X.X.X and SYN TCP segments to Y.Y.Y.Y (DST port 443) without any replies and with many TCP Retransmission segments. On Y.Y.Y.Y I do not see any from these segments...
    – Mato
    Commented Aug 13, 2022 at 22:13
  • So traffic from .x.x.x.x eth0 never reach y.y.y.y? What src address outgoing packets have on x.x.x.x? What ping y.y.y.y and traceroute y.y.y.y from x.x.x.x shows?
    – gapsf
    Commented Aug 14, 2022 at 4:24
  • Yes, it never reaches Y.Y.Y.Y. Src address is the original address of some client. For example public IP address of my desktop. Ping is OK, traceroute (ICMP) shows only the last node (Y.Y.Y.Y) other nodes on the route expired.
    – Mato
    Commented Aug 14, 2022 at 8:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .