Is DNAT not possible without SNAT?

Hello Team,
I am a noob in VyOS platform, but got up in a P1 requirement in my organisation where we want to preserve the source client ip.
Here we are using VyOS as NAT router or the main frontend appliance holding a public IP.
This VyOS server accepts all INTERNET Traffic and performs DNAT to send this to INTERNAL HAPRoxy LoadBalancer sitting in a Private Kubernetes cluster.

The issue is HAPRoxy always gets VyOS outbound interface internal IP as source IP (as we perform SNAT to pass the request to private local network sitting behind VyOS)

My NAT configuration -

nat {
    destination {
        rule 100 {
            description "route 80,443 to haproxy Internal loadbalancer"
            destination {
                address 0.0.0.0/0
                port 80,443
            }
            inbound-interface eth0
            protocol tcp_udp
            translation {
                address 10.XXX.XX.XX
            }
        }
    }
    source {
        rule 100 {
            outbound-interface eth1
            source {
                address !10.0.0.0/8
            }
            translation {
                address masquerade
            }
        }
    }
}

if I remove the Source NAT section then the request gets Timed OUT

Can you please review this scenario and suggest the best way to overcome this blocker?
please suggest any different approach within VyOS?

According to your configuration, your internal hosts has an IP 10.x.x.x.x. If those devices needs to reach internet, at some point source NAT is needed, because private networks are not routed in the internet.

Thanks for the reply @n.fort
Actually the internal Hosts doesn’t use VyOS for NAT (or outbound connectivity).
They use other NAT medium for that.
In our Infra VyOS is only used for incoming external/Internet traffic to internal hosts.

Client → xyz.com → VyOS PublicIP → DNAT - SNAT → HAProxy → App

You don’t need sNAT , for a dNAT entry to work.
On 1st packet (from WAN to LAN), dNAT rule is used, this generates a conntrack entry, which is used for all subsequent packets (both in and out), belonging to connection.
As there is some other gateway on your network, you either have to hassle with route table on the client, or add a masquerade rule on LAN interface.
This masquerade rule should only kick in for port mapped packets, and will translate source addres to VyOS LAN address. Now the client return traffic is destined to VyOS local LAN IP address, and is automatically routed correct.

1 Like

Thanks @16again for the detailed explanation.
So by using my current config, I cannot pass/persist the actual client IP address to upstream services, right?

Hi All,
We are looking to keep client IP after Dnating the source address, is there any way to achive this on Vyos?
Thanks in advance

This isn’t a VyOS problem it’s the routing on the system behind it.
If VyOS leaves original source IP in tact, the internal machine will see a request coming from some public IP, and return traffic to its default gateway. Which isn’t VyOS.
A way around this is makeV yOS the default gateway, and use PBR rules to redirect traffic sinitiated internally to the current default gateway.

Hi @MHAnas , I propose you to describe a bit details. By default DNAT should not touch source IPs.
Let’s revise basic topology.

VyOS6 has IP 10.217.80.42 and trying to reach port 22 on 10.217.80.44 on VyOS7.
VyOS7 has configured DNAT which translate port 22 to IP 100.64.0.2 and port 22

set nat destination rule 10 destination port '22'
set nat destination rule 10 inbound-interface 'eth0'
set nat destination rule 10 protocol 'tcp_udp'
set nat destination rule 10 source
set nat destination rule 10 translation address '100.64.0.2'
set nat destination rule 10 translation port '22'

On VyOS8 we will receive packets with native source IP from VyOS6

vyos@vyos8# run monitor traffic interface eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
06:53:59.514044 IP 10.217.80.42.52276 > 100.64.0.2.ssh: Flags [S], seq 2522261910, win 64240, options [mss 1460,sackOK,TS val 1123317130 ecr 0,nop,wscale 6], length 0
06:53:59.514691 IP 100.64.0.2.ssh > 10.217.80.42.52276: Flags [S.], seq 1152887333, ack 2522261911, win 65160, options [mss 1460,sackOK,TS val 4070826786 ecr 1123317130,nop,wscale 6], length 0
06:53:59.516780 IP 10.217.80.42.52276 > 100.64.0.2.ssh: Flags [.], ack 1, win 1004, options [nop,nop,TS val 1123317133 ecr 4070826786], length 0
06:53:59.581682 IP 100.64.0.2.ssh > 10.217.80.42.52276: Flags [P.], seq 1:42, ack 1, win 1019, options [nop,nop,TS val 4070826854 ecr 1123317133], length 41
06:53:59.592642 IP 10.217.80.42.52276 > 100.64.0.2.ssh: Flags [.], ack 42, win 1004, options [nop,nop,TS val 1123317200 ecr 4070826854], length 0

Thanks @Dmitry & @16again for your response.

In our case VyOS accepts all INTERNET Traffic on eth0 which is configured with a Public IP.
if we did not perform the SNAT, the internal/private network/system behind VyOS will see a request coming from some public IP and it will not be accepted.

The VyOS NAT config can be seen in the question above.

Is there any way to preserve the Client IP with this architectural approach & configuration?

Sorry but what is your goal?
Do you want to see original SRC IP’s on haproxy site or internal SRC of the VyOS IP?
Does haproxy have some different gateway? Does this gateway also use some NAT?
Perhaps you have some firewall rules on VyOS site like “state established/invalid” and packets may marked as “invalid” and then dropped
Try to dump traffic on eth0 and check if ACK/PUSH_ACK packets are received from the client

DNAT itself doesn’t work as “reverse proxy”

Yes @Viacheslav our goal is to have the source client public IP at HAProxy end, so that we can pass it to the upstream services (under X-Forwarded-For header) for the record.
Haproxy is exposed with internal load Balancer in a kubernetes cluster.
Vyos is exposed to the internet with public ip, yes client requests are accepted at eth0 but as we perform the snat , we are unable to pass the original client ip to HAPROXY.
Nope we don’t have any such firewall.

If you use SNAT it is impossible to send original headers to haproxy
What is going without SNAT rules?

Without SNAT the request fails.
As the HAProxy is in private network & when it will see a request coming from some public IP and it will not be accepted.
With SNAT, HAProxy receives the VyOS SNAT’d IP (VyoS private IP) not real client IP.

1.4 rollings have native package haproxy
Maybe it will help somehow in your case sudo nano -c /etc/haproxy/haproxy.cfg
Just check that ports are not used by “sstp” or “API”
The second option it uses containers already in CLI