Wireguard – A Server With Docker

My OpenVPN installations are aging a bit and every time I go from one major operating system version on the server to the next, it takes some effort to adapt the installation. As I am coming closer to such a point again, I was wondering if I should rather spend my time on a different approach. In recent years, an interesting alternative has gained popularity: Wireguard. Now that Ubuntu 24.04 Desktop comes with built-in Wireguard client support, I thought I’d have another look and see how much effort would be required setting up a Wireguard VPN server.

After looking around a bit I decided to go for ‘wg-easy’, a Docker encapsulated Wireguard server that comes with a built-in web-based client certificate management front-end. Also, the project supplies a docker-compose.yml file, so getting an instance up and running in a VM is as easy as adapting a few lines in the yml file and starting the container.

WG-Easy Config in a Nutshell

To get started, the only two things that need to be modified in the yml file are:

1) The WG_HOST parameter. This is where the public IP address or domain name of the host goes. In case there is a NAT in front of the machine, UDP port 51820 for Wireguard itself and TCP port 51821 for the http admin interface must be forwarded.

WG_HOST=<IP address or DOMAIN NAME>

Note: As far as I can tell, this parameter is only relevant for the client config files that are later generated by the admin web-ui. I changed this value on a server after installation after it was already running and there were no ill effects.

2) The admin web interface needs a password and its hash needs to be put into the yml file. Details how the hash can be generated are described here. IMPORTANT: Don’t overlook that it is necessary to replace all $ signs in the hash with $$ and not only the first one you see in the string. Long story short, here’s the command:

docker run --rm -it ghcr.io/wg-easy/wg-easy wgpw 'YOUR_PASSWORD'

3) One optional thing to modify in the yml file is the location of the volume where configuration data is stored. I prefer to store it in a local directory where the docker compose file is located, so I changed the volume line as follows:

volumes:
  - ./etc_wireguard:/etc/wireguard

And that’s it, a ‘docker compose up -d‘ then starts the server. The http web interface on port 51821 can then be used to generate individual configuration files for users. These can then be directly imported on the client devices. I tried with Ubuntu 24.04 and it worked out of the box. Ubuntu 22.04 required a bit more work and had some shortcomings. More about that in follow up posts.

15 Character Profile Names

One important note at this point:The profile name is limited to15 characters. On Ubuntu 22.04, longer filenames result in a mysterious ‘interface not found’ error when a Wireguard connection is started.

Web-UI Security

You might have noticed that the project itself only offers a non-encrypted http based web interface for creating user profiles. For a production server, this needs to be secured. One option is to put this behind a web proxy. Another option, which I went for in the end, is to limit TCP port 51821 to localhost. This makes the web interface only accessible directly from the host and not over the network. Here’s how to do this in the yml file:

ports:
  - "51820:51820/udp"
  - "127.0.0.1:51821:51821/tcp"

To access the web interface over the network, I use an ssh connection with port forwarding to my notebook. Here’s the command for my admin notebook:

ssh -L 51821:localhost:51821 HOSTNAME

In my local browser on the notebook I can then access the remote server as follows:

http://localhost:51821/

All data to and from the the Wireguard admin-ui thus flows over the ssh connection and is secure. Even better, the http port is not even exposed to the outside world.

Docker and Host Security

There is one other security topic you should be aware of: As Wireguard is a kernel feature, the Docker container it runs in must be allowed to make changes to the network and kernel configuration. In the yml file, this is enabled as follows:

cap_add:
  - NET_ADMIN
  - SYS_MODULE

Particularly the SYS_MODULE capability gives the Wireguard container a lot of rights outside the container on the physical or virtual host, and it is hence no longer limited to changes that only affect the container itself. Therefore, I chose to run my containerized Wireguard VPN server in dedicated VM rather than on the VM that is the home of the containers of most of my other projects.

And there we go, a few adaptations in the template configuration file and that Wireguard VPN server is up and running. Need another one somewhere else? Just take your yml file, change the password and the IP/domain name in the config file, and you’ve got another instance with a simple ‘docker compose up -d’. Really nice!

So much for now. In a number of follow up posts, I’ll have a look of how to use the VPN server with clients on Ubuntu 22.04, 24.04 and Android.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.