Direct/Bare metal or VM deployment
Introduction
Direct installation on a VM or bare metal is the preferred option as you have the best possible customizability. NAT and firewalling is possible with the default system tooling and you won’t have to fiddle with Docker or K8s specialized configuration.
Requirements
This project is designed to be run in a Linux environment, if you want to run the server on a Mac or on Windows you will probably have to replace the systemd configuration and some scripts with something that will support that environment. It could be possible but was not tested by the developers.
For this service to work you’ll need some packages from your linux distribution installed as well as some other requirements:
Wireguard compiled into Linux kernel (should be the case for any modern distro)
Installed wireguard tools (we need the
wg-quick
andwg
tools here)Systemd (sorry non-systemd users, I don’t want to debug any other solutions)
Python >= 3.9 with
pip
andvirtualenv
support (extra packages on Ubuntu!)sudo
dnsmasq
if you want to use the routed DNS functionalitynginx
as a reverse proxycertbot
for Letsencrypt certificates.
Python environment
To be independent from system-packaging quirks and have the correct versions of all Python dependencies installed we’re using a Python virtualenv for this service.
Unpack the release zip/tar.gz and switch to unpacked directory
Create python venv:
python -m venv ~/.virtualenvs/wireguard_web
Activate venv:
source ~/.virtualenvs/wireguard_web
Install dependency manager:
pip install poetry
Install dependencies:
poetry install
Ideally you’ll create a new user for this service to run and add the activation of the virtualenv to the shell profile, for example:
#
# ~/.bash_profile
#
[[ -f ~/.bashrc ]] && . ~/.bashrc
source $HOME/.virtualenvs/bin/activate
Application setup
Before you can run the service we need some configuration to be done.
Environment config
To configure the application the only file you have to touch is the .env
file in the source directory. To get a template for this
run cp .env.sample .env
.
Description of configuration options:
WIREGUARD_WEB_BASE_URL
: Base URL on which the service will be run. Usually you’ll run the service behind a reverse proxy likenginx
to terminate TLS. Make sure to use the externally visible URL for this.WIREGUARD_STAGING_CONFIG_DIRECTORY
where to store new configuration files for the systemd services to pick them up and deploy them. See below for a description of the mechanism used to deploy the configuration.WIREGUARD_WEB_EMAIL_*
settings to be used by the email module to send mailWIREGUARD_WEB_EMAIL_BACKEND
this one defines which Django mail backend to use. Use one of the following:django.core.mail.backends.console.EmailBackend
: just log to console, do not send any mail.django.core.mail.backends.dummy.EmailBackend
: do not send mailsdjango.core.mail.backends.smtp.EmailBackend
: Use SMTP to send mail
It is possible to add other e-mail backends (for example Amazon SES), please consult the Django documentation for more information on that.
WIREGUARD_WEB_DEBUG
set this to 1 if you need to debug the Django application. It is not recommended to run the service on production with this set to 1 as it may leak information in generated backtraces.
Migrate Database
Create the database and a first user:
python manage.py migrate
python manage.py createsuperuser
python manage.py create_groups
Sudo configuration
To allow the web-interface to display wireguard connection information you’ll need to allow some commands to be run via sudo without a password:
1 wireguard-web ALL=(ALL) NOPASSWD: /usr/bin/wg show all endpoints
2 wireguard-web ALL=(ALL) NOPASSWD: /usr/bin/wg show all latest-handshakes
Systemd configuration
To run the service there are some systemd-unit-files in the systemd
sub-directory of the source-tree. Be aware that you cannot use them as they are
because they contain some placeholders to be replaced by the install-script.
The following placeholders will be replaced:
{path}
with the path of the source code checkout{dnsmasq}
path todnsmasq
binary{wg}
path towg
binary{wgquick}
path towq-quick
binary{config}
path tointerfaces.conf
in configured config staging directory
Let’s go through each file and describe what it does:
wireguard-web-config.path
1[Unit]
2Description="Monitor the wireguard-web configuration for changes and update system config"
3
4[Path]
5PathChanged={config}
6Unit=wireguard-web-config.service
7
8[Install]
9WantedBy=multi-user.target
This is a path trigger to run the wireguard-web-config.service
when the
interfaces.conf
in the staging directory changes.
wireguard-web-config.service
1[Unit]
2Description="Update Wireguard config when wireguard-web modifies config files"
3
4[Service]
5Type=oneshot
6RemainAfterExit=no
7Environment=WIREGUARD_STAGING_CONFIG_DIRECTORY={config}
8ExecStart={path}/update_config.sh
9WorkingDirectory={path}
10
11[Install]
12WantedBy=multi-user.target
This service copies the configuration from the staging-directory to the
corresponding /etc/wireguard-web
directory which is only writeable by
the root
user.
wireguard-web-dnsmasq@.service
1[Unit]
2Description=Lightweight caching DNS server for wireguard-web interface %I
3Documentation=man:dnsmasq(8)
4Requires=wireguard-web-wg-quick@%i.service
5After=network.target
6After=wireguard-web-wg-quick@%i.service
7Before=network-online.target nss-lookup.target
8Wants=nss-lookup.target
9
10[Service]
11Type=exec
12ExecStartPre={dnsmasq} -C /etc/wireguard-web/dnsmasq-%i.conf --test
13ExecStart={dnsmasq} -C /etc/wireguard-web/dnsmasq-%i.conf -k --user=dnsmasq --pid-file=/var/run/dnsmasq-%i.pid
14ExecReload=/bin/kill -HUP $MAINPID
15Restart=on-failure
16PrivateDevices=true
17ProtectSystem=full
18
19[Install]
20WantedBy=multi-user.target
This runs the DNS service when enabled for a server. This is a parametrized
service file which can be enabled multiple times for multiple WireGuard
interfaces (example: systemctl --enable wireguard-web-dnsmas@wg0
for the
wg0
interface)
wireguard-web-wg-quick@.service
1[Unit]
2Description=WireGuard via wg-quick(8) configured by wireguard-web for %I
3After=network-online.target nss-lookup.target
4Wants=network-online.target nss-lookup.target
5PartOf=wg-quick.target
6Documentation=man:wg-quick(8)
7Documentation=man:wg(8)
8Documentation=https://www.wireguard.com/
9Documentation=https://www.wireguard.com/quickstart/
10Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
11Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
12
13[Service]
14Type=oneshot
15RemainAfterExit=yes
16ExecStart={wgquick} up /etc/wireguard-web/%i.conf
17ExecStop={wgquick} down /etc/wireguard-web/%i.conf
18ExecReload=/bin/bash -c 'exec {wg} syncconf %i <(exec {wgquick} strip /etc/wireguard-web/%i.conf)'
19Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity
20
21[Install]
22WantedBy=multi-user.target
This runs a new wg-quick
based WireGuard tunnel. The service is parametrized
with an interface name and loads it’s config file from
/etc/wireguard-web/<interface>.conf
. It may be enabled multiple times for
multiple interfaces to support more than one VPN endpoint.
wireguard-web-ui.service
1[Unit]
2Description=Django service to configure the wireguard VPN
3After=network.target
4
5[Service]
6Type=exec
7EnvironmentFile=/home/user/wireguard-web/.env
8WorkingDirectory=/home/user/wireguard-web
9ExecStartPre=/home/user/.virtualenvs/wireguard-web/bin/python manage.py migrate
10ExecStart=/home/user/.virtualenvs/wireguard-web/bin/gunicorn -b 0.0.0.0:8000 --workers 2 wireguard_web.wsgi:application
11Restart=on-failure
12User=user
13Group=user
14
15[Install]
16WantedBy=multi-user.target
This runs the Django web-interface that manages the configuration used by the services above. You’ll have to modify it before installing it to match your system.
This one runs on gunicorn
so it requires the additional gunicorn
dependency to be installed into your virtualenv.
You can install this by activating your virtualenv:
source $HOME/.virtualenvs/wireguard-web
and installing the package with:
pip install gunicorn
.
Then change the following things in the .service
-file:
Change
EnvironmentFile
to point to your.env
configuration.Change
WorkingDirectory
to point to your source-code checkout.Change
ExecStartPre
andExecStart
to point to your virtualenv.Change
User
andGroup
to match your choosen username and group-name for the service. Do not use root here.Copy the changed service file to your systemd configuration:
cp wireguard-web-ui.service /etc/systemd/system
Reload the systemd config:
systemctl daemon-reload
Enable and run the service:
systemctl enable --now wireguard-web-ui
Nginx configuration
If you want to use nginx
as a reverse proxy to enable SSL/TLS make sure to
allow for plain HTTP access without a redirect too if you want to use zero
configuration peer2peer communication between VPN clients. If you don’t need
that feature, feel free to redirect all HTTP traffic to HTTPS.
Example configuration with peer2peer support
If you want to use peer2peer support you’ll have to allow plain HTTP to go through to the application as the peering clients will contact the service on it’s IP address and usually the issued SSL/TLS certificates do not include the IP in their common name, so nginx has trouble to do propper SNI to route the requests and the clients get a certificate that is technically not valid.
The Django application automatically sets HSTS
headers when the base URL is
configured as a HTTPS URL, so redirecting your browser based ui to a secure
channel without explicitly redirecting all requests. The API endpoints that
will be called by the peering client will not set HSTS
headers and the API
client will ignore them anyways.
1 server {
2 listen [::]:443 ssl default_server
3 listen 443 ssl default_server
4 listen 80 default_server;
5 listen [::]:80 default_server;
6
7 root /var/www/html;
8
9 index index.html index.htm index.nginx-debian.html;
10
11 server_name my.vpn.dev;
12
13 location / {
14 proxy_set_header Host $host;
15 proxy_set_header X-Real-IP $remote_addr;
16 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
17 proxy_set_header X-Forwarded-Proto $scheme;
18 proxy_set_header Proxy "";
19 proxy_pass_header Server;
20
21 proxy_pass http://127.0.0.1:8000;
22 tcp_nodelay on;
23 }
24
25 ssl_certificate /etc/letsencrypt/live/my.vpn.dev/fullchain.pem; # managed by Certbot
26 ssl_certificate_key /etc/letsencrypt/live/my.vpn.dev/privkey.pem; # managed by Certbot
27 include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
28 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
29 }
This is a server-snippet that may be used as an item in
/etc/nginx/sites-available
and linked to /etc/nginx/sites-enabled
on
Debian or Ubuntu. Alternatively you may replace the server
section of your
/etc/nginx/nginx.conf
file.
You’ll have to replace the server_name
and the certificate paths with the
domain name of your server (here we used my.vpn.dev
).
As you can see the certificates are managed by certbot
. For instructions to
enable certbot
see below.
Example configuration without peer2peer support
If you don’t need direct peer2peer support for your VPN clients you may redirect all traffic to https in nginx configuration:
1 server {
2 if ($host = my.vpn.dev) {
3 return 301 https://$host$request_uri;
4 } # managed by Certbot
5
6 server_name my.vpn.dev;
7 listen 80;
8 return 404; # managed by Certbot
9 }
10
11 server {
12 listen [::]:443 ssl default_server
13 listen 443 ssl default_server
14
15 root /var/www/html;
16
17 index index.html index.htm index.nginx-debian.html;
18
19 server_name my.vpn.dev;
20
21 location / {
22 proxy_set_header Host $host;
23 proxy_set_header X-Real-IP $remote_addr;
24 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
25 proxy_set_header X-Forwarded-Proto $scheme;
26 proxy_set_header Proxy "";
27 proxy_pass_header Server;
28
29 proxy_pass http://127.0.0.1:8000;
30 tcp_nodelay on;
31 }
32
33 ssl_certificate /etc/letsencrypt/live/my.vpn.dev/fullchain.pem; # managed by Certbot
34 ssl_certificate_key /etc/letsencrypt/live/my.vpn.dev/privkey.pem; # managed by Certbot
35 include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
36 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
37 }
This is a server-snippet that may be used as an item in
/etc/nginx/sites-available
and linked to /etc/nginx/sites-enabled
on
Debian or Ubuntu. Alternatively you may replace the server
section of your
/etc/nginx/nginx.conf
file.
You’ll have to replace the server_name
and the certificate paths with the
domain name of your server (here we used my.vpn.dev
).
As you can see the certificates are managed by certbot
. For instructions to
enable certbot
see below.
Certbot
To configure certbot
to manage your HTTPS certificates do the following:
Install
certbot
and it’s nginx pluginMake sure your DNS entries are working correctly (at least an
A
-Record)Configure your web-server like above
Run
certbot
without any parameters and follow the interactive prompt to enable HTTPS support for your domainInstall the auto-renewal-service:
systemctl daemon-reload && systemctl enable --now certbot.timer
Voila, your system will now automatically maintain it’s certificates.