Creating a Simple VPN Client for my Friends

GUI of the VPN client

I enjoy self-hosting a variety of services, including my photo collection, document management system, smart home setup, and password manager. To access these services securely from outside my home, I use WireGuard VPN.

While this works well for my own devices, inviting others to use my systems can be quite a hassle.
The main reason I invite friends to join my home VPN is to play on our Factorio server, which I also host locally. This requires me to share the WireGuard client installer, guide them through the setup, distribute configuration files, and help with the connection process.

Therefore, I wanted a simple solution that anyone could use without requiring technical knowledge. My idea was to build a lightweight Windows GUI that comes preloaded with the necessary configuration files and includes only the most essential features.

I explored several ways to control WireGuard from my own Windows application. The simplest method I found was using the command-line interface provided by wireguard.exe:

Output of wireguard /help

Using the information from this output together with some own research, I was able to implement a custom interface to Wireguard in C++:

void MainWindow::slotSetup()
{
	runWireguardWithAction("/installmanagerservice");
}

void MainWindow::slotConnect()
{
    runWireguardWithAction(
      std::format("/installtunnelservice \"{}\\autoconnect.conf\"",
        std::filesystem::current_path().string()));
}

void MainWindow::slotDisconnect()
{
	runWireguardWithAction("/uninstalltunnelservice autoconnect");
}

What took me the most time to figure out was that the /installtunnelservice command requires an absolute path to the configuration file.

I built a simple GUI using Qt6 with some convenient features—such as disabling buttons based on the service status.

void MainWindow::updateUI()
{
	// windowsServiceExists: Small custom method based on the Windows API
	const auto managerServiceExists = windowsServiceExists("WireGuardManager");
	const auto tunnelServiceExists = windowsServiceExists("WireGuardTunnel$autoconnect");

	m_pMainWindow->buttonSetup->setEnabled(!managerServiceExists);
	m_pMainWindow->buttonConnect->setEnabled(!tunnelServiceExists);
	m_pMainWindow->buttonDisconnect->setEnabled(tunnelServiceExists);
}

The Result

The result is a small program that I can distribute along with WireGuard, a configuration file, and a few DLLs in a compact ZIP file. Now, my friends can easily connect to my home network without needing to install or configure anything.

Many thanks to Flo for his hard work as a beta-Tester.