TTR Networks

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

The forward firewall

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

NAT isn't a firewall. The NAT-LAN was protected by the lack of a route to it, not by any policy. The public LAN had no protection at all, and once you add port forwards, those are open to everyone too. The protection has to come from a firewall.

VyOS's firewall ipv4 forward filter applies to traffic passing through the router (unlike the input filter, which protects the router itself; see the VyOS BGP lesson). A sensible starting policy:

  1. Accept replies to existing connections (established, related), and drop invalid packets.
  2. Accept anything from either LAN. Outbound traffic, and the replies to it, just work.
  3. Accept connections that destination NAT has rewritten (connection-status nat destination). Every port forward you add is then allowed automatically, with no second rule to keep in sync.
  4. Accept web traffic (TCP 80 and 443) to the public LAN, because that's what those servers are for. Add more rules here as you add services.
  5. Accept ICMP, so ping and path MTU discovery keep working.
  6. Drop everything else.

After the commit, test again from the server. The public web server and the port forward still work, ping still works, and the database on 5432 now times out. Both LANs can still reach out. The counters show each rule doing its job, and the default drop catching the database attempts.

If R1 gets IPv6 (and most ISPs now provide it), there's usually no NAT at all for IPv6: every host has a public address. That makes a forward firewall for IPv6 essential, not optional. Write the same policy under firewall ipv6 forward filter.

Stage 4: forward firewall
set firewall ipv4 forward filter default-action 'drop'
set firewall ipv4 forward filter rule 10 action 'accept'
set firewall ipv4 forward filter rule 10 state 'established'
set firewall ipv4 forward filter rule 10 state 'related'
set firewall ipv4 forward filter rule 20 action 'drop'
set firewall ipv4 forward filter rule 20 state 'invalid'
set firewall ipv4 forward filter rule 30 action 'accept'
set firewall ipv4 forward filter rule 30 description 'Anything from our LANs may go out'
set firewall ipv4 forward filter rule 30 inbound-interface name 'eth1'
set firewall ipv4 forward filter rule 31 action 'accept'
set firewall ipv4 forward filter rule 31 inbound-interface name 'eth2'
set firewall ipv4 forward filter rule 40 action 'accept'
set firewall ipv4 forward filter rule 40 description 'Port forwards made by destination NAT'
set firewall ipv4 forward filter rule 40 connection-status nat 'destination'
set firewall ipv4 forward filter rule 50 action 'accept'
set firewall ipv4 forward filter rule 50 description 'Web servers on the public LAN'
set firewall ipv4 forward filter rule 50 outbound-interface name 'eth2'
set firewall ipv4 forward filter rule 50 protocol 'tcp'
set firewall ipv4 forward filter rule 50 destination port '80,443'
set firewall ipv4 forward filter rule 60 action 'accept'
set firewall ipv4 forward filter rule 60 protocol 'icmp'
  • Line 1: Anything passing through R1 that isn't allowed below is dropped.
  • Lines 2–6: Replies to existing connections, and junk that matches no connection.
  • Lines 7–11: Anything from either LAN may go out.
  • Lines 12–14: Every port forward, automatically.
  • Lines 15–19: Only web traffic to the public LAN's servers.
  • Lines 20–21: ICMP.
Testing again (real output)
root@server:~# curl -sS -m 5 http://198.51.100.2/
hello from pubhost
root@server:~# nc -zv -w 3 198.51.100.2 5432
nc: connect to 198.51.100.2 port 5432 (tcp) timed out: Operation now in progress
root@server:~# curl -sS -m 5 http://203.0.113.2:8080/
hello from lanhost
root@server:~# ping -c 2 -W 2 198.51.100.2
PING 198.51.100.2 (198.51.100.2) 56(84) bytes of data.
64 bytes from 198.51.100.2: icmp_seq=1 ttl=62 time=1.15 ms
64 bytes from 198.51.100.2: icmp_seq=2 ttl=62 time=2.95 ms

--- 198.51.100.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1006ms
rtt min/avg/max/mdev = 1.148/2.048/2.949/0.900 ms
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
  • Lines 1–2: Public web server: still open, on purpose.
  • Lines 3–4: The database: now dropped.
  • Lines 5–6: The port forward: still works.
  • Lines 15–18: Both LANs can still reach out.
Firewall counters (real output)
vyos@R1:~$ show firewall ipv4 forward filter
Ruleset Information

---------------------------------
ipv4 Firewall "forward filter"

Rule     Action    Protocol      Packets    Bytes  Conditions
-------  --------  ----------  ---------  -------  --------------------------------------------
10       accept    all                39     3274  ct state { established, related }  accept
20       drop      all                 0        0  ct state invalid
30       accept    all                 1       60  iifname "eth1"  accept
31       accept    all                 1       60  iifname "eth2"  accept
40       accept    all                 1       60  ct status dnat  accept
50       accept    tcp                 1       60  tcp dport { 80, 443 } oifname "eth2"  accept
60       accept    icmp                1       84  meta l4proto icmp  accept
default  drop      all                 3      180  comment "FWD-filter default-action "
  • Line 9: Most packets are replies on established connections.
  • Line 13: The port forward's connection.
  • Line 14: The web request to the public LAN.
  • Line 16: Everything else, including the database attempts (each retry counts).

Check your understanding

Before the firewall, why couldn't the internet reach hosts on the NAT-LAN directly?

  • Because NAT blocks inbound connections
  • Because there's no route to 10.0.0.0/24 on the internet
  • Because R1 had an input firewall
  • Because of masquerade