The forward firewall
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:
- Accept replies to existing connections (
established,related), and dropinvalidpackets. - Accept anything from either LAN. Outbound traffic, and the replies to it, just work.
- 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. - 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.
- Accept ICMP, so ping and path MTU discovery keep working.
- 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.
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.
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.
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