It can occasionally be useful to expose a (TCP or UDP) port on a different port, without passing traffic on to a different host as is usually the case with port forwarding. In effect, changing the destination port of TCP or UDP traffic.
With Linux nftables, this is most easily done in a prerouting
chain.
Starting with a typical example nftables configuration:
#!/usr/bin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority 0; policy drop; ct state invalid drop ct state established accept ct state related accept iifname "lo" accept } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; } }
Add a prerouting chain with the correct priority:
table inet filter {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
}
}
Add a rule to this new chain which results in a redirect
action:
table inet filter { chain prerouting { type nat hook prerouting priority dstnat; policy accept; tcp dport 80 redirect to :22 } }
With the chain in place, to add the redirect rule programmatically, use something like:
# nft add rule inet filter prerouting tcp dport 80 redirect to :22
where inet filter
maps to the table
specification, and prerouting
is the name of the chain to which to add the rule.
With this in place, any packets arriving at TCP port 80 will, in your subsequent input
chain (and by the time they reach userspace), have a destination port of 22, thereby exposing your SSH server (listening on port 22) on the standard HTTP port (80) but subject to all normal tcp dport 22
conditions (and none of the tcp dport 80
ones) within the input
chain.
In the words of the nft
(8) man page:
The
redirect
statement is a special form of dnat which always translates the destination address to the local host’s one. It comes in handy if one only wants to alter the destination port of incoming traffic on different interfaces.
And yes, it works even with sysctl net.ipv4.ip_forward = 0
.