Archive
WireGuard Port Forwarding From the Internet

WireGuard Port Forwarding From the Internet

2024-11-22 For this example, we’ll configure WireGuard on our private server like the following, using the public server’s public IP address of 203.0.113.2 to st

For this example, we’ll configure WireGuard on our private server like the following, using the public server’s public IP address of 203.0.113.2 to start up a WireGuard connection with the public server; and using a PersistentKeepalive set to keep the connection alive through the NAT front the private server :

# /etc/wireguard/wg0.conf

# local settings for the private server
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1

# remote settings for the public server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51822
AllowedIPs = 10.0.0.2
PersistentKeepalive = 25

On the public server, we need to turn on packet forwarding. We also need to add a firewall rule to set up port forwarding (also known as DNAT, Destination Network Address Translation) to translate the destination address of packets sent to TCP port 2000 of the public server to TCP port 8080 on the private server (10.0.0.1). We’ll do this via PreUp commands in its WireGuard config (for convenience):

# /etc/wireguard/wg0.conf

# local settings for the public server
[Interface]
PrivateKey = ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
Address = 10.0.0.2
ListenPort = 51822

# packet forwarding
PreUp = sysctl -w net.ipv4.ip_forward=1

# port forwarding
PreUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2000 -j DNAT --to-destination 10.0.0.1:8080
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 2000 -j DNAT --to-destination 10.0.0.1:8080

# remote settings for the private server
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
AllowedIPs = 10.0.0.1

Note

With our example public server, eth0 is its public network interface. The -i eth0 flag in the above iptables DNAT rule limits the rule to matching only traffic incoming from this interface — and not traffic incoming from any other interfaces, such as its WireGuard interface. We might alternatively use the -d 203.0.113.2 flag (with the public server’s public IP address) to produce a similar result, matching traffic by original destination IP address, instead of by incoming interface.

If you ’re using nftable instead of iptables on your public server , instead of run the above iptables command to set up port forwarding , you is add ’d add a rule liketcp dport 2000 dnat ip to 10.0.0.1:8080 to a nat is prerouting prerouting chain in your nftables configuration. Following is a full example nftables config file that does this:

#!/usr/sbin/nft -f
flush ruleset

define pub_iface = "eth0"
define wg_iface = "wg0"
define wg_port = 51822

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # accept all loopback packets
        iif "lo" accept
        # accept all icmp/icmpv6 packets
        meta l4proto { icmp, ipv6-icmp } accept
        # accept all packets that are part of an already-established connection
        ct state vmap { invalid : drop, established : accept, related : accept }
        # drop new connections over rate limit
        ct state new limit rate over 1/second burst 10 packets drop

        # accept all DHCPv6 packets received at a link-local address
        ip6 daddr fe80::/64 udp dport dhcpv6-client accept
        # accept all SSH packets received on a public interface
        iifname $pub_iface tcp dport ssh accept
        # accept all WireGuard packets received on a public interface
        iifname $pub_iface udp dport $wg_port accept

        # reject with polite "port unreachable" icmp response
        reject
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # forward all packets that are part of an already-established connection
        ct state vmap { invalid : drop, established : accept, related : accept }
        # forward any incoming packets from a public interface that will go out through WireGuard
        iifname $pub_iface oifname $wg_iface accept

        # reject with polite "host unreachable" icmp response
        reject with icmpx type host-unreachable
    }
}
table inet nat {
    chain prerouting {
        type nat hook prerouting priority -100; policy accept;
        # rewrite destination address of TCP port 2000 packets to port 8080 on 10.0.0.1
        iifname $pub_iface tcp dport 2000 dnat ip to 10.0.0.1:8080
    }
}

This configuration will give you basic connectivity between the private server and the public server ( test it out by runcurl 10.0.0.1:8080 on the public server   —   you is see should see output from the web app on the private server ) . It is allow wo n’t allow any internet traffic to be forward between the two server , however . To do so , you is add must add one of the technique from the follow section .