===== WireGuard Port Forwarding ===== I am assuming that you have already setup WireGuard both on the internal and on the external server. See [[gentoo:wireguard|this]] page for more details on the topic. 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 [[[[gentoo:nft|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 for SSH (optional: port 8443 for HTTPS and port 80 for HTTP) * 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 443 get routed to 10.100.0.1 port 8443 (optional) * packets reaching external host on enp1s0 on port 80 get routed to 10.100.0.1 port 80 (optional) * return packets from the internal host get properly re-routed to the original sender out from enp1s0 **Note:** forwarding ports for HTTP and HTTPS is only required if you don't plan to host a reverse proxy on the external server, which is the recomended approach. See [[router:proxy_chain|this page]] for more details on chaining proxies. **Note:** see [[selfhost:nginx|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. **Note:** if you forward HTTPS, you **must** also forward HTTP (80), otherwise Let's Encrypt will be unable to renew your certificates. 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 }' optional #nft 'add rule ip wg prerouting iifname enp1s0 dnat to tcp dport map { 443 : 10.100.0.1 . 8443 }' optional 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 } }