User Tools

This is an old revision of the document!


Port Forwarding

Using WireGuard for port-forwarding between an external, public accessible host, and an internal non-accessible host (like behind a CGNAT) is a less known topic and i will cover it here.

You will need to use NFTables, see here for more details to better understand the following.

Assumptions:

  • You have one internal host which is behind CGNAT, and is not accessible from the internet
  • You have one external host which has a public IP address
  • Both hosts have WireGuard setup according to the configuration above
  • Internal host expose port 22, 80 and 8443
  • External host needs to route those three ports to the internal host
  • The WireGuard subnet is 10.100.0.0/24 with internal host being 10.100.0.1 and external host 10.100.0.2
  • External host public network interface is called enp1s0
  • Both hosts WireGuard network iunterface is called wg0

What we need to do is create nftable rules to ensure that:

  • packets reaching external host on enp1s0 on port 2022 get routed to 10.100.0.1 port 22
  • packets reaching external host on enp1s0 on port 80 get routed to 10.100.0.1 port 80
  • packets reaching external host on enp1s0 on port 443 get routed to 10.100.0.1 port 8443
  • return packets from the internal host get properly re-routed to the original sender out from enp1s0

Note: see here on why i use port 8443 instead of port 443 on the internal server. This is to differentiate internal connections, which have lesser security, from external connections which get additional SSO layers.

What we need:

  • A dedicated table called wg
  • A prerouting chain to apply DNAT to incoming packes
  • Rules to route port 2022/80/443 to the wg tunnel ports
  • A postrouting chain to ensure that all reply packets are properly SNAT back to outside
  • Return masquerading rules to ensure the return packets get sent back out of enp1s0

Create the wg table:

nft add table ip wg

Create the base chains:

nft 'add chain ip wg prerouting { type nat hook prerouting priority -100 ; }'
nft 'add chain ip wg postrouting { type nat hook postrouting priority 100 ; }'

Create the in-bound rules:

# nft 'add rule ip wg prerouting iifname enp1s0 tcp dport 80 counter' # <<- optional for debugging purposes
# nft 'add rule ip wg prerouting iifname enp1s0 tcp dport 443 counter' # <<- optional for debugging purposes
# nft 'add rule ip wg prerouting iifname enp1s0 tcp dport 2022 counter' # <<- optional for debugging purposes
nft 'add rule ip wg prerouting iifname enp1s0 dnat to tcp dport map { 2022 : 10.100.0.1 . 22 }'
nft 'add rule ip wg prerouting iifname enp1s0 dnat to tcp dport map { 80 : 10.100.0.1 . 80 }'
nft 'add rule ip wg prerouting iifname enp1s0 dnat to tcp dport map { 443 : 10.100.0.1 . 8443 }'

Create the SNAT return rule (the counter rule is only for debugging, you can omit that rule):

# nft 'add rule ip wg postrouting ip daddr 10.100.0.1 counter' # <<- optional for debugging purposes
nft 'add rule ip wg postrouting ip daddr 10.100.0.1 masquerade'

This is the resulting NFTables setup:

nft list table wg
table ip wg {
        chain prerouting {
                type nat hook prerouting priority dstnat; policy accept;
                iifname "enp1s0" tcp dport 443 counter packets 100 bytes 5712
                iifname "enp1s0" tcp dport 2022 counter packets 0 bytes 0
                iifname "enp1s0" tcp dport 80 counter packets 0 bytes 0
                iifname "enp1s0" dnat ip to tcp dport map { 2022 : 10.100.0.1 . 22 }
                iifname "enp1s0" dnat ip to tcp dport map { 80 : 10.100.0.1 . 80 }
                iifname "enp1s0" dnat ip to tcp dport map { 443 : 10.100.0.1 . 8443 }
        }
 
        chain postrouting {
                type nat hook postrouting priority srcnat; policy accept;
                ip daddr 10.100.0.1 counter packets 390 bytes 25945
                ip daddr 10.100.0.1 masquerade
        }
}

This website uses technical cookies only. No information is shared with anybody or used in any way but provide the website in your browser.

More information