WireGuard Site to Site Configuration

This article will cover how to set up two WireGuard peers in a Site to Site topology. This is the configuration you’d use when you want to connect a variety of computers at one site through a single WireGuard tunnel to a variety of computers at another site; like to connect the LAN (Local Area Network) of one office location to another, or to connect your office network to a bunch of servers you have set up in a cloud network.

In the specific scenario I’ll cover for this article, we’ll have “Host α” running WireGuard in one LAN, “Site A”, connected across the Internet to “Host β” running WireGuard in another LAN, “Site B”. Host α will be configured as the gateway from Site A to Site B, and Host β as the gateway from Site B to Site A. “Endpoint A” is an end-user workstation in Site A that will be able to connect through our WireGuard tunnel to an HTTP server on “Endpoint B” in Site B.

The diagram below illustrates this scenario:

WireGuard site-to-site scenario

In order to accomplish this, Host α needs to be able to connect through Site B’s firewall to UDP port 51822 on Host β, and Host β needs to be able to connect through Site A’s firewall to UDP port 51821 on Host α. From the perspective of Host β and the Internet, Host α’s IP address is 198.51.100.1, but from the perspective of Site A, Host α’s IP address is 192.168.1.1, and from the perspective of the WireGuard tunnel between Host α and Host β, Host α’s IP address will be 10.0.0.1.

Similarly, from the perspective of Host α and the Internet, Host β’s IP address is 203.0.113.2, and from the perspective of Site B, Host β’s IP address is 192.168.200.2, and from the perspective of the WireGuard tunnel between Host α and Host β, Host β’s IP address will be 10.0.0.2.

Endpoint A’s IP address will be 192.168.1.11, and Endpoint B’s IP address will be 192.168.200.22. With the WireGuard tunnel up and running, a user on Endpoint A will be able to access the HTTP server on Endpoint B simply by navigating to http://192.168.200.22 in her web browser.

This article will walk through how to install and configure WireGuard on Host α and Host β, as well as how to configure Host α and Host β to allow them to route packets between Site A and Site B. These are the steps we’ll follow:

Install WireGuard

Install WireGuard on both Host α 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 Keys

Next, generate two WireGuard keys, one for Host α, 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 Host A:

$ wg genkey > host-a.key
$ wg pubkey < host-a.key > host-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: host-a.key, host-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 host-a.key
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
$ cat host-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 Host α 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

Now let’s configure WireGuard. 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 = 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

And on Host β, create a new file at the same location 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 Host α
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
Endpoint = 198.51.100.1:51821
AllowedIPs = 192.168.1.0/24

Set each 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).

With a Site to Site topology, the configuration of the two WireGuard hosts usually will be perfectly symmetrical, with the [Interface] section for each host describing the host itself, and the [Peer] section describing the other host. Let’s go through each configuration setting:

Interface.PrivateKey

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

Interface.Address

This is the 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 host 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.

Also, with a Site to Site topology, this setting isn’t all that important — typically you want route packets through the 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 the host’s WireGuard port. The 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 Site to Site topology, this setting in Host α’s configuration file should match the port in the Peer.Endpoint setting of Host β’s configuration file, and vice versa.

Peer.PublicKey

This is the other host’s public key — replace this value with the actual public key you generated for the other host. On Host α, this should be Host β’s public key; and on Host β, this should be Host α’s public key.

Peer.Endpoint

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

In our example, Host β’s public IP address is 203.0.113.2 (and WireGuard is listening on port 51822 of Host β), so that’s the IP address (and port) we put in the Peer.Endpoint setting on Host α. And Host α’s public IP address is 198.51.100.1 (with WireGuard listening on port 51821), so that’s the address (and port) we put in the Peer.Endpoint setting on Host β.

Host α must be able to accept new UDP connections from the internet at this address and port (198.51.100.1:51821), and Host β similarly must be able to accept new UDP connections from the Internet at its address and port (203.0.113.2:51822).

Peer.AllowedIPs

This is the other site’s subnet block — replace this value with the actual block of IP addresses the other site uses. If the other host can route to multiple subnets within the other site, 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.

In our example, Site B’s subnet block is 192.168.200.0/24, so that’s the block we put in the Peer.AllowedIPs setting on Host α. And Site A’s subnet block is 192.168.1.0/24, so that’s the block we put in the Peer.AllowedIPs setting on Host β.

Tip

If you want to allow one (or both) of the WireGuard hosts itself to be able to access the other site, you need to add the WireGuard address of the host to the other site’s AllowedIPs setting.

For example, if you want to run curl on Host α in order to access Endpoint B’s webserver (or run ping on Host α in order to test its connection with Host β), add Host α’s WireGuard IP address (10.0.0.1) to the AllowedIPs setting on Host β:

AllowedIPs = 192.168.1.0/24, 10.0.0.1/32

You may then also have to add a route for this address to Site B, as described in the next section.

Configure Routing

If both WireGuard hosts are already set up as the Internet gateway for their site (or the gateway in their site to a subnet that includes the other site’s LAN), you don’t need to do anything extra for WireGuard; just skip on to the next section.

Otherwise, we need to do two things:

  1. Turn on IP forwarding on the WireGuard hosts

  2. Update the routing tables for each site

Since we’ll use wg-quick to bring the WireGuard tunnel up and down on Host α and Host β, the easiest way to make sure IP forwarding is on when the WireGuard tunnel is to have wg-quick do it for us when it brings the WireGuard tunnel up. Update the /etc/wireguard/wg0.conf file on Host α to add the following Interface.PreUp setting:

# /etc/wireguard/wg0.conf

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

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

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

And add the exact same setting to the /etc/wireguard/wg0.conf on Host β:

# /etc/wireguard/wg0.conf

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

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

# remote settings for Host α
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
Endpoint = 198.51.100.1:51821
AllowedIPs = 192.168.1.0/24

If you’re using IPv6 addresses for your WireGuard VPN, use this Interface.PreUp setting instead (on both hosts):

PreUp = sysctl -w net.ipv6.conf.all.forwarding=1

For the second thing, updating each site’s routing tables, unfortunately you can’t do via WireGuard config. You could configure each endpoint in both sites individually to route the traffic it generates destined for the other site through the WireGuard host in its own site — but the easiest thing to do is simply update the configuration of an existing gateway in each site to do that routing.

So for Site A, you want to update the gateway for the subnet that subsumes Site B’s subnet (192.168.200.0/24), which usually would be the default gateway for Site A (like if Site A is a small office, it’s probably the Internet router for the office). You want to add a route to this gateway to make it route Site B’s subnet (192.168.200.0/24) via Host α (192.168.1.1) on the Site A (LAN) side of the gateway.

If this gateway is a Linux box, run the ip route command on the gateway to check what (IPv4) routes it currently is using (for IPv6, run ip -6 route). On Site A, the result might look something like this:

$ ip route
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.100
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.128
default via 192.0.2.1 dev eth0

The example above shows the gateway having an IP address of 192.0.2.100 on its eth0 network device, and 192.168.1.128 on its eth1 device. The eth1 device is connected to the Site A subnet, 192.168.1.0/24.

So run the following command on the gateway to (temporarily) add a route to Site B through Host α on the eth1 device:

ip route add 192.168.200.0/24 via 192.168.1.1 dev eth1

Replace the subnet for Site B (192.168.200.0/24) with the actual Site B subnet you’re using, the IP address for Host α (192.168.1.1) with the actual Host α IP address you’re using, and the network device name (eth1) with the actual name of the device through which the gateway is connected to Site A.

Note that adding a route this way just adds it temporarily, until the gateway is restarted or reconfigured — if you test out the WireGuard tunnel and everything works out, you’ll want to make the route change permanent via whatever mechanism you regularly use to configure the gateway (like via networkd or netplan config files, or your own hand-built shell scripts, or some tool with a graphical user interface).

Similarly, check the routes used on Site B’s default gateway with ip route, and then run a command on it like the following on it to add a route to Site A through Host β:

ip route add 192.168.1.0/24 via 192.168.200.2 dev eth1

Start Up WireGuard

On both Host α 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
[#] sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
[#] 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 8921 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]: [#] sysctl -w net.ipv4.ip_forward=1
wg-quick[271288]: net.ipv4.ip_forward = 1
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 8921 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 B, start up a webserver on port 80 (or any other port, like 8080). If you don’t have a webserver handy, a dead-simple substitute is to use the http.server module of Python 3, like the following, running as root to listen on port 80:

$ sudo python3 -m http.server 80

Or run it as a regular user to listen on any port above 1023 (like port 8080):

$ python3 -m http.server 8080

The http.server module will serve the directory listing of and files in your current directory. A somewhat safer version of this is to create an empty directory in your current directory, and serve that instead:

$ mkdir dummydir && cd dummydir
$ python3 -m http.server 8080

On Endpoint A, use curl (or a regular webrowser) to request a page from the webserver running on Endpoint B port 80:

$ curl 192.168.200.22

Or if the webserver is running on port 8080 (or some other port), specify that port explicitly:

$ curl 192.168.200.22:8080

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 IP forwarding on one of your WireGuard hosts. Run this command on Host α and Host β to double check:

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

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

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

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

The next thing to do is to try to use traceroute or tracepath to see how ICMP packets are being routed between Endpoint A and Endpoint B. If we run tracepath 192.168.200.22 (Endpoint B’s IP address) from Endpoint A, we might see a result like this:

$ tracepath 192.168.200.22
1?: [LOCALHOST]                              pmtu 1500
1:  192.168.1.1                      0.411ms
2:  no reply
3:  no reply
4:  no reply
...

The above result indicates that our ICMP packets from Endpoint A only got as far as Host α. What we should see is something like this:

$ tracepath 192.168.200.22
1?: [LOCALHOST]                              pmtu 1500
1:  192.168.1.1                      0.411ms
2:  10.0.0.2                         3.832ms pmtu 1420
3:  192.168.200.22                   0.390ms reached
    Resume: pmtu 1420 hops 3 back 3

The above indicates our ICMP packets made it all the way to Endpoint B and back.

If the packets never even make it to Host α, that probably means that you need to work on the routing rules for Site A (see the Configure Routing section above). If the packets stop at Host α, probably either the tunnel from Host α to Host β wasn’t set up successfully, or Host β’s own firewall rules are blocking packets. If the packets stop at Host β, probably either the routing rules in Site B, or a firewall between Host β and Endpoint B, is to blame.

If you’re using iptables on your WireGuard hosts, you can double-check the hosts’ active firewall rules by running sudo iptables-save (see the Extra: Configure Firewall_ section below for some iptables rules suggestions). If you’re using nftables, you can get a similar list via sudo nft list ruleset. If you see nothing from either command, you have no firewall set up; if you see an error, you probably don’t have iptables or nftables installed.

If everything on Host α and Host β seem to be right, but packets still seem to be stopping at Host α, you likely have some other firewall rules or network configuration blocking the WireGuard tunnel itself from being set up. Try running sudo wg on both Host α 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: 192.168.1.0/24

If Host α had successfully connected to Host β, Host β’s display would include a “latest handshake” field under the peer line, as well as a “transfer” field indicating some data had been received. The above output indicates that Host α has not successfully connected yet.

On Host α, 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

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 Host α and Host β before the hosts are able to set up the WireGuard tunnel. If this is the case for you, you need to fiddle with your firewalls or other network configuration until they allow Host α to send UDP packets to Host β via the IP address and port configured in Host α’s Peer.Endpoint setting (in this example, it’s 203.0.113.2:51822), and vice versa.

Extra: Configure Firewall

If you already have some basic firewall rules for forwarded traffic set up on your WireGuard hosts, you don’t need to do anything else to your WireGuard configuration. If you don’t already have some basic rules in place, however, here are a few simple iptables rules that you can use to reject unexpected WireGuard traffic from accessing local sockets on the WireGuard hosts themselves, and to reject any WireGuard traffic from being forwarded to unexpected destinations within your sites.

I’ll go over these rules using Site B as an example, where for now we want to block any WireGuard traffic except to and from port 80 on Endpoint B. You can apply similar rules to Site A (adding additional --state NEW rules for each service in the site that you want to allow the other site to be able to initiate access to).

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

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

# firewall local host from wg peers
PreUp = iptables -A INPUT -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
PreUp = iptables -A INPUT -i wg0 -j REJECT
PostDown = iptables -D INPUT -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
PostDown = iptables -D INPUT -i 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 192.168.200.22 -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 192.168.200.22 -p tcp --dport 80 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j REJECT
# clamp mss of tcp connections forwarded to wg peers
PreUp = iptables -t mangle -A FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
PostDown = iptables -t mangle -D FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# remote settings for Host α
[Peer]
PublicKey = /TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=
Endpoint = 198.51.100.1:51821
AllowedIPs = 192.168.1.0/24

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

This updated configuration will cause wg-quick on Host β to add six new iptables rules before it brings up the WireGuard interface, and remove the same rules after it brings the interface down.

The first two rules (added to the INPUT chain) apply to traffic destined for Host β itself:

iptables -A INPUT -i wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i wg0 -j REJECT

The first rule will accept all packets for already-established connections (ie connections that Host β initiated) coming through the WireGuard tunnel; and the second rule will reject anything else coming through the tunnel destined for Host β.

The middle three rules (added to the FORWARD chain) apply to traffic forwarded through Host β to endpoints in Site B:

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

Similar to the rules for the INPUT chain, the first and third of the rules for the FORWARD chain allow already-established connections, but reject new connections.

The second rule, however, will allow new TCP connections to be forwarded to port 80 of Endpoint B (192.168.200.22 in Site B). If you want to allow access to a different port of Endpoint B other than port 80 (like 8080), specify that port instead of 80. If you want to allow access to multiple ports of Endpoint B, adjust the rule slightly to use the multiport match directive with the --dports flag (note the trailing s in --dports) — like, for example, to allow both port 80 and 443:

iptables -A FORWARD -i wg0 -m state --state NEW -d 192.168.200.22 -p tcp -m multiport --dports 80,443 -j ACCEPT

The very last rule (added to the mangle table’s FORWARD chain) applies to traffic forwarded out of Host β from the endpoints in Site B:

iptables -t mangle -A FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

It automatically adjusts the MSS (Maximum Segment Size) option of TCP SYN and SYN-ACK packets forwarded out its WireGuard interface, to account for the interface’s lower MTU (Maximum Transmission Unit) size. This helps endpoints in Site A, at the other end of the TCP connections, to optimize the size of the packets they send to Site B, avoiding packet fragmentation.

Whenever you make changes to these rules, make sure you make the exact same change to both the PreUp setting and its corresponding PostDown setting (the PreUp and PostDown rules should be the same, just with a -A flag for PreUp and -D flag for PostDown). And if you use IPv6 addresses in your WireGuard VPN, substitute the ip6tables command for iptables in everything above.

Also note that when you’re fiddling with PreUp and PostDown settings, it’s best to bring the interface down first, make your configuration changes, and then bring the interface back up again (rather than making the configuration changes while the interface is still up, and then trying to restart afterward). The reason is that most of the time, your “Up” and “Down” settings are meant to be symmetrical (like add a firewall rule with PreUp and remove it with PostDown) — so if you change a PostDown rule while your WireGuard interface is still running, you might inadvertently leave a stray firewall rule in place from the previous configuration version (which you’ll have to track down and clean up manually).

For tips on how to set up an iptables firewall under a more complicated site-to-site scenario, check out the “Site to Site” section of the WireGuard Access Control With Iptables guide.

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