Well although rate limiting is one way of lessing the problem, it isn't a solution to my mind, your log still gets filled with hack attempts.
Its worth though going into the source of the problem, in prerouting an external port is forwarded to and internal machine on port 22. Now the problem is when that packet leaves prerouting, and enters the main input/forward loop its going to port 22, so you have to let it through. Hence the push to open port 22, but then port 22 as well as the other port get let through.
Ideally what I'd do is when the packet is dnat in prerouting, that it is also marked, and that mark is what is allowed through. Then you don't need to open port 22.
A half way solution though is enabling port 22 in the forward rules, but not in the input rules. Then only forwarded packets are let through, and the only real place that the forward packets from the WAN originate from is the DNAT for ssh.
Anyway hassle for me, is things like this really should be done with testing. Whilst I used to previously log into my home network remotly alot, change in life means I don't do it any more. So not easy for me to test remotely.
But it still on my list of things to do, really just to tidy up the nftable page. I'd prefer it shows a very good example of routing, rather than an example with some holes in. Think this is part of the hassle of firewalls, configuring them to do exactly what you want, is exceptionally difficult in iptables, and far from simple in nftables.
Think to my mind thats why its good to get other involved in discussions on nftables, its only when people try it, they can see how it compares with iptables, and how to impliment things. Really its expsure to nftables, that will make people move to them ...
So solution something like:
table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
tcp dport 12345 dnat to 192.168.2.111:ssh meta mark set 76
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
}
}
table inet filter {
chain forward {
type filter hook forward priority 0; policy drop;
ct state { related, established } accept
ct state invalid drop
iiftype != ppp accept
meta mark 76 accept
iiftype ppp drop
}
}
But this is totally untested - just a brain dump.