WireGuard Tunnel

From Computernewb Wiki
Revision as of 21:28, 5 January 2025 by Elijah (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
Warning icon
Read this entire guide before attempting to follow it. Not doing so may result in broken configurations or unexpected consequences.

This guide will instruct you on how to set up a WireGuard tunnel between a server machine and one or more clients. This establishes a secure tunnel between the machines (connecting them as if they were on the same LAN), and can further be used to route traffic and forward ports.

These instructions are specifically tailored to Linux machines.

Prerequisites

You'll need:

  • A server machine that will host the WireGuard endpoint
  • At least one client machine.
  • Basic knowledge of how computers and Linux systems work.

Setting up the tunnel

For the purposes of this guide, we are calling our tunnel wg0. If you would like to use a different name, make sure to substitute wg0 with the interface name of your choice in all commands and config files.

We will be using 10.0.32.0/24 as our WireGuard subnet for the purposes of this guide.

Install Dependencies

First, make sure you have wireguard-tools and nftables installed on both your server machine, and your client machines.

$ sudo pacman --needed --noconfirm -Sy wireguard-tools nftables # Arch
$ sudo apt-get install -y wireguard-tools nftables # Debian

Generating the Keys

Now, we need to generate a public and private key for each system that will be in the tunnel. You can use these commands on each computer, as root:

$ sudo -i
# mkdir -p /etc/wireguard/keys/wg0 # Create a directory for the keys
# cd /etc/wireguard/keys/wg
# umask 077 # Temporarily set the default permissions for all files to rwx------
# wg genkey > private.key # Generate the private key
# wg pubkey < private.key > public.key # Derive the public key from the private key

# cat private.key
/ExampleServerPrivateKey=
# cat public.key
/ExampleServerPublicKey=

Configuring the tunnel (Server)

On your server machine, create /etc/wireguard/wg0.conf and put the following in it.

[Interface]
PrivateKey =  <Paste your private.key from the SERVER> 
Address = 10.0.32.1/32
ListenPort = 51820

This will assign your WireGuard server with the IP 10.0.32.1 on the WireGuard tunnel, and listen on UDP port 51820

Next, add the clients. In the server's wg0.conf, add the following for each client:

[Peer]
PublicKey = <Paste the PUBLIC key from the CLIENT>
AllowedIPs = 10.0.32.2/32

This will give the specified client an IP address of 10.0.32.2 on the WireGuard tunnel. Make sure you increment this for each client and use the correct public keys.

Here is an example of a completed server wg0.conf:

[Interface]
PrivateKey = /ExampleServerPrivateKey=
Address = 10.0.32.1/32
ListenPort = 51820

[Peer]
PublicKey = /ExampleClientPublicKey1=
AllowedIPs = 10.0.32.2/32

[Peer]
PublicKey = /ExampleClientPublicKey2=
AllowedIPs = 10.0.32.3/32

[Peer]
PublicKey = /ExampleClientPublicKey3=
AllowedIPs = 10.0.32.4/32

Configuring the tunnel (Client)

Now, on your client(s), create /etc/wireguard/wg0.conf as well and put the following in

[Interface]
PrivateKey = <Paste your PRIVATE key from THIS CLIENT>
Address = 10.0.32.2/24

[Peer]
PublicKey = <Paste the PUBLIC key from the SERVER>
AllowedIPs = 10.0.32.0/24
Endpoint = <Your server's IP address>:51820

Make SURE to use the correct combination of IP address and private key defined in a [Peer] section in the server configuration.

Bringing Your WireGuard Tunnel Up

Now that we have configured our server and clients, we can start the tunnel. The following command will start the tunnel, and configure it to be started on every boot. Run it on your server and all your clients.

sudo systemctl enable --now wg-quick@wg0

Run sudo systemctl status wg-quick@wg0 to make sure that WireGuard started properly.

Test the connection

Assuming everything started correctly, each machine should now have a wg0 interface with the correct IP assigned to it. Test the connection from one of the clients with ping:

$ ping 10.0.32.1
PING 10.0.32.1 (10.0.32.1) 56(84) bytes of data.
64 bytes from 10.0.32.1: icmp_seq=1 ttl=64 time=1.99 ms
64 bytes from 10.0.32.1: icmp_seq=2 ttl=64 time=1.81 ms
64 bytes from 10.0.32.1: icmp_seq=3 ttl=64 time=1.76 ms
(...)

If all went well, congratulations! You now have a secure tunnel between your server and all of your WireGuard clients. Continue reading for how to configure it to your liking

Routing traffic

You may want to set up your WireGuard server as a router, so that all outbound traffic from one or more of your clients goes through the server (the common definition of a VPN). This is very easy to do.

Enable IP Forwarding

First, we enable IP forwarding on the server machine. This instructs the Linux kernel that when it receives a packet from one of your clients destined to the internet, it should route that packet to its destination.

Run the following command to enable IP forwarding in the sysctl configuration:

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/router.conf
sudo sysctl --system

Configuring SNAT

With IP forwarding enabled, the next step is to set an SNAT rule. This instructs the server that when it routes a packet from your client to the internet, it should also replace the source IP address (the client's internal WireGuard IP) with its own public IP. This can be accomplished through the NFTables firewall.

First, get the name of the server's internet-facing network interface, and it's public IP address. For example, we will use eth0 as the interface.

Now, open /etc/nftables.conf and configure as follows:

table inet nat {
    chain postrouting {
      type nat hook postrouting priority srcnat; policy accept;
      meta iifname wg0 meta oifname eth0 snat ip to <Enter server's public IP address here>
    }
}

Reload the NFTables configuration:

sudo nft -f /etc/nftables.conf

Depending on distro and prior configuration, you may already have a table inet nat block, and possibly a chain postrouting inside it. In this case, merge the above configuration with your own. Otherwise, paste the above snippet into the bottom of the file.

Configure the client route

Warning icon
Be very careful doing this if you have no offline access to the client! If something is configured incorrectly, this has a chance of killing the client's internet connection until it is fixed.

Now, all you need to do is configure WireGuard on the client to route all traffic through the server. You do this by configuring the [Peer] section on the client to have an AllowedIPs value of 0.0.0.0/0. For example:

[Interface]
PrivateKey = /ExampleClientPrivateKey1=
Address = 10.0.32.2/24

[Peer]
PublicKey = /ExampleServerPublicKey=
AllowedIPs = 0.0.0.0/0
Endpoint = server ip:51820

Restart the WireGuard tunnel on the client:

sudo systemctl restart wg-quick@wg0
Warning icon
If you were connected to the client via SSH, your connection will probably drop out here, as the client is now routing all of its traffic through the server and is not using its public IP for anything other than connecting to the tunnel. To reconnect to the client, you can use ssh 10.0.32.2 from the server or another client on the tunnel. Read below for how to forward this port to the server's public IP.

If your client is on your LAN and you are accessing it by its private IP, read the below section on Excluding subnets before using this configuration.

If all went well, all traffic on the client should now be routed through the server:

$ curl ipinfo.io/what-is-my-ip
{
  "ip": "143.244.47.86",
  "hostname": "unn-143-244-47-86.datapacket.com",
  "city": "Weehawken",
  "region": "New Jersey",
  "country": "US",
  "loc": "40.7696,-74.0204",
  "org": "AS212238 Datacamp Limited",
  "postal": "07086",
  "timezone": "America/New_York",
  "readme": "https://ipinfo.io/missingauth"
}

Excluding subnets

If your client is on a LAN, you will probably want to configure your client in a way that it can still access other devices on the LAN directly and not through the tunnel. This can be done by further configuring the AllowedIPs setting.

For this, we will be using this very nice AllowedIPs calculator tool.

For example, let's say your client is on a LAN that uses a subnet of 192.168.1.0/24, and you want to route its internet connection to the tunnel, but still be able to access devices on the LAN. To do this, open the tool linked above, and put 0.0.0.0/0 in the Allowed IPs field. Then, put 192.168.1.0/24 in the Disallowed IPs field. This will give you a value like this:

AllowedIPs = 0.0.0.0/1, 128.0.0.0/2, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.168.0.0/24, 192.168.2.0/23, 192.168.4.0/22, 192.168.8.0/21, 192.168.16.0/20, 192.168.32.0/19, 192.168.64.0/18, 192.168.128.0/17, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3

Use it in your client's wg0.conf like so:

[Interface]
PrivateKey = /ExampleClientPrivateKey1=
Address = 10.0.32.2/24

[Peer]
PublicKey = /ExampleServerPublicKey=
AllowedIPs = 0.0.0.0/1, 128.0.0.0/2, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.168.0.0/24, 192.168.2.0/23, 192.168.4.0/22, 192.168.8.0/21, 192.168.16.0/20, 192.168.32.0/19, 192.168.64.0/18, 192.168.128.0/17, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3
Endpoint = server ip:51820

Restart the tunnel:

sudo systemctl restart wg-quick@wg0

If all went well, connections to public IPs should now be routed through the tunnel while the LAN subnet is still accessible.

Port Forwarding

You may want to forward ports on your server's public IP to the guest. This is very easy to do. We will update the server's NFTables configuration to set a DNAT rule. The following example will forward TCP ports 80 and 443 to the client at 10.0.32.2.

table inet nat {
  chain prerouting {
    type nat hook prerouting priority dstnat; policy accept;
    meta iifname eth0 tcp dport { 80, 443 } dnat ip to 10.0.32.2
  }
}

Make sure to change eth0 to your server's public interface if it differs. Note that this rule goes into the prerouting chain, NOT the previously used postrouting chain.

Reload the NFTables configuration:

sudo nft -f /etc/nftables.conf

If all went well, connections to ports 80 and 443 on the server's public IP should now be forwarded to the HTTP server running on the client at 10.0.32.2.