TTR Networks

Beginner · Setting up VyOS with NAT · section 3 of 7

Source NAT

✓ Lab-tested 17 October 2026 on VyOS 2026.10.16-0024-rolling

A source NAT rule has three parts: which packets it matches, where they're going, and what to rewrite them to.

  • outbound-interface name eth0: only traffic leaving via the WAN.
  • source address 10.0.0.0/24: only traffic from the NAT-LAN. This line matters more than it looks; see below.
  • translation address masquerade: use whatever address eth0 has right now. Masquerade suits a WAN address that might change (DHCP or PPPoE). With a fixed address, you can write translation address 203.0.113.2 instead.

After the commit, both hosts reach the server, and its log shows the difference: the NAT-LAN host arrives as 203.0.113.2 (R1's WAN address), and the public host still arrives as 198.51.100.2. show nat source translations shows the mapping R1 keeps for each connection, so it can rewrite the replies on the way back. Many private hosts can share the one public address because R1 changes source ports too when it has to (Port Address Translation (PAT), the usual form of NAT).

The classic mistake: no source match

It's tempting to write the rule with just the outbound interface. To see what happens, remove the source line and test from the public LAN again. The public host now appears on the internet as 203.0.113.2: R1 is NATing addresses that are already public. Everything still seems to work, but you've broken the reason for having public addresses: other people's logs and allow-lists see the wrong address, and anything that expects to be reached at 198.51.100.2 gets confused. Always match the private source range.

Stage 2: source NAT for the NAT-LAN
set nat source rule 100 description 'NAT-LAN to the internet via the WAN address'
set nat source rule 100 outbound-interface name 'eth0'
set nat source rule 100 source address '10.0.0.0/24'
set nat source rule 100 translation address 'masquerade'
  • Line 2: Only traffic leaving via the WAN.
  • Line 3: Only traffic from the private LAN.
  • Line 4: Rewrite the source to eth0's current address.
Both LANs, after source NAT (real output)
root@lanhost:~# curl -sS -m 5 http://192.0.2.10/
hello from server
root@pubhost:~# curl -sS -m 5 http://192.0.2.10/
hello from server
root@server:~# cat /tmp/http.log
203.0.113.2 - - [17/Oct/2026 00:53:01] "GET / HTTP/1.1" 200 -
198.51.100.2 - - [17/Oct/2026 00:53:01] "GET / HTTP/1.1" 200 -
  • Lines 1–2: The private host works now.
  • Line 6: It arrives as R1's WAN address…
  • Line 7: …while the public host still arrives as itself.
NAT rules and translations (real output)
vyos@R1:~$ show nat source rules
Rule    Source       Destination    Proto    Out-Int    Translation
------  -----------  -------------  -------  ---------  -------------
100     10.0.0.0/24  0.0.0.0/0      any      eth0       masquerade
        sport any    dport any
vyos@R1:~$ show nat source translations
Pre-NAT          Post-NAT           Proto    Timeout    Mark    Zone
---------------  -----------------  -------  ---------  ------  ------
10.0.0.10:46886  203.0.113.2:46886  tcp      116        0
  • Lines 4–5: The rule: private source, out of eth0, masquerade.
  • Line 9: One live connection: 10.0.0.10 port 46886 became 203.0.113.2 port 46886. The port was free, so it didn't need changing.
The mistake: a rule with no source match (real output)
root@pubhost:~# curl -sS -m 5 http://192.0.2.10/
hello from server
root@server:~# cat /tmp/http.log
203.0.113.2 - - [17/Oct/2026 00:53:22] "GET / HTTP/1.1" 200 -
vyos@R1:~$ show nat source rules
Rule    Source     Destination    Proto    Out-Int    Translation
------  ---------  -------------  -------  ---------  -------------
100     0.0.0.0/0  0.0.0.0/0      any      eth0       masquerade
        sport any  dport any
  • Line 4: The public host is now hidden behind the WAN address.
  • Line 8: Source 0.0.0.0/0: everything leaving eth0 is NATed, public addresses included.

Check your understanding

Why does the source NAT rule match 10.0.0.0/24 instead of everything leaving eth0?

  • So the public LAN keeps its own addresses on the internet
  • Because masquerade only works with a source match
  • To stop the router NATing its own traffic
  • It's required syntax