I'm trying to configure my Gentoo Linux as a router.
this is my configuration so far.
WAN NIC is enp3s0
and LAN NIC is enp1s0f0
accepting connections to ICMP, tcp ports 53, 22, 80, 443, 445, 5900 and udp ports 53,67,68 from LAN
accepting connection from SSH port 22 from WAN
these work great, what I failed to do is create port forwarding.
I am trying to set up that if a connection on port 222 comes in from WAN, to forward it to machine with ip address 192.168.1.2
on port 22
and this rule doesn't produce an error, but also doesn't allow me to connect.
this is my configuration:
table ip filter {
chain input {
type filter hook input priority filter; policy accept;
ct state { established, related } accept
iif "lo" accept
iif "enp1s0f0" tcp dport { 22, 53, 80, 443, 445, 5900 } counter packets 0 bytes 0 log accept
iif "enp3s0" tcp dport { 22 } counter packets 0 bytes 0 log accept
iif "enp1s0f0" udp dport { 53, 67, 68 } accept
iif "enp1s0f0" ip protocol icmp accept
counter packets 1 bytes 259 drop
}
chain output {
type filter hook output priority filter; policy accept;
ct state { established, related, new } accept
iif "lo" accept
}
chain forward {
type filter hook forward priority filter; policy accept;
iif "enp3s0" oif "enp1s0f0" ct state { established, related } accept
iif "enp1s0f0" oif "enp3s0" accept
iif "enp3s0" oif "enp1s0f0" counter packets 0 bytes 0 drop
}
chain postrouting {
type filter hook postrouting priority filter; policy accept;
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "enp3s0" masquerade
}
chain prerouting {
type nat hook prerouting priority 100; policy accept;
iif "enp3s0" tcp dport { 222 } dnat to 192.168.1.2:22 ### <- PORT FORWARDING RULE HERE
}
}
how can I correct this problem?
thank you.
Once the first packet of a new flow (thus state NEW) traverses the nat prerouting chain, dnat happens and with the new destination the packet is routed and traverses the filter forward chain.
Then this rule drops it:
A rule to allow this first packet (which is not in established state) is needed.
This could be inserted before the drop rule in a first naive way:
and because of the specific topology: the 192.168.1.0/24 LAN isn't routable so is not reachable by default from Internet, it would probably be good enough (technically the next hop router could cheat and reach 192.168.1.2:22 directly without NAT). But if the system wasn't doing any masquerade (and there was a routable LAN instead of 192.168.1.0/24) this would leave the service reachable directly.
There's actually a simpler and safer method which is also more generic in case some other ports are also dnat-ed and all such dnat rules are all to be allowed: add a rule that allows any packet that underwent a dnat transformation. It's more detailed in the equivalent iptables-extensions' conntrack match:
Just insert this instead in filter forward before the last drop rule:
or to be a bit more precise:
up to very precise:
This status can only appear because of a previous dnat rule done on a flow, so it validates the intent: accept.
Notes:
there is no need to use different tables for different hook types (filter and nat) as long as it's about the same family (ip here).
That's an habit inherited from iptables that might limit possibilities. For example, the scope of a set is a table. Using the same set between a filter chain and a nat chain requires them to be in the same table (where the set is defined). Sadly many examples even from the wiki still use naming conventions mimicing iptables.
while it doesn't matter here, the historical priority for nat prerouting isn't 100 but -100 (aka dstnat).
It would only matter if there were other tables also including nat prerouting chains or if iptables nat rules were used together (and in such case it would be advised to use -101 or -99 rather than exactly -100), to determine which rules take precedence.