Deploy WireGuard VPN Server on Debian 12
- Steps
- Taking It Further With Web Proxing
- Taking It Further With Port Forwarding
- Debugging
- References
Steps
Setup Server
Install WireGuard
sudo apt update
sudo apt-get install wireguard -y
Create Server Keys
Now we need to create the public and private key-pairs used for establishing a secure connection. We can do this by running the following command:
wg genkey | tee privatekey | wg pubkey > publickey
This will result in there being two files locally called privatekey and publickey which contain the private and public keys accordingly.
Configure Tunnel Interface
WIREGUARD_INTERFACE_NAME="wg0"
sudo touch /etc/wireguard/${WIREGUARD_INTERFACE_NAME}.conf
wg0
, but whatever you call it, the file
must be at that path and have the same name with the .conf
extension.
Now either fill it in manually by substituting the variables in the content below:
[Interface]
PrivateKey=${PRIVATE_KEY}
# Specify the subnet that should be used for assigning IP addresses to the clients on this
# tunnel interface. This needs to not overlap with your server's private IP subnet!
Address=${WIREGUARD_SUBNET}
SaveConfig=false
# Add rules to iptables networking when the interface come up in order to have the server do the
# following:
# 1. Accept every forwarding packet on the tunnel interface (wg0)
# 2. have packets that we forward out on the eth0 interface be manipulated to look like they came
# from this server instead of having their original source IP.
PostUp=iptables -A FORWARD -i ${WIREGUARD_INTERFACE_NAME} -j ACCEPT; iptables -t nat -A POSTROUTING -o ${NORMAL_INTERFACE_NAME} -j MASQUERADE;
# Remove the rules we added, when the wireguard tunnel interface comes down
PostDown=iptables -D FORWARD -i ${WIREGUARD_INTERFACE_NAME} -j ACCEPT; iptables -t nat -D POSTROUTING -o ${NORMAL_INTERFACE_NAME} -j MASQUERADE;
# Specify which port you wish for the wireguard to listen on for this interface. This can be anything
# you want, but 51820 is the default for wireguard.
ListenPort=${PORT}
... or you can use the following BASH script to fill in the variables and have it create the file for you:
# Settings - fill these in as appropriate to you.
PRIVATE_KEY=""
WIREGUARD_INTERFACE_NAME="wg0"
WIREGUARD_SUBNET="10.172.0.1/24"
NORMAL_INTERFACE_NAME="eth0"
PORT=51820
sudo echo "
[Interface]
PrivateKey=${PRIVATE_KEY}
# Specify the subnet that should be used for assigning IP addresses to the clients on this
# tunnel interface. This needs to not overlap with your server's private IP subnet!
Address=${WIREGUARD_SUBNET}
# Specify if you want wireguard to overwrite the config on shutdown.
SaveConfig=false
# Add rules to iptables networking when the interface come up in order to have the server do the
# following:
# 1. Accept every forwarding packet on the tunnel interface (wg0)
# 2. have packets that we forward out on the eth0 interface be manipulated to look like they came from this
# server instead of having their original source IP.
PostUp=iptables -A FORWARD -i ${WIREGUARD_INTERFACE_NAME} -j ACCEPT; iptables -t nat -A POSTROUTING -o ${NORMAL_INTERFACE_NAME} -j MASQUERADE;
# Remove the rules we added, when the wireguard tunnel interface comes down
PostDown=iptables -D FORWARD -i ${WIREGUARD_INTERFACE_NAME} -j ACCEPT; iptables -t nat -D POSTROUTING -o ${NORMAL_INTERFACE_NAME} -j MASQUERADE;
# Specify which port you wish for the wireguard to listen on for this interface. This can be anything
# you want, but 51820 is the default for wireguard.
ListenPort=${PORT}
" | sudo tee /etc/wireguard/${WIREGUARD_INTERFACE_NAME}.conf
Enable Packet Forwarding
Finally, we need to ensure that the server will allow packet forwarding. This can be enabled by running:
sudo echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
Then we also need to enable it in the sysctl kernel configuration file:
SEARCH="#net.ipv4.ip_forward.*"
REPLACE="net.ipv4.ip_forward=1"
FILEPATH="/etc/sysctl.conf"
sudo sed -i "s;$SEARCH;$REPLACE;" $FILEPATH
# Have the change take immediate effect
sudo sysctl -p
Setup Client
Now we need to setup and configure wireguard on the client, for it to connect to the server.
Install Wireguard
Install wireguard as you did before:
sudo apt update
sudo apt-get install wireguard -y
Create a public/private key-pair as you did before on your client computer:
wg genkey | tee privatekey | wg pubkey > publickey
Configure Tunnel Interface
Now create a wireguard configuration file by running:
touch /etc/wireguard/wg0.conf
wg0.conf
and can be labelled anything with the .conf
extension. However, it does need to be in the /etc/wireguard
folder and the name of the
file will reflect the name of the interface. The name of the interface doesn't need to match
the name of the interface on the server either.
Now either fill it with the following, taking care to manually swap out the values as appropriate to your setup:
[Interface]
PrivateKey=${PRIVATE_KEY}
# Specify the subnet that should be used for assigning IP addresses to the clients on this
# tunnel interface. This needs to not overlap with your private IP subnet, and needs to be
# unique on the subnet you created. E.g. we used 10.172.0.1 for the server, so we
# are using 10.172.0.2 here. If you were to create another client it would need to be
# 10.172.0.3 etc.
Address=10.172.0.2/24
# Specify if you wish for WireGuard to overwrite the config on shutdown.
SaveConfig=false
[Peer]
# Specify the public key of the server you wish to connect to.
PublicKey=${SERVER_PUBLIC_KEY}
# Specify the IP address of the server to connect to. If you are using something like DigitalOcean,
# then this would be that instance's public/internet IP address. If you are using a computer on
# your private network, then you could specify its private IP address.
Endpoint=${SERVER_PUBLIC_IP_ADDRESS}
# Specify which subnets that wireguard should send through the tunnel. We wish to send all traffic
# through the server, so we will use 0.0.0.0/0, but if you only wanted to forward traffic for
# another private subnet that your server is on, you could specify it's subnet instead.
AllowedIPs=0.0.0.0/0
# Optionally have the following to have wireguard send a keepalive packet every x seconds so
# that the connection remains open, otherwise routers/gateways between the server and the client
# may close the connection because wireguard uses UDP and is stateless.
PersistentKeepalive=30
... or run the following BASH script with setting the variables at the top:
CLIENT_PRIVATE_KEY=""
SERVER_PUBLIC_KEY=""
WIREGUARD_INTERFACE_NAME="wg0"
WIREGUARD_SUBNET_CLIENT_IP="10.172.0.2/24"
SERVER_PUBLIC_IP_ADDRESS=""
AllowedIPs="0.0.0.0/0"
PORT=51820
sudo echo "
[Interface]
PrivateKey=${CLIENT_PRIVATE_KEY}
# Specify the subnet that should be used for assigning IP addresses to the clients on this
# tunnel interface. This needs to not overlap with your private IP subnet, and needs to be
# unique on the subnet you created. E.g. we used 10.172.0.1 for the server, so we
# are using 10.172.0.2 here. If you were to create another client it would need to be
# 10.172.0.3 etc.
Address=${WIREGUARD_SUBNET_CLIENT_IP}
# Specify if you wish for WireGuard to overwrite the config on shutdown.
SaveConfig=false
[Peer]
# Specify the public key of the server you wish to connect to.
PublicKey=${SERVER_PUBLIC_KEY}
# Specify the IP address of the server to connect to. If you are using something like DigitalOcean,
# then this would be that instance's public/internet IP address. If you are using a computer on
# your private network, then you could specify its private IP address.
Endpoint=${SERVER_PUBLIC_IP_ADDRESS}:${PORT}
# Specify which subnets that wireguard should send through the tunnel. We wish to send all traffic
# through the server, so we will use 0.0.0.0/0, but if you only wanted to forward traffic for
# another private subnet that your server is on, you could specify it's subnet instead.
AllowedIPs=${AllowedIPs}
# Optionally have the following to have wireguard send a keepalive packet every x seconds so that
# the connection remains open, otherwise routers/gateways between the server and the client may
# close the connection because wireguard uses UDP and is stateless.
PersistentKeepalive=30" | sudo tee /etc/wireguard/${WIREGUARD_INTERFACE_NAME}.conf
Add Client To Server
We now need to tell the server to allow the client to be able to connect. This is similar to how we add a public SSH key to the $HOME/.ssh/authorized_keys file to tell the server we allow a client to connect with their SSH Key.
Run the following BASH script to append the peer connection details to your WireGuard interface's configuration file:
WIREGUARD_INTERFACE_NAME="wg0"
# Specify the *public* key of the client
CLIENT_PUBLIC_KEY="xxxxxxxxxxxxxxxxxxxx"
# Specify the IP address you gave the client in their `Address` setting but use /32 for the CIDR
# so that the client can only use that single IP.
CLIENT_TUNNEL_IP="10.172.0.2/32"
echo "
[Peer]
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = ${CLIENT_TUNNEL_IP}
" >> /etc/wireguard/${WIREGUARD_INTERFACE_NAME}.conf
... or you can do it manually, with the following template:
[Peer]
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = ${CLIENT_TUNNEL_IP}
Alternative Method
The alternative would be to just run the following command to add the client to wireguard interface:
WIREGUARD_INTERFACE="wg0"
CLIENT_PUBLIC_KEY="xxxxxxxxxxxxxxxxxxxx"
# Specify the IP address you gave the client in their `Address` setting but use /32 for the CIDR
# so that the client can only use that single IP.
CLIENT_TUNNEL_IP="10.172.0.2/32"
sudo wg set $WIREGUARD_INTERFACE peer ${CLIENT_PUBLIC_KEY} allowed-ips ${CLIENT_TUNNEL_IP}
However, that relies on having set SaveConfig
to true
for it to be remembered, and relies on Wireguard overwriting your config file for
the client to be kept, and you not having to keep entering the line. The downside to this is that it will also write in the endpoint for
the client, which is not ideal in which your client's may likely change IP such as a laptop you travel with. Thus I go with just adding
the peers manually to the config file, and having SaveConfig
set to false.
Using The Wireguard Tunnel
Now in order to get the tunnel running and make use of it, you first need to run the connect/up command shown below on the server first, before then running it on the client.
Connect
Finally, after all of that configuration, you should be able to use the following command on your client in order to create the WireGuard tunnel connection to the server and start making use of it:
WIREGUARD_INTERFACE_NAME="wg0"
sudo wg-quick up $WIREGUARD_INTERFACE_NAME
Using Filepath Instead
If you don't like sticking the configuration files in the /etc/wireguard
folder, and placed them somewhere
else, then you can just specify the full path to the configuration file instead:
CONFIG_FILEPATH="/path/to/my/wireguard/config.conf"
sudo wg-quick up $CONFIG_FILEPATH
Disconnect
To disconnect, you an then run the down command like so:
WIREGUARD_INTERFACE_NAME="wg0"
sudo wg-quick down $WIREGUARD_INTERFACE_NAME
Show WireGuard Status
If you wish to see your current wireguard connections and how much traffic is being sent over them, you can run the following command:
sudo wg show
Taking It Further With Web Proxying
I tested running Nginx proxy manager on the WireGuard server, and having it forward the traffic to the WireGuard client IP address. This actually worked without a hitch, and I had expected it to fail due to the fact that Docker likes to mess with the iptables rules. If you want to run the Nginx proxy manager on a public IP, and create a client for each of your home VPS's, this would absolutely work. You would have to create a tunnel for each server though which is a bit tedious!
Taking It Further With Port Forwarding
I wanted to see if I could forward incoming traffic on my eth0
interface through to one of my WireGuard clients.
For testing purposes, I tried forwarding TCP traffic on port 8080 to my client's port 80, in order to test serving
up web content on a custom port. This worked by having the following PostUp
and PostDown
rules:
# Accept forwarding from wg0 -> eth0 (traditional vpn)
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
# Accept forwarding from eth0 -> wg0 (tunnelling into home NAT e.g. cloudflare tunnels)
PostUp = iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o wg0 -j ACCEPT
# forward incoming port 8080 to NAT on port 80
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.172.0.2:80
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.172.0.2:80
# set up masquerading on wg0 (NAT tunnel)
PostUp = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE
# set up masquerading on eth0 (traditional VPN)
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;
eth0
, your WireGuard client is on a "made up" IP of 10.172.0.2
PreUp
instead of PostUp
but I found this would cause things to break.
Debugging
Vultr Instances
When I tried to set this up on Vultr, I found that the instance came pre-deployed with a UFW firewall in place that was preventing things from working. I fixed this by running the following command to disable this firewall:
sudo ufw disable
Then I just rebooted the instance.
Since we use iptables for network manipulation, I suggest you add the relevant iptables rules manually/carefully to implement your own firewall. That way you can be sure you take everything into account, having the full context.
I also tested on DigitalOcean which did not have this issue.
Interface Name
Also, Vultr instances will have an interface called enp1s0
instead of DigitalOcean's eth0
.
References
- reintech.io - Configuring a VPN with WireGuard on Debian 12
- Linode - Set Up WireGuard VPN on Ubuntu
- WireGuard - Installation
- Debian Backports Instructions
First published: 15th May 2025