WireGuard Point to Site With Port Forwarding

Usually when you connect a remote endpoint to a local site, you want the endpoint to be able to access some resources (like a web app or a mail server) at the local site, and don’t need to allow hosts at the site to be able to initiate connections to the remote endpoint. But sometimes you want the reverse — for the hosts at the local site to be able to access a service (like a web app or database) on the remote endpoint.

This article will cover exactly how to do that: we’ll set up a remote endpoint as one WireGuard peer, and connect it to a second WireGuard peer at a local site; and forward a port on the second WireGuard peer with DNAT (Destination Network Address Translation) to allow other hosts at the local site to access the remote endpoint through this WireGuard tunnel.

This is the reverse of the way a WireGuard Point to Site Topology is usually used, but the WireGuard configuration for it is similar to the standard Point to Site Configuration — just with a few different firewall rules to enable port forwarding.

In the example scenario for this article, we’ll have the remote endpoint running a web server on port 80, as well as running WireGuard on port 51821. I’ll call this endpoint “Endpoint A”. We’ll have another host running WireGuard at our local site on port 51822, which I’ll call “Host β”, in “Site B”. And we’ll have an end-user workstation on the LAN (Local Area Network) at Site B, which I’ll call “Endpoint B”. This endpoint is not running WireGuard, but we still want it to be able to connect to the web server on Endpoint A.

The Internet is between Endpoint A and Site B. Endpoint A is behind NAT (Network Address Translation) and a firewall that allows only established connections through. Site B is also behind NAT and a firewall, but its NAT + firewall allows port 51822 from the Internet to be forwarded on to Host β.

The diagram below illustrates this scenario:

Wireguard point-to-site scenario

By connecting Endpoint A and Host β in a WireGuard VPN (Virtual Private Network), Endpoint B will be able to access Endpoint A as if the web server on Endpoint A was running on Host β instead. Host β has an IP address of 192.168.200.2 within Site B, so a user using Endpoint B will be able to access the web server on Endpoint A simply by navigating to http://192.168.200.2 in her web browser.

Take note of the IP addresses shown in the above diagram: In this scenario, Endpoint A’s IP address, from the perspective of the Internet, is 198.51.100.1, but from the perspective of its own LAN (Site A), it’s 192.168.1.11; and from the perspective of the WireGuard VPN that we’ll build, it’s 10.0.0.1. Endpoint B is not accessible from the Internet; but on its own LAN (Site B), its IP address is 192.168.200.22.

Host β’s IP address, from the perspective of the Internet, is 203.0.113.2, but from the perspective of its own LAN (Site B), it’s 192.168.200.2; and from the perspective of the WireGuard VPN we’ll build, it’s 10.0.0.2. And from the perspective of Endpoint B (or any other endpoints in Site B), Endpoint A’s packets will appear to come from Host β — so from the perspective of Endpoint B, Endpoint A’s IP address will be the same as the IP address for Host β in the Site B LAN: 192.168.200.2.

These are the steps we’ll follow for this scenario:

Install WireGuard

Install WireGuard on both Endpoint A and Host β by following the installation instructions for the appropriate platform on the WireGuard Installation page. You can verify that you’ve installed WireGuard successfully by running wg help on both hosts.

Generate WireGuard Keys

Next, generate two WireGuard keys, one for Endpoint A, and one for Host β. A WireGuard key (also known as a “key pair”) is composed of two parts, the “private key” (also known as the “secret key”), and the “public key”. The magic of this kind of crypto (known as “public-key cryptography”) is that it’s trivial to compute the public key if you know the private key, but practically impossible to compute the private key if all you know is the public key.

While you don’t have to generate the keys on the hosts, generating a host’s key on the host itself often is the best way to keep its private key secure — that way the private key never leaves the host. If you generate your keys outside of the host, be very careful with the private keys, as WireGuard’s security depends entirely on keeping the private keys a secret.

Run the following commands to generate a new key pair for Endpoint A:

$ wg genkey > endpoint-a.key
$ wg pubkey < endpoint-a.key > endpoint-a.pub

And similar commands to generate a new key pair for Host β:

$ wg genkey > host-b.key
$ wg pubkey < host-b.key > host-b.pub

This will generate four files: endpoint-a.key, endpoint-a.pub, host-b.key, and host-b.pub. The *.key files contain the private keys, and the *.pub files contain the public keys. The content of each will be 44 characters of Base64-encoded text:

$ cat endpoint-a.key
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
$ cat endpoint-a.pub
/TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=

$ cat host-b.key
ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
$ cat host-b.pub
fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=

You don’t actually need to keep these files around — the content of each will be used in the WireGuard configuration files we build for Endpoint A and Host β in the next sections. The private key for a host goes in the host’s own configuration file; and its public key goes in the configuration file of every other host you want to connect it to. Each end of a connection must be pre-configured with the other end’s public key in order for WireGuard to establish the connection.

Configure WireGuard on Endpoint A

Now let’s configure Endpoint A (the remote web server). On Endpoint A, create a new file at /etc/wireguard/wg0.conf with the following content:

# /etc/wireguard/wg0.conf

# local settings for Endpoint A
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
ListenPort = 51821

# remote settings for Host β
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51822
AllowedIPs = 192.168.200.0/24
PersistentKeepalive = 25

Set the file’s owner to root, and its permissions to 600 (owner can read+write; everyone else is denied access — the file contains the endpoint’s private key, which must be kept secret).

Let’s go through the configuration, setting-by-setting:

Interface.PrivateKey

This is Endpoint A’s private key — replace this value with the actual private key you generated for Endpoint A. This value is secret, and this is the only place where it should live.

Interface.Address

This is Endpoint A’s IP address inside the WireGuard VPN we’re building — replace this value with a suitable value for your network. You can use any address you want for this, but the address should be within the “Private-Use” IPv4 or “Unique-Local” IPv6 address space (like 10.0.0.0/8 — see RFC 6890 for all available address blocks), and should not collide with any other private subnets to which the endpoint itself or any of its peers can connect.

With a Point to Site topology, this setting in Endpoint A’s configuration file should match exactly the Peer.AllowedIPs setting in Host β’s configuration file (discussed in the next section).

This setting isn’t used by WireGuard directly — it’s only used by the wg-quick helper service when it sets up a virtual network interface for WireGuard. In order for network packets to be routed correctly to and from this endpoint when they’re outside of the WireGuard tunnel, they need to use the IP address you set here.

While you can configure multiple IP addresses for this setting, unless you have a special use-case, you should just use a single IP address (in the form of a /32 block with an IPv4 address, or a /64 block with an IPv6 address).

Interface.ListenPort

This is Endpoint A’s WireGuard port. Endpoint A must be able to receive UDP traffic for established connections on this port from the Internet (or wherever the traffic of the other WireGuard peers with which it will communicate comes from).

If you omit this setting, WireGuard will select a new random, unused port in the in the operating system’s ephemeral port range (which may range from 1024 to 65535, depending on operating system) every time it starts up.

Peer.PublicKey

This is Host β’s public key — replace this value with the actual public key you generated for Host β. This value is not secret; however, it is a globally-unique identifier for Host β.

Peer.Endpoint

This is Host β’s publicly-facing IP address (and port) — the IP address and port Endpoint A will use to connect over the Internet to Host β to set up the WireGuard tunnel. Replace this value with the actual IP address that you would normally use to connect to Host β from Endpoint A (and suffix it with the actual WireGuard port you’ll use for Host β).

Host β must be able to accept new UDP connections from the Internet at this address and port; and Endpoint A must be able to send UDP traffic over the Internet to it at this address and port.

Peer.AllowedIPs

This is Site B’s subnet block — replace this value with the actual block of IP addresses Site B uses. If Host β can route to multiple subnets within Site B, you can specify each block of IP addresses separated by commas (like 192.168.200.0/24,192.168.202.64/26,192.0.2.48/28) for this setting, or you can just specify this setting multiple times, one for each CIDR (Classless Inter-Domain Routing) block.

Peer.PersistentKeepalive

This is a number of seconds between keepalive packets. The default is 0, which turns off keepalive packets.

If you set this to a positive integer N, WireGuard will send a small keepalive packet to Host β when the WireGuard interface on Endpoint A starts up, and again after every N seconds. This will open up any stateful firewalls between Endpoint A and Host β that accept only established connections in Endpoint A’s direction.

We need this in our example scenario, which has firewalls in front of Endpoint A that accept inbound traffic only for established connections, to allow Host β to initiate new connections to Endpoint A when it forwards HTTP requests from Endpoint B.

Configure WireGuard on Host B

Now let’s configure Host β (the local site side of the connection). On Host β, create a new file at /etc/wireguard/wg0.conf with the following content:

# /etc/wireguard/wg0.conf

# local settings for Host β
[Interface]
PrivateKey = ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
Address = 10.0.0.2/32
ListenPort = 51822

# remote settings for Endpoint A
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
AllowedIPs = 10.0.0.1/32

Set the file’s owner to root, and its permissions to 600 (owner can read+write; everyone else is denied access — the file contains the host’s private key, which must be kept secret).

Let’s go through the configuration, setting-by-setting:

Interface.PrivateKey

This is Host β’s private key — replace this value with the actual private key you generated for Host β. This value is secret, and this is the only place where it should live.

Interface.Address

This is Host β’s IP address inside the WireGuard VPN we’re building — replace this value with a suitable value for your network. You can use any address you want for this, but the address should be within the “Private-Use” IPv4 or “Unique-Local” IPv6 address space (like 10.0.0.0/8 — see RFC 6890 for all available address blocks), and should not collide with any other private subnets to which the endpoint itself or any of its peers can connect.

This setting isn’t used by WireGuard directly — it’s only used by the wg-quick helper service when it sets up a virtual network interface for WireGuard. In order for network packets to be routed correctly to and from this host when they’re outside of the WireGuard tunnel, they need to use the IP address you set here.

In a Point to Site topology, this setting on the site’s WireGuard host isn’t very important — typically you want route packets through the site’s WireGuard host (and on to other endpoints in the site), not to the host itself. So you just need to make sure you pick an IP address that isn’t going to collide with any other IP addresses the host can route to.

While you can configure multiple IP addresses for this setting, unless you have a special use-case, you should just use a single IP address (in the form of a /32 block with an IPv4 address, or a /64 block with an IPv6 address).

Interface.ListenPort

This is Host β’s WireGuard port. Host β must be able to receive UDP traffic for new connections on this port from the Internet (or wherever the traffic of the other WireGuard peers with which it will communicate comes from).

With a Point to Site topology, this setting in Host β’s configuration file should be the same as the port in the Peer.Endpoint setting of Endpoint A’s configuration file (discussed in the section above).

Peer.PublicKey

This is Endpoint A’s public key — replace this value with the actual public key you generated for Endpoint A. This value is not secret; however, it is a globally-unique identifier for Endpoint A.

Peer.AllowedIPs

This is Endpoint A’s IP address inside the WireGuard VPN we’re building — replace this value with a suitable value for your network. You can use any address you want for this, but the address should be within the “Private-Use” IPv4 or “Unique-Local” IPv6 address space (like 10.0.0.0/8 — see RFC 6890 for all available address blocks), and should not collide with any other private subnets to which the endpoint itself or any of its peers can connect.

With a Point to Site topology, this setting in Host β’s configuration file should match exactly the Interface.Address setting in Endpoint A’s configuration file (discussed in the section above).

While you can configure multiple IP addresses for this setting, unless you have a special use-case, with a Point to Site topology you should just use a single IP address (in the form of a /32 block with an IPv4 address, or a /64 block with an IPv6 address).

Alternate Endpoint Configuration

You will notice that we omitted the Peer.Endpoint setting from Host β’s configuration file, even though we used it in Endpoint A’s configuration file. In this example, because Endpoint A is configured with Host β’s endpoint address — and to send keepalive packets to Host β every 25 seconds — we don’t need to configure Host β with Endpoint A’s endpoint address. WireGuard will figure out Endpoint A’s address automatically when it receives the first keepalive packet from Endpoint A.

In cases where your remote endpoint has a static IP address — and its firewall allows new connections on its WireGuard port — you can reverse these settings, and include a Peer.Endpoint setting on the site side, while omitting it from the point side. You can also omit the Peer.PersistentKeepalive from either side. If we did that in this example, this is what Endpoint A’s wg0.conf would look like:

# /etc/wireguard/wg0.conf

# local settings for Endpoint A
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
ListenPort = 51821

# remote settings for Host β
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
AllowedIPs = 192.168.200.0/24

And this is what Host β’s wg0.conf file would look like:

# /etc/wireguard/wg0.conf

# local settings for Host β
[Interface]
PrivateKey = ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
Address = 10.0.0.2/32
ListenPort = 51822

# remote settings for Endpoint A
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
Endpoint = 198.51.100.1:51821
AllowedIPs = 10.0.0.1/32

Configure Routing on Host B

Assuming Host β is a Linux host, we need to configure two things on it:

  1. Turn on packet forwarding

  2. Set up port forwarding with iptables

If you don’t have iptables installed on the host, install it now with your package manager (like sudo apt install iptables or sudo yum install iptables etc).

While there are other ways to set these two things up, the simplest way is to add PreUp commands for them in the [Interface] section of our WireGuard config file — wg-quick will execute these for us automatically when it starts the WireGuard interface up (and execute the PostDown commands when it shuts the interface down).

So update the /etc/wireguard/wg0.conf file on Host β to add the following Interface.PreUp and Interface.PostDown settings:

# /etc/wireguard/wg0.conf

# local settings for Host β
[Interface]
PrivateKey = ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
Address = 10.0.0.2/32
ListenPort = 51822

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

# port forwarding
PreUp = iptables -t nat -A PREROUTING -d 192.168.200.2 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
PostDown = iptables -t nat -D PREROUTING -d 192.168.200.2 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1

# remote settings for Endpoint A
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
AllowedIPs = 10.0.0.1/32

If you’re using IPv6 addresses for your WireGuard VPN, replace net.ipv4.ip_forward with net.ipv6.conf.all.forwarding, and iptables with ip6tables.

The first PreUp setting above makes sure that packet forwarding is turned on (this setting is global for the Linux kernel’s networking subsystem). The second PreUp setting configures iptables to forward packets sent to port 80 on Host β’s LAN address (192.168.200.2) over to port 80 on Endpoint A (10.0.0.1). Because we’re using Endpoint A’s WireGuard address as their new destination, these packets will be forwarded through the WireGuard tunnel between Host β and Endpoint A.

Note that we can forward packets to a different destination port by including the different port in the --to-destination value. For example, if we wanted to forward port 80 on Host β to port 8080 on Endpoint A, we’d do it like this:

PreUp = iptables -t nat -A PREROUTING -d 192.168.200.2 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1:8080

The PostDown command deletes the iptables rule when wg-quick brings the interface down. If you change the iptables rule in the PreUp command, make sure you make the corresponding change to the rule in the PostDown command (the two commands should be identical, except the PreUp command uses the -A flag, for --append, whereas the PostDown command uses the -D flag, for --delete).

Start Up WireGuard

On both Endpoint A and Host β, start the wg-quick service. On Linux with systemd, run the following commands:

$ sudo systemctl enable wg-quick@wg0.service
$ sudo systemctl start wg-quick@wg0.service

On systems without systemd, run this command instead:

$ sudo wg-quick up /etc/wireguard/wg0.conf

Either way, starting up the wg-quick service will set up a WireGuard network interface named wg0 on the host, and configure some routing rules to route packets destined for any IP address listed in the Peer.AllowedIPs setting(s) of the /etc/wireguard/wg0.conf file to go out the wg0 interface.

Note that the name wg0 is just the standard convention for your first WireGuard interface. You can create as many WireGuard interfaces as you like, and name them however you like. For example, you could create another configuration file named /etc/wireguard/mytunnel.conf, and start it up with the command wg-quick up /etc/wireguard/mytunnel.conf; this would create a new WireGuard interface named mytunnel.

But for now, if you ran wg-quick up directly, you’ll see output like the following:

$ sudo wg-quick up /etc/wireguard/wg0.conf
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.1/32 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.0.0.2/32 dev wg0

This output shows the routing rules that wg-quick set up for you automatically.

If you ran systemctl start instead, you can see the same output by running the journalctl tool, like so:

$ journalctl -u wg-quick@wg0.service
systemd[1]: Starting WireGuard via wg-quick(8) for wg0...
wg-quick[271288]: [#] ip link add wg0 type wireguard
wg-quick[271288]: [#] wg setconf wg0 /dev/fd/63
wg-quick[271288]: [#] ip -4 route add 10.0.0.1/32 dev wg0
wg-quick[271288]: [#] ip link set mtu 1420 up dev wg0
wg-quick[271288]: [#] ip -4 address add 10.0.0.2/32 dev wg0
systemd[1]: Started WireGuard via wg-quick(8) for wg0.

Test Out the Tunnel

On Endpoint A, if you haven’t already, start up the web server on port 80. On Endpoint B, use curl (or any other browser) to request a page from Endpoint A:

$ curl 192.168.200.2

If you see any HTML output from this, your WireGuard tunnel works!

Basic Troubleshooting

If curl hangs, or you see an error like like Connection refused or No route to host, you may have neglected to turn on packet forwarding for Host β. Run this command on Host β to double check:

$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

Or, if you’re using IPv6 addresses, run the IPv6 variant:

$ sysctl net.ipv6.conf.all.forwarding
net.ipv6.conf.all.forwarding = 0

If the result is 0, you need to turn on packet forwarding (see the Configure Routing on Host B section above). If the result is 1, packet forwarding is on.

The next thing to check is to make sure Host β is performing port forwarding for Endpoint A. Run this command on Host β to list out all your iptables tables rules:

$ sudo iptables-save -c

If you don’t have any iptables rules in place, the result will be blank. What you should see instead is something like this:

$ sudo iptables-save -c
# Generated ...
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [12:123]
:POSTROUTING ACCEPT [34:567]
[21:432] -A PREROUTING -d 192.168.200.2/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.1
COMMIT
# Completed ...

In particular, you should see this rule under the *nat table:

[21:432] -A PREROUTING -d 192.168.200.2/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.1

If you don’t have this rule, Host β isn’t going to be able to forward HTTP requests from Endpoint B to Endpoint A. Try following the Configure Routing on Host B section above to set up port forwarding.

The numbers in brackets (like [21:432]) are the packet and byte count processed by each rule. If you see [0:0] for the DNAT rule, it means no packets have matched it. If that’s the case, you likely have some other firewall rules or network configuration blocking packets sent to port 80 on Host β.

Otherwise, try running sudo wg on both Endpoint A and Host β, to see if the WireGuard interface on both is up and configured as you expect.

On Host β, you’ll probably see output like this:

$ sudo wg
interface: wg0
  public key: fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
  private key: (hidden)
  listening port: 51822

peer: /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
  allowed ips: 10.0.0.1/32

If Endpoint A had successfully connected, this display would include a “latest handshake” field for the peer, as well as a “transfer” field indicating some data had been received. The above output indicates that Endpoint A has not successfully connected yet.

On Endpoint A, you’ll probably see output like this:

$ sudo wg
interface: wg0
  public key: /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
  private key: (hidden)
  listening port: 51821

peer: fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
  endpoint: 203.0.113.2:51822
  allowed ips: 192.168.200.0/24
  transfer: 0 B received, 123 B sent
  persistent keepalive: every 25 seconds

If the “transfer” field indicates some data has been sent to Host β, but none has been received (as it does above), it likely means packets are being blocked or misdirected somewhere between Endpoint A and Host β. If this is the case for you, you need to fiddle with your firewalls or other network configuration until they allow Endpoint A to send UDP packets to Host β via the IP address and port configured in Endpoint A’s Peer.Endpoint setting (in this example, it’s 203.0.113.2:51822).

Extra: Configure Firewall on Host B

As long as Site B is not configured to use Host β as a gateway, the only traffic that will be routed successfully between Site B and our WireGuard network will be packets sent to and responding from Endpoint A’s port 80. But as an extra security measure, you may want to add some additional firewall rules to Host β to prevent traffic from your WireGuard network from accessing any services on Host β itself (and if you add additional WireGuard peers to Host β’s network, to prevent those peers from connecting to one another through Host β).

Here are a few simple iptables rules that you can use to reject WireGuard traffic from accessing local services on Host β itself, and to reject any forwarded WireGuard traffic from being used to make new connections to anything other than port 80 of Endpoint A. First, bring down the WireGuard interface on Host β with wg-quick or systemctl:

$ sudo wg-quick down /etc/wireguard/wg0.conf
$ # or alternately:
$ sudo systemctl stop wg-quick@wg0.service

Then edit the /etc/wireguard/wg0.conf file to add several new Interface.PreUp and Interface.PostDown settings, like the following:

# /etc/wireguard/wg0.conf

# local settings for Host β
[Interface]
PrivateKey = ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBFA=
Address = 10.0.0.2/32
ListenPort = 51822

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

# port forwarding
PreUp = iptables -t nat -A PREROUTING -d 192.168.200.2 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1
PostDown = iptables -t nat -D PREROUTING -d 192.168.200.2 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1

# firewall local host from wg peers
PreUp = iptables -A INPUT -i wg0 -j REJECT
PreUp = iptables -A OUTPUT -o wg0 -j REJECT
PostDown = iptables -D INPUT -i wg0 -j REJECT
PostDown = iptables -D OUTPUT -o wg0 -j REJECT

# firewall other hosts from wg peers
PreUp = iptables -A FORWARD -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
PreUp = iptables -A FORWARD -i wg0 -m state --state NEW -d 10.0.0.1 -p tcp --dport 80 -j ACCEPT
PreUp = iptables -A FORWARD -i wg0 -j REJECT
PostDown = iptables -D FORWARD -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -m state --state NEW -d 10.0.0.1 -p tcp --dport 80 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j REJECT

# remote settings for Endpoint A
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
AllowedIPs = 10.0.0.1/32

Then bring the interface back up again with wg-quick or systemctl:

$ sudo wg-quick up /etc/wireguard/wg0.conf
$ # or alternately:
$ sudo systemctl start wg-quick@wg0.service

The first set of the new PreUp firewall commands will prevent any WireGuard peers from connecting directly to services on Host β (and vice versa):

PreUp = iptables -A INPUT -i wg0 -j REJECT
PreUp = iptables -A OUTPUT -o wg0 -j REJECT

The second set of PreUp firewall commands will prevent Host β from forwarding any connections to and from its WireGuard peers, other than connections related to Endpoint A’s port 80:

PreUp = iptables -A FORWARD -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
PreUp = iptables -A FORWARD -i wg0 -m state --state NEW -d 10.0.0.1 -p tcp --dport 80 -j ACCEPT
PreUp = iptables -A FORWARD -i wg0 -j REJECT

For a guide to setting up a more complicated iptables firewall, check out the “Point to Site” section of the WireGuard Access Control With Iptables article.

To use UFW to set up a firewall similar to the firewall described in this article, see the “Extra: Port Forwarding on Host β” section of the How to Use WireGuard with UFW guide; to use firewalld, see the “Extra: Port Forwarding on Host β” section of the How to Use WireGuard with Firewalld guide; or to use nftables, see the “Point to Site With Port Forwarding” section of the How to Use WireGuard With Nftables guide.