Infrastructure Series -- Wireguard Site to Site Tunnel

Infrastructure Post 1: Making a site to site tunnel for access behind a well structure firewall and obscufation of home hosting

Obligatory shill of blog stream post: Phaselockedloopable- PLL's continued exploration of networking, self-hosting and decoupling from big tech

As always check for updates in the second post :wink:

What is a site to site Tunnel

The main aim of a site-to-site VPN is to securely connect two locations through gateway hardware; in my case I am using Raspberry Pi’s (running Manjaro/Arch) and a coreboot Protectli (running OPNsense). Site-to-site VPNs are often used in WANs to connect the LANs of separate branches or offices without the need for individual VPN software on each device. This is the business application. However, we can also use them to host on home connections where this is typically a violation of terms of service and one is usually behind CG-NAT. This will fully bypass those protections.

The main principal advantages I see hosting through a site to site tunnel are the following:

  • Security- Site-to-site VPN security is the most important benefit, as most VPN protocols will ensure all traffic is encrypted in transit through the VPN tunnel. The site-to-site VPN tunnel only allows traffic from one end to the other, blocking any attempts to intercept the traffic from the outside. All traffic must be signed by a digital certificate, and to get authenticated, a public key infrastructure (PKI) must be deployed. Each VPN has their own method of handling this.
  • Scalability - When compared to a traditional VPN, a benefit of a site-to-site VPN is its scalability. Rather than needing to ensure each system is running VPN client software as if it were on a remote access VPN, a site-to-site VPN only requires a VPN gateway at each location (say your home and a Linode server :wink: ). This makes it easy to add a new site or add multiple sites tied to one location of hosting. Say you want a server in Toronto and a server location in the UK. You can connect one location through one gateway through two site to site VPNs and forward the traffic hosted through both.
  • Obscufation of main location IPs- Obviously through the use of clever routing and the use of an NGINX Proxy-passing reverse proxy we can hide the IP of say our homes from those whom wish to whois us or find our servers IP and location

What is the best protocol?

In my humble opinion, I submit the not yet fully audited wireguard protocol.

Wireguard is a lightweight protocol. Its remarkably efficient due to its reliance on elliptic curves. Its also quite a bit more secure. It uses ChaCha20 for symmetric encryption, authenticated with Poly1305, using RFC7539’s AEAD construction and Curve25519 for ECDH.

So you guys probably know me as more of a math, scientistic engineer type but I can be an absolute crypto-nut. AES is simply not as fast as chacha20. We dont need AES-NI to take advantage of it either which highly benefits phones and other ARM based systems without it. Hence why I can use it on the Pi’s and why I use it as the main encryption standard in my certificates.

You see on a general-purpose 64 bit CPU without dedicated instructions such as AES-NI, ChaCha20 blows AES out of the water. The reason for this is the fact that ChaCha20 is based on the ARX (Addition-Rotation-XOR) method, which are CPU friendly instructions for all intents and purposes. (if you have been through a microprocessors class or programmed assembly you understand why this is). AES does have its own optimizations and lookup tables but ChaCha20 does surpass it when it comes to performance. Additionally, those optimizations with AES can make it vulnerable to attacks (lookup cache timing attacks). The reason we have AES-NI is to prevent that. But what if we did not need AES-NI and had all the advantages. Sounds too good to be true but it is true. ChaCha20 is pretty nice. That said it is young. It has not had the auditing AES has had and these come as a risk in ways for some. So keep that in mind before using wireguard if that sort of thing concerns you.

Heres a blog comparison of most of the common symmetric methods of encrypting.

How do I set it up?

To setup wireguard first you must understand its not a client server VPN setup. Both sides are a peer so in essence you need to setup private and public keys for each side. Technically you will derive the public key from the private key. In fact this is the first thing I suggest you do after installing the requisite tools. Please see your distribution documentation for setting up wireguard .

On your site A side you should run the following commands:

$ umask 077
$ wg genkey > siteApriv
$ wg pubkey < siteApriv > siteApub

On your site B side you should run the following commands:

$ umask 077
$ wg genkey > siteBpriv
$ wg pubkey < siteBpriv > siteBpub

I took the optional step of generating a preshared key which gives wireguard quantum resilience or so they claim:

wg genpsk > site_A-site_B.psk

you will need to keep this for configuration on both sides later

Now you have both sets of keys. Keep the private keys somewhere safe. Its time to set up the respective site configurations. I am going to provide mine with some information missing in the hope that it is instructionally useful. It is by no means the absolute way to set this up.

Site A wg0.conf:

Address    = <Private IP4 Address of Choice>/24, <Link Local IP6 Address of Choice>/64
PrivateKey = <Private Key of  Site A>
ListenPort = <PORT#- Keep for site B>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -D FORWARD -o %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

### Begin Site B ###
PublicKey = <Public Key of  Site B>
PresharedKey = <PSK you generated (optional)>
AllowedIPs =<Subnet CIDR of wireguard tunnel>/24, <Prefix CIDR of wireguard tunnel>/64, <Subnet CIDR of site B's network to be accesed>/24
PersistentKeepalive = 25
### End Site B ###

Site B wg0.conf:

PrivateKey = <Private Key of  Site B>
Address = <Private IP4 Address of Choice>/24, <Link Local IP6 Address of Choice>/64
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -D FORWARD -o %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = <PORT#>

### Begin Site A###
PublicKey = <Public Key of  Site A>
PresharedKey = <PSK you generated (optional)>
Endpoint = <Public IP4 Site A>
AllowedIPs =<Subnet CIDR of wireguard tunnel>/24, <Prefix CIDR of wireguard tunnel>/64
### End Site A ###

So it would be helpful to all to understand my mapping here.
Site A is my NGINX reverse proxy on the Linode. It serves as a frontend. I want it to be able to access my home network but I dont need my home network to access all the networks that linode has within it (or virtual networks). So the ENDPOINT is mapped on the site B end. The site A end will lack this however it will gain the inclusion of an extra allowed subnet to allow the mapping of my internal subnet outward to its wireguard subnet. Once this is complete the Linode and my Internal network will be able to talk (in theory there are a few more things to do).

So in essence your confs will take the perspective of Site A is a host. It has a peer of site B. It must make a handshake that direction. Site B is a host and it has a peer of site A. It must make the hand shake that direction. They kind of meet in the middle, which makes sense given the type of encryption used. Wiregurd is great for encryption it’s not to potentially great on the privacy end of things nor was it designed for this purpose specifically.

The most important thing you must do before you setup one of these. MAP YOUR NETWORK. Understand what you are doing so you can apply it conceptually. I have a tendency to wild wild west it. You absolutely will have the most massive headache if you attempt it the way I do.

Now that you have your proper configuration on both sites run the config through wg-quick via the command wg-quick up <location of wg0.conf> . This will bring the interfaces up on both sides. You can verify its up by typing in wg. The sites will not be able to access each other yet so we need to take a few more steps.

Now what do we need to finalize this? The IP tables rules will not work if you do not have IP forwarding enabled on site A and site B. Now this usually can be distribution specific. The main values you want set in your systemctl conf are the following


This will allow forwarding and masquerading that you need from the IP rules.

Now that we have the wireguard setup we can use our firewall or route of choice to setup and additional gateway. This gateway will have the H, S and U flags. This is because the gateway is not on the router. We will not be checking if its online etc. It will always allow the routes we define to it.

OPNSense makes it easy to add the LAN gateway. In anycase I will not show the specifics as your software could differ but this should visually help. You want the Gateway IP to be the IP of whatever device is Site B or the WG-Gateway In my case it is the .5 and ::5 device. It is helpful if they have a static mapping in your DHCP server so that their IP doesnt change but this is entirely up to you. wireguard is dynamic IP tolerant.

The last thing you need to add is a static route.

Pretty easy but it will again differ from software to software. The “jist” is that you want the wireguard subnet to be routed in its entirety to and from that gateway so that access can be established and mapped by your router without the need to add firewall rules. On the protectli with OPNsense there is one more step I had to take but pfsense doesnt need this step and neither do most other common firewalls

Now I suggest for immediate effects that you reboot all devices (site A, site B and the site B router). That should bring everything up properly and access will be established between sites between all devices at each site.

( I will update this as I go. I just wanted to share some knowledge so enjoy)



Links to Infrastructure Series and Other Resources

Blog: Phaselockedloopable- PLL’s continued exploration of networking, self-hosting and decoupling from big tech

Phaselockedloopable- PLL’s continued exploration of networking, self-hosting and decoupling from big tech

Series 1: Native Dual Stack IP4+IP6

Infrastructure Series – Native Dual Stack IP4+IP6

Series 2: Wireguard Site to Site Tunnel

Infrastructure Series – Wireguard Site to Site Tunnel

Series 3: Recursive DNS and Adblocking DNS over TLS w/NGINX

Infrastructure Series – Recursive DNS and Adblocking DNS over TLS w/NGINX

Series 4: NGINX Reverse Proxy and Hardening SSL

Infrastructure Series – NGINX Reverse Proxy and Hardening SSL

Series 5: Taking DNS One Step Further - Full DNS Server infrastructure

Infrastructure Series – Taking DNS One Step Further - Full DNS Server infrastructure

Series 6: HTTP(S) Security Headers! You should use them!

Infrastructure Series – HTTP(S) Security Headers! You should use them! [NGINX]

Series 7: Use NGINX to inject CSS themes

Infrastructure Series – Use NGINX to inject CSS themes


Setting up a YubiKey Properly – One Key to rule them ALL!

Series 9: Infrastructure Series: BIND9 Authoritative DNS Guide “Please See Me Edition”

Infrastructure Series: BIND9 Authoritative DNS Guide “Please See Me Edition”

Buy me a crypto-beer

If you found this guide helpful you can donate Monero or Bitcoin to me at the following address in my User Card Profile



What I would add is that you don’t need “path to wg0.conf,” but you can simply

wg-quick up wg0

… if your wg0.conf is in /etc/wireguard. Another thing I would add is to

chmod 700 /etc/wireguard
chown -R root:root /etc/wireguard
chmod 600 /etc/wireguard/wg0.conf

Doing so will prevent anyone without root access from taking a peak in your wg0.conf file.

Another thing to note is that Wireguard is not necessarily more secure (OpenVPN supports the same encryption algorithms for the most part), however, due to the code base being WAY smaller than OpenVPN, Wireguard has less attack surface, which means less exploits. Also, Wireguard doesn’t have a way to negotiate encryption algorithms, hashing and other such things, like OpenVPN and IPSec do, so Wireguard is not vulnerable to any downgrade attacks.

I’ve been using Wireguard on lots of devices, with the most “battle tested” being my Pi 4 2gb which is a gateway for a DVR to upload camera footage to an offsite backup storage on the other end of the tunnel. I’ve also tested it to my workplace as a replacement for OpenVPN (due to the speed and easy split-tunnel setup in the client configuration). I don’t intend it to use full time in production, but I’m thinking of making a site to site VPN between our office and data center (and redirect all traffic to the data center). But that’s just me messing around while I still can.


This tutorial if I followed it from the beginning would have helped me a lot. Had to go to the creator of this tut to find help. All the tutorials i tried across the internet are shit. Heim aka @PhaseLockedLoop knows what he is doing


Honestly, it was giving me such a headache trying to figure wg out. This tutorial is good. and the discord call helped too! :stuck_out_tongue:


I’m glad it worked out for you I think the principal problem with the documentation on the wire guard website is that it has this general assumption that you already know what you’re doing when it comes to VPN so adapting that knowledge and groking It out shouldn’t be a terrible difficulty for someone like that.

But because they don’t really talk about the actual structure of the VPN as much on that particular tutorial page I think there’s some details missing out and it leaves much to be desired

It’s not wrong It definitely tells you how to set it up It’s just if you don’t understand the intricacy which I didn’t either it took @Novasty beating the network map into my head for me to finally fucking understand what I was doing. Excuse my French. But once you do it the first time that guide becomes like oh duh. You don’t want to know the level of Asian rage it took novasty to explain that to me enough for me to go oh duh :rofl: … thanks man don’t ban me lol

I don’t know maybe I can go and see if they want to make the guide a bit better

The type of wire guard setup you actually made is what we call a client to site. This is actually what you’re more used to It’s not site to site because you did not open your entire home site to the entire subnet of linode You only did specific Ips in the version of the configurations you sent me.

That’s fine and it’ll work this way You can always modify it later to make it true siite to site

1 Like

A good way to visualize wireguard is each node has their own private & public key.

Private key would be the person & public key is the home address. For each person be able to give stuff to each other, they must exchange addresses (public keys). Endpoints would be the route they take to get to each other. Pre-shared keys is their secret handshake.

1 Like

Akin to the Diffie-Hellman parameter of a TLS handshake (perfect forward secrecy) lol. Not a true apples to apples comparisons since I’m comparing socket layer versus application layer but still

That’s a good analogy damn I wish I was in the guide

The secret handshake is optional as well.

The analogy can be refined a bit to encompass every required parameter, but I choose not to as I don’t teach this to people.

I saw something in the documentation that said that it makes it quantum resistant

I still haven’t really figured out that part

Is it just placing a handshake inside a handshake

Okay so an issue on this configuration that has been bugging me

iptables -D FORWARD -i %i -j ACCEPT;  # In
iptables -D FORWARD -o %i -j ACCEPT;  # Out
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; # NAT MASQ
#ip6tables -D FORWARD -i %i -j ACCEPT;  # 6 in
#ip6tables -D FORWARD -o %i -j ACCEPT; # 6 out
#ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE # NAT 6 MASQ

How do you get IPv6 to IPv6 pure 6 stack wireguard going?

4 works
6 in 4 works
pure 6 does not…

Thoughts from the more experienced?


How are you assigning the IPV6 addresses on both sides? How did you set up routing?
You shouldn’t be needing NAT at all. if all. parties support IPV6 but there’s alot of ‘it depends’ :slight_smile:

The answer is it ultimately became a pain to do IPv6 routing through wireguard. It involves NPT and I decided not to set it up. IPv6 without NAT requires neighborhood discovery like anything would. This isn’t working because my server’s gateway assumes the ENTIRE /64 is on-link and in the neighborhood, even with the private address space in the middle. From what I understand, this assumes that every address is directly reachable on the same Ethernet subnet/segment, so the gateway initiates a Neighbor Discovery query for your endpoints MAC address but won’t receive any reply because the peer is not on the same link nor network.

Now I have been reading around and it seems a few people have gotten this reliably working with ipv6 by running a NDP Proxy daemon on each side. It will listen for Neighbor Solicitation packets and send the correct Neighbor Advertisements through the tunnel. I have no idea how this might be accomplished. I see the option on both ends to do so but I’m quite afraid to touch it haha

I think IP4 has the equivalent of proxy NDP vs proxying ARP?

As for assigning IPv6 internally. So here is my schtick… well RFCs schtick. Your internal addy for wireguard must follow it. RFC 4193: Unique Local IPv6 Unicast Addresses

To generate a RANDOM unicast properly according to RFC this is how you will do so on lincucks

fd00::/8 is the base remember that
The next thing you need is combined unix time so run the command date +%s%N and combine that in a pipe with your machine ID… Your going to want the sha1 sum of this. You can find the machine ID on rhel at /etc/machine-id …

so essentially you will
printf <TIME><IDENT> | sha1sum now once you have that you reall only want the trailing 40 bits of this. Thats what RFC calls for so `printf | cut -c 31- so you are taking from position 31 and forward. This should give you 5 bytes as the output. You will translate this into the addy fd(byte1):byte2byte3:byte4byte5::/64

Seriously though. not that hard to create

[[email protected] wireguard]# date +%s%N
[[email protected] wireguard]# cat /etc/machine-id
[[email protected] wireguard]# printf 16611227594112930617f1e18d5a0a443f3a1aca8ff34964745 | sha1sum
[[email protected] wireguard]# printf dc967da0b6bc47f53405f8474e3173614d7da6e4 | cut -c 31-
[[email protected] wireguard]# echo fd61:4d7d:a6e4::/64
(for reminder)