Userspace Networking Stack + WireGuard + Go

Jason A. Donenfeld Jason at zx2c4.com
Wed Jan 13 16:04:29 UTC 2021


Hi,

Sometimes people say things like, "I want to make an application that
connects to the server over WireGuard" or "I want to host this service
application behind WireGuard" or "I want this small utility to initiate
an SSH connection to a specific server over WireGuard." The usual answer
to this is to simply bring up a WireGuard interface on the computer, and
then do networking as usual. And making WireGuard easy enough that
people can pull up ad-hoc interfaces trivially has been a big motivating
factor for the simplicity of the tooling, such as `ip link add wg0 type
wireguard && wg set wg0 ...` and similar commands.

On Linux, adding an interface is easy to do and is a solution that fits
nearly all circumstances. Even if you're unprivileged and want a
WireGuard interface for just a single application that's bound to the
lifetime of that application, you can still use WireGuard's normal
kernel interface inside of a user namespace + a network namespace, and
get a private process-specific WireGuard interface. Pretty cool stuff.
It's not like that's an accident, of course -- WireGuard was originally
developed with Linux's model in mind.

But not all operating systems are as neat as Linux. To that end, we've
tried very hard to make WireGuard integrate as natively as possible into
other operating systems, with native clients for every platform, as well
as APIs for every platform to allow application developers to embed
tunnels: https://www.wireguard.com/embedding/ . This is great and works
well. WireGuard's design centers around the "network interface" as the
central object, and that's what these libraries enable. But still, some
people do not want to think about network interfaces or operating
systems when writing code.

To that end, I've recently written some code that couples a userspace
networking stack (from gVisor) with a userspace WireGuard implementation
(from wireguard-go), along with a little custom DNS client
implementation, so that Go applications can use an in-process WireGuard
interface directly to make TCP and UDP connections and listeners. From
the operating system's point of view, the application just sends and
receives encrypted UDP packets, and never sees the inner TCP or UDP
packets or knows about WireGuard at all.

Here's a quick example of what's possible:

    package main

    import (
        "io"
        "log"
        "net"
        "net/http"

        "golang.zx2c4.com/wireguard/device"
        "golang.zx2c4.com/wireguard/tun"
    )

    func main() {
        tun, tnet, err := tun.CreateNetTUN(
            []net.IP{net.ParseIP("192.168.4.29")},
	    []net.IP{net.ParseIP("8.8.8.8")},
	    1420)
        if err != nil {
            log.Panic(err)
        }
        dev := device.NewDevice(tun, &device.Logger{log.Default(), log.Default(), log.Default()})
        dev.IpcSet(`private_key=a8dac1d8a70a751f0f699fb14ba1cff7b79cf4fbd8f09f44c6e6a90d0369604f
    public_key=25123c5dcd3328ff645e4f2a3fce0d754400d3887a0cb7c56f0267e20fbf3c5b
    endpoint=163.172.161.0:12912
    allowed_ip=0.0.0.0/0
    `)
        dev.Up()

        client := http.Client{
            Transport: &http.Transport{
                DialContext: tnet.DialContext,
            },
        }
        resp, err := client.Get("https://www.zx2c4.com/ip")
        if err != nil {
            log.Panic(err)
        }
        body, err := io.ReadAll(resp.Body)
        if err != nil {
            log.Panic(err)
        }
        log.Println(string(body))
    }

This snippet prints out:

    163.172.161.0
    demo.wireguard.com
    Go-http-client/1.1

, because that HTTPS request is going through the demo box.

Other use cases of this include cloud VM providers to bundle an ssh
client directly into their tooling that connects to a "cloud private
network" over WireGuard. Or hosting utilities that allow people to run
web apps from their own computers that are then accessible on the
Internet by connecting in reverse through WireGuard. Or maybe other use
cases.

The API and the code is new, and potentially incomplete. Please take it
for a spin and let me know your feedback.

Enjoy,
Jason

PS: While the gophers on the list might be happy about this, the
rustaceans may well be left wondering, "what about us?" I hope to have
some positive news about wireguard-rs not before too long.


More information about the WireGuard mailing list