Go语言 如何仅使用UDP通过WireGuard协议进行REST调用?

ryoqjall  于 11个月前  发布在  Go
关注(0)|答案(2)|浏览(163)

我需要调用WireGuard VPN内部的REST API。WireGuard VPN是客户端管理的,所以我不能修改它。我只是提供了一个配置文件来访问VPN。
我需要一个仅代码的解决方案,可以发送UDP数据包的功能。我的应用程序无法修改我的网络或REST API服务器网络的网络配置。
这个代码解决方案可能使用Go、Python或JavaScript,因为我们现有的代码库已经使用了所有这些语言。
如果在Java或C#中有足够有用的解决方案,我们可以将这些语言添加到构建链中。

wydwbb8l

wydwbb8l1#

WireGuard被设计为用作网络接口,而不是应用程序编程接口。它没有API。
如果WireGuard网络与客户端连接的其他网络之间存在路由冲突,并且客户端运行的是Linux,则可以在客户端上使用策略路由规则,以确保只有特定应用程序或用户的流量路由到客户端的WireGuard接口。或者,可以在客户端上的单独网络命名空间中运行应用程序,并将WireGuard接口仅附加到该名称空间。
如果你真的想在不设置网络接口的情况下发送和接收WireGuard流量,你可以使用wireguard-go客户端创建一个自定义应用程序,并使用Go的虚拟网络堆栈(来自gVisor项目)来设置一个只能从该应用程序内部访问的虚拟WireGuard接口。wireguard-go 源代码包括一个很好的示例,可以这样做来获取HTTP资源:

客户端WireGuard IP肯定会相互冲突,
无论您做什么,您都必须为每个客户端提供其在WireGuard网络中的IP地址以及其自己的密钥对。如果连接的REST API端的WireGuard服务器几乎同时接收到来自两个使用相同IP地址或密钥对的不同客户端的请求,(即,在它可以向另一个发送响应之前接收到来自一个的请求),它将尝试向它从其接收到最后一个请求的客户端发送两个响应(并且不向另一个客户端发送任何响应)。

rqcrx0a6

rqcrx0a62#

我独立找到了Justin Ludwig引用的the same library,并使用它实现了完整的解决方案。
这似乎允许编程调用WireGuard VPN背后的任何REST服务。
这听起来可能有些矫枉过正(gVisor“实现了Linux系统表面的相当大一部分”?!),然而它可以工作,并且对于我的用例来说足够快。

package main

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

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

func restCall(
    privateKey string,
    publicKey string,
    allowedIP string,
    endpoint string,
    selfIP string,
    dnsIP string,
    url string,
) []byte {
    tun, tnet, err := netstack.CreateNetTUN(
        []netip.Addr{netip.MustParseAddr(selfIP)},
        []netip.Addr{netip.MustParseAddr(dnsIP)},
        1420)
    if err != nil {
        log.Panic(err)
    }
    dev := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger(device.LogLevelVerbose, ""))

    err = dev.IpcSet(`private_key=` + privateKey + `
public_key=` + publicKey + `
allowed_ip=` + allowedIP + `
endpoint=` + endpoint + `
`)
    if err != nil {
        log.Panic(err)
    }
    err = dev.Up()
    if err != nil {
        log.Panic(err)
    }

    client := http.Client{
        Transport: &http.Transport{
            DialContext: tnet.DialContext,
        },
    }
    resp, err := client.Get(url)
    if err != nil {
        log.Panic(err)
    }
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Panic(err)
    }
    print("Finished request", string(body)[:100])

    return body
}

func main() {
    restCall(
        "389fb0fbadd9880e09b7278f1712f0...",
        "679d50a357ebc91e602c07d9af88f4...",
        "0.0.0.0/0",
        "X.X.X.131:21576",
        "192.168.X.X",
        "10.0.0.254",
        "http://X/apicall",
    )
}

字符集

相关问题