From c93206e6d172b96e65f03154c809671addd740fb Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Thu, 29 Aug 2024 22:38:23 +1000 Subject: [PATCH] add network/tunnel package to establish user space WG tunnel to a machine --- internal/machine/network/tunnel/tunnel.go | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 internal/machine/network/tunnel/tunnel.go diff --git a/internal/machine/network/tunnel/tunnel.go b/internal/machine/network/tunnel/tunnel.go new file mode 100644 index 00000000..101f6501 --- /dev/null +++ b/internal/machine/network/tunnel/tunnel.go @@ -0,0 +1,96 @@ +package tunnel + +import ( + "context" + "fmt" + "golang.zx2c4.com/wireguard/conn" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun/netstack" + "net" + "net/netip" + "time" + "uncloud/internal/secret" +) + +const ( + DefaultEndpointPort = 51820 + // DefaultKeepaliveInterval is sensible interval that works with a wide variety of firewalls. + DefaultKeepaliveInterval = 25 * time.Second +) + +type Tunnel struct { + dev *device.Device + net *netstack.Net +} + +type Config struct { + LocalAddress netip.Addr + LocalPrivateKey secret.Secret + Endpoint netip.AddrPort + RemotePublicKey secret.Secret + RemoteNetwork netip.Prefix + DNS *netip.Addr + MTU int + KeepAlive time.Duration +} + +func Connect(config *Config) (*Tunnel, error) { + var dns netip.Addr + if config.DNS != nil { + dns = *config.DNS + } else { + dns = netip.MustParseAddr("1.1.1.1") + } + mtu := config.MTU + if mtu == 0 { + mtu = device.DefaultMTU + } + keepAlive := config.KeepAlive + if keepAlive == 0 { + keepAlive = DefaultKeepaliveInterval + } + + tun, tnet, err := netstack.CreateNetTUN([]netip.Addr{config.LocalAddress}, []netip.Addr{dns}, mtu) + if err != nil { + return nil, fmt.Errorf("create WireGuard TUN device: %w", err) + } + + dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(device.LogLevelVerbose, "WireGuard tunnel: ")) + conf := fmt.Sprintf( + "private_key=%s\n"+ + "public_key=%s\n"+ + "endpoint=%s\n"+ + "allowed_ip=%s\n"+ + "persistent_keepalive_interval=%d\n", + config.LocalPrivateKey.String(), + config.RemotePublicKey.String(), + config.Endpoint.String(), + config.RemoteNetwork.String(), + int(keepAlive.Seconds()), + ) + err = dev.IpcSet(conf) + if err != nil { + return nil, fmt.Errorf("configure WireGuard device: %w", err) + } + + err = dev.Up() + if err != nil { + return nil, fmt.Errorf("enable WireGuard device: %w", err) + } + + return &Tunnel{ + dev: dev, + net: tnet, + }, nil +} + +func (t *Tunnel) Close() { + if t.dev != nil { + t.dev.Close() + } + t.dev, t.net = nil, nil +} + +func (t *Tunnel) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return t.net.DialContext(ctx, network, address) +}