This guide will teach you how to tunnel your internet traffic through a Wireguard VPN service, that you will configure yourself. You could also use this to run your own servers on your home or office internet for example, by port forwarding through your wireguard tunnel. The wireguard tunnel can be set up on a standard VPS or colo provider, who would give you a Linux system with SSH, and you can just log in there to set it up; this gives you static IPv4 and IPv6 addresses at home.
You can find information about WireGuard here: https://www.wireguard.com/
WireGuard replaced OpenVPN as the modern VPN implementation. It’s much more efficient and generally has much better performance. Most VPN providers use this nowadays.
I wrote this guide for myself, because I was sick and tired of using my UK IP address, because lots of sites nowadays block access to UK visitors, especially mastodon instances. Even imgur blocks the UK now. No more.
And I want to host some servers. If you’re like me, then this guide is for you.
Another reason you might want to do this is, for example, if your ISP doesn’t give you IPv6 or blocks websites. You could probably torrent on a setup like this too, but do check the ToS of your VPS/colo provider. Bittorrent is the most reliable way to download Linux and BSD distros, since 2001.
This guide was written for Debian, but it’s pretty much applicable to any distro, or even BSD systems. Adapt accordingly.
Install these on your server and your client (which I assume is a Debian machine):
apt install wireguard wireguard-tools resolvconf
The resolvconf utility is needed if you’re directly using wg-quick on your client, to just make a quick connection. Integrated setups using systemd will often use systemd-resolved these days, if you were making a more permanent client setup - not covered in this guide. NetworkManager can also set resolvers. Here, in this guide, I was using Addresses Only DHCP mode in NetworkManager, with default routes ignored, and a static route set to my Wireguard server, where a default route is created by running wg-quick (more on this later). Adapt accordingly. Note that resolvconf running on its own here, may conflict with another setup you have for resolver config, so do beware.
You should also install wireguard-tools on your client machine. Note that this is a Debian package, so adapt accordingly (my client machine and server machine are both Debian).
Please also ensure that resolvconf is installed on your (Debian) client. This guide assumes that the client and server are both Debian, but you can adapt accordingly to whatever you use.
Now run:
wg
If there is no output, it means that WireGuard is set up. Next, we need to configure it.
Load the wireguard kernel module:
modprobe wireguard
Wireguard uses private/public key pairs (asymetric encryption).
Your server shall have its key, and your client will have a key.
Now, run this on your local machine, not your VPS:
wg genkey | tee privatekey | wg pubkey > publickey
Then check you have the files publickey and privatekey. Keep these safe.
We are configuring IPv4 and IPv6.
We are to configure the private IPs used by the WireGuard server. Note that this is distinct from your server’s public IP address. We will be configuring NAT and NAT66.
NAT66 is not common for IPv6, but lots of VPS providers don’t give you a block of v6 addresses, only a single one, or they make prefixes really weird to set up. For our purposes, NAT66 will do just fine and this is what many professional VPN providers do.
RFC 1918 states that the following IPv4 ranges are private:
10.0.0.0/8172.16.0.0/12192.168.0.0/16For IPv6, fc00::/7 is the ULA range (Unique Local Address).
We will have the subnet 10.66.66.0/8 for our setup, and our server shall have the address 10.66.66.1 - clients shall use other addresses inside this range.
If you were setting up a lot more clients, like for a professional VPN service, you might instead use something in the 10. range or 172. range.
Edit the file /etc/wireguard/wg0.conf and enter the following:
[Interface]
PrivateKey = <private key of the server>
Address = 10.66.66.1/8,fd66:66:66::1/64
ListenPort = 8999
The PrivateKey entry should be set to whatever your private key was, as generated earlier, copied from your local privatekey file.
In the above setup, WireGuard shall listen on port 8999. The standard port is 51820, but lots of IPs will block or throttle this port, so we use a non-standard port.
These are separate from your server keys. Make sure you move the privatekey and publickey files to a directory for your server config, so that the next commands don’t overwrite them.
Same as before.
Do this as root:
mkdir -p /etc/wireguard/keys
cd /etc/wireguard/keys
umask 077
wg genkey | tee privatekey | wg pubkey > publickey
Create the file /etc/wireguard/wg0.conf with the following contents:
[Interface]
PrivateKey = <private key of the client>
Address = 10.66.66.2/8,fd66:66:66::2/64
DNS = 1.1.1.1
[Peer]
PublicKey = <public key of the server>
Endpoint = <public ip of the server>:8999
AllowedIPs = 0.0.0.0/0,::/0
PersistentKeepalive = 25
Pay attention to the keys you use. There are four keys:
We create all keys locally, not on the server, because then for example, if you were communicating between clients, there’s no chance of a potentially hostile provider being able to decrypt the traffic easily, since they never have both keys in a given exchange. For exit traffic, it is irrelevant, though in practise you will often encrypt that too (HTTPS on websites for example).
This is the point of a VPN. It enables you to have encrypted communication between clients, as though they were all on a local network, even if they are located all over the world.
The DNS line just sets /etc/resolv.conf, defining what DNS resolver to use. We used 1.1.1.1 in the above example, which is CloudFlare, but you can set this to whatever you want. Running your own local resolver is even possible, if you wanted to do that. Name resolvers are beyond the scope of this guide, so adapt accordingly.
Note that the client IP, as seen above, is 10.66.66.2, exactly as we alluded to earlier. The server IP (privately) is 10.66.66.1. The public IP of the server is also specified, above.
I’m using ufw on my server, so I did this there:
ufw allow from any proto udp to any port 8999
I also did this:
ufw route allow in on eth0 out on wg0
ufw route allow in on wg0 out on eth0
This enables IP forwarding in ufw.
Note that if you’re not using ufw, you should adapt accordingly. For example if using e.g. iptables directly. Maybe you’re using FreeBSD with pf or something. Just make sure you enable IP forwarding in your firewall.
You should probably also enable SSH. If you’re running sshd on the default port 22 TCP, you could do:
ufw allow from any proto tcp to any port 22
Adapt accordingly. Packet filtering is beyond the scope of this guide.
Add your client public key to your server’s /etc/wireguard/wg0.conf:
[Peer]
PublicKey = <public key of the client goes here>
AllowedIPs = 10.66.66.2/32,fd66:66:66::2/128
You’ll note that we allow these local IPs, that we’d set for the client.
In that same file, on the server, also add these lines in the first section, where you’d specified the server’s private key:
PostUp = /sbin/fwd -A
PostDown = /sbin/fwd -D
The PostUp and PostDown commands specify what to do server-side, when the wireguard tunnel goes up or down respectively. We will use this for port forwarding and NAT/NAT66, which shall enable clients to have internet.
Port forwarding would also be possible here, and we will provide some examples!
In the example below, please note that the PREROUTING lines are for port forwarding. Remove as appropriate. Ample example is provided. If you don’t want to open any ports to your client, just don’t add the PREROUTING lines.
If you use this, make sure also to set your IPv4 and IPv6 addresses, the public ones that is, for your server.
Also pay attention to eth0 in the example below.
Create the file /sbin/fwd with these contents:
#!/bin/sh
# SPDX-License-Identifier: CC-0
set +u +e
if [ $# -lt 1 ]; then
"$0" -D
"$0" -A
exit 0
fi
if [ "$1" != "-A" ] && [ "$1" != "-D" ]; then
"$0"
exit 0
fi
/sbin/sysctl -w net.ipv4.ip_forward=1
/sbin/sysctl -w net.ipv6.conf.all.forwarding=1
/sbin/iptables -t nat $1 POSTROUTING -o eth0 -j MASQUERADE
# /sbin/iptables -t nat $1 PREROUTING -i eth0 -p tcp -d <public IPv4 address> --dport 80 -j DNAT --to 10.66.66.2:80
/sbin/ip6tables -t nat $1 POSTROUTING -o eth0 -j MASQUERADE
# /sbin/ip6tables -t nat $1 PREROUTING -i eth0 -p tcp -d <public IPv6 address> --dport 80 -j DNAT --to [fd66:66:66::2]:80
exit 0
You’ll note the sysctl lines. For some reason, on the VPS I used, the /etc/sysctl.conf file was not being used at startup, so I just did it manually there. IP forwarding is only relevant when Wireguard is actually running anyway.
Pay attention to all the PREROUTING lines. These are examples of how to do port forwarding on your wireguard tunnel. You can see here that I’m forwarding these to a client. It’s commented in the above example, and this example would enable port 80 TCP, for HTTP. Make sure your server (that you forward ports to) is properly configured and hardened - it is assumed that you know what you’re doing!
This is the real power of running a Wireguard server on your VPS. You can use it to forward ports to your local network, and self-host your own servers, just using the VPS as a router essentially.
Note that a lot of VPS providers will block SMTP ports such as port 25 by default, unless you ask them to unblock it.
The MASQUERADE lines enable NAT and NAT66.
Make sure to mark it executable:
chmod +x /sbin/fwd
This will enable NAT, NAT66 and port forwarding as you desire.
You can play with this and just restart your wireguard server as desired, once it’s up and running.
You must enable packet forwarding.
If you have /etc/sysctl.conf you could add/uncomment these there, otherwise perhaps add them to /etc/sysctl.d/0000-interface or something:
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
NOTE:
If you have a packet filter, you should also enable IP forwarding there.
On my server, I use ufw, so I edited the file /etc/ufw/sysctl.conf and uncommented these lines:
net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1
I then restarted ufw:
systemctl restart ufw
On the server:
wg-quick up wg0
This is only temporary, to see if it works. If you don’t get an error, you should be OK.
Try to see if you can access the internet. Test IPv4 and IPv6, for example you could try:
ping -4 google.com
ping -6 google.com
Since the link is now up, what you can also do is enable it on startup:
systemctl enable wg-quick@wg0.service
Note that wg0 in this command refers to the wg0 config that you made. It is actually possible to make multiple setups.
You can do e.g. this:
ip a
And you might see, within the output, something like this:
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.66.66.1/8 scope global wg0
valid_lft forever preferred_lft forever
inet6 fd66:66:66::1/64 scope global
valid_lft forever preferred_lft forever
NOTE: Pay attention to MTU!
Most of the time, clients will be quite conservative about this and also set a matching MTU, or they will be even safer and set something like 1400, or even lower.
This is really the only disadvantage to running everything behind a tunnel, in that everything has an overhead on MTU.
This means fragmented packets, and/or you might enable MSS clamping for TCP as one mitigation.
In practise, I’ve been using tunneled internet for years and never really had any serious problems.
My client is Debian, so I added this to /etc/network/interfaces
auto wg-client
iface wg-client inet static
address 10.66.66.2
netmask 255.0.0.0
pre-up wg-quick up wg0
post-down wg-quick down wg0
iface wg-client inet6 static
address fd66:66:66::2
netmask 64
Then you can restart networking:
systemctl restart networking
I’m not using this on my workstation. I just connect to WiFi or ethernet with NetworkManager, and I specify in NetworkManager that (via DHCP) any default route on IPv4 should be ignored; I also disable IPv6 on the hard network, in NetworkManager.
In NetworkManager, I set a static route to my VPS where I host Wireguard.
Then when I’m on the network, I just do:
wg-quick up wg0
I do down instead of up to turn it off, or when restarting the tunnel after switching network. It’s a bit more manual. I’m OK with that.
You might do the setup in ifupdown, as shown above, if you’re running a server. The use-case here would be that you run e.g. web, mail, DNS server locally, and you forwarded ports through the VPS.
Self-hosting this way is inefficient, yes, but means that you keep data local. You don’t trust your VPS to store data, and you make sure all your site visitors connect with HTTPS for example.
On your server, when your clients are running, you could try this:
wg show
This will show you what clients, if any, are connected.
Example:
interface: wg0
public key: <ENCRYPTION KEY>
private key: (hidden)
listening port: 8999
peer: <ENCRYPTION KEY>
endpoint: 81.187.201.146:58128
allowed ips: 10.66.66.2/32, fd66:66:66::2/128
latest handshake: 16 seconds ago
transfer: 239.06 MiB received, 1.08 GiB sent
You could pretty much just do this on a client, that you configure as a local WireGuard router, as shown above.
You would make the router have a static route, only to the Wireguard server. When wg0 is up, a default route is created.
Although beyond the scope of this guide, you could very quickly just create a masquerade rule there for IPv4 and IPv6 (double NAT and double NAT66). It’s a bit cursed, but should work.
You could run isc-dhcp-server (the good one), or whatever you want. Though I’d just keep it simple (no DHCP, just set IPs on your machines). This is useful for offloading CPU usage, because Wireguard uses heavy encryption, so having a dedicated client router would be useful on networks with lots of clients (instead of having every client draw more current by burning more CPU cycles).
To prevent DNS leaks, and other traffic leaks, you might do this:
When the wireguard tunnel is down, ensure that no default route is present. You would only have a static route to your Wireguard server’s IP address.
This differs per system. I’m using NetworkManager on my client machine, so I set this up there.
Because the IP address to your Wireguard server would work, and would be the only one that does work, you would then only have a default route when the wireguard tunnel is up.
This is optional, but highly recommended.
It should be noted that even if you do have a normal default route, what wg-quick does is create an entirely different routing table with its own default route, and this is used instead, while the tunnel is up. That’s all well and good, but you want to ensure absolutely under some use-case scenarios, that no traffic except WireGuard shall leave your network.
Depending on how you do packet filtering, or not do it, you may be able to use SSH through the wireguard tunnel. For example, you could SSH into your wireguard server when necessary, at 10.66.66.1 instead of its public IP. You would ensure that only wireguard traffic leaves through your main ISPs network, and all other traffic would be encapsulated within the Wireguard tunnel.
This is beyond the scope of the guide, so adapt accordingly. It’s pretty advanced, and most people aren’t going to do this.
Some VPN providers will offer a “kill switch” feature, which is pretty much what is described above. They just usually give you an “app” that does it.
As you can see, Wireguard is pretty simple. VPN providers make it look more complicated than it is. If you’re just setting this up for yourself and a few friends, this guide is pretty much all you need.
You also saw examples earlier of how to do port forwarding. Some people will baulk at the idea of port forwarding over NAT66, but I do it and it’s fine.
Firstly, this setup likely gives you a ULA IPv6 address on your workstation, which means that you might have IPv4-first. Ideally, names (in DNS) should resolve to IPv6 first. This is intentional behaviour, because global IPv6 addresses come first, but not local; it’s just the way things are done.
You can change it. On my Debian system, I simply set this in /etc/gai.conf - I place these exact contents:
## RFC 6724 Default
label ::1/128 0
label ::/0 1
label ::ffff:0:0/96 4
label 2002::/16 2
label 2001::/32 5
# label fc00::/7 13
# Changed the above line for IPv6-first-on-NAT66 workaround. See next line:
label fc00::/7 1
label ::/96 3
label fec0::/10 11
label 3ffe::/16 12
precedence ::1/128 50
precedence ::/0 40
precedence ::ffff:0:0/96 35
precedence 2002::/16 30
precedence 2001::/32 5
precedence fc00::/7 3
precedence ::/96 1
precedence fec0::/10 1
precedence 3ffe::/16 1
You might not run into this.
Consider setting an MSS clamp. This mitigates packet fragmentation on TCP connections, due to the MTU overhead of Wireguard.
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1380
ip6tables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360
NOTE: In the above example, we set MSS to 1380 and 1360 on IPv4 and IPv6 respectively. This assumes an MTU of 1420 on IPv4 (for the wg0 interface).
Let’s break this down:
So… IP header plus TCP header gives you 40 for IPv4 and 60 for IPv6.
Subtract these from MTU (e.g. 1420), whatever it is on your setup.
Please note that these go in the client, not the server. By setting this, the hosts you talk to on the internet are told to send you back smaller frames rather than e.g. full MTU 1500.
This is pretty advanced and most people probably won’t be bothered either way.
Due to MTU overhead, packet fragmentation is inevitable. UDP doesn’t really matter here, but some applications / operating systems will behave more nicely with proper MSS clamping.
If you’re running a dedicated client router (routing through your wireguard server), you might do this.
Try on server, to see what clients are up:
wg show
This will show routing tables (use on client and/or server):
ip route show table all
(it can show you if you have a default route, and what default routes you have, if you have several)
If you’re controlling wireguard with systemd, you could do something like this:
journalctl -u wg-quick@wg0
(this will show you any errors reported in systemd’s log output)
No app nonsense. You could also set up a client on your phone if you want, e.g. Android phone.
We’ll assume you already knew to disable password auth, deferring to key-based auth instead, and that you disabled root ssh login. And maybe installed something like fail2ban. We assume a lot.
You could set this in /etc/ssh/sshd_config like so:
ListenAddress 10.66.66.1
This would make SSH only respond inside the tunnel, not on the naked internet. This adds a layer of hardening, since it then essentially whitelists only to approved clients.
You could further IP whitelist within the tunnel, for example only allowing the IP address 10.66.66.1 to access SSH. Since each IP address has to be approved, with a specific key assigned to it in wireguard, this is quite powerful whitelisting indeed, and it adds an extra layer of encryption for your SSH sessions.
In my case, using ufw:
ufw allow from 10.66.66.2 proto tcp to any port 22
You would do this, either in ufw or adapting to whatever you use. Incoming traffic blocked by default, but you whitelist this IP address for SSH. Since we’re running wireguard on a non-standard port, it’s unlikely that it’d be blocked, and we’re doing SSH inside wireguard, so it’s probably fine to just use the standard port 22 here.
You could then, for example, if you were running a mail server for only your use, auth over SSH. The sky’s the limit, though the latter is far beyond the scope of this guide.
I made this guide for me. Adapt it for your own needs accordingly. Have fun!
Markdown file for this page: https://fedfree.org/docs/router/wireguard.md
This HTML page was generated by the Libreboot Static Site Generator.