Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/urnetwork/glog v0.0.0
github.com/urnetwork/proxy v0.0.0
github.com/urnetwork/sdk v0.0.0
github.com/urnetwork/userwireguard v0.0.0
golang.org/x/crypto v0.47.0
golang.org/x/exp v0.0.0-20260112195511-716be5621a96
google.golang.org/protobuf v1.36.11
Expand Down Expand Up @@ -76,6 +77,7 @@ require (
golang.org/x/net v0.49.0 // indirect
golang.org/x/term v0.39.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 // indirect
gvisor.dev/gvisor v0.0.0-20260202191832-0bd9aedd142c // indirect
)

Expand Down Expand Up @@ -106,3 +108,5 @@ replace github.com/urnetwork/proxy => ../proxy
replace github.com/urnetwork/sdk => ../sdk

replace github.com/urnetwork/glog => ../glog

replace github.com/urnetwork/userwireguard => ../userwireguard
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
27 changes: 14 additions & 13 deletions model/network_client_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,8 @@ type AuthNetworkClientError struct {
}

type ProxyConfigResult struct {
KeepaliveSeconds int `json:"keepalive_seconds"`
SocksProxyUrl string `json:"socks_proxy_url"`
HttpProxyUrl string `json:"http_proxy_url"`
HttpsProxyUrl string `json:"https_proxy_url"`
ApiBaseUrl string `json:"api_base_url"`
AuthToken string `json:"auth_token"`
InstanceId server.Id `json:"instance_id"`
ProxyHost string `json:"proxy_host"`
HttpProxyPort int `json:"http_proxy_port"`
HttpsProxyPort int `json:"https_proxy_port"`
SocksProxyPort int `json:"socks_proxy_port"`
ApiPort int `json:"api_port"`
KeepaliveSeconds int `json:"keepalive_seconds"`
ProxyClient
}

type ProxyAuthResult struct {
Expand Down Expand Up @@ -323,7 +313,8 @@ func AuthNetworkClient(
apiPort,
)

authClientResult.ProxyConfigResult = &ProxyConfigResult{
proxyClient := &ProxyClient{
ProxyId: proxyDeviceConfig.ProxyId,
SocksProxyUrl: socksProxyUrl,
HttpProxyUrl: httpProxyUrl,
HttpsProxyUrl: httpsProxyUrl,
Expand All @@ -336,6 +327,16 @@ func AuthNetworkClient(
HttpsProxyPort: httpsProxyPort,
ApiPort: apiPort,
}
err = CreateProxyClient(session.Ctx, proxyClient)
if err == nil {
authClientResult.ProxyConfigResult = &ProxyConfigResult{
ProxyClient: *proxyClient,
}
} else {
authClientResult.Error = &AuthNetworkClientError{
Message: "Could not create proxy client",
}
}
} else {
authClientResult.Error = &AuthNetworkClientError{
Message: "Could not create proxy device",
Expand Down
32 changes: 32 additions & 0 deletions model/network_client_proxy_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,35 @@ func GetConnectLocationForCountryCode(ctx context.Context, countryCode string) *
CountryLocationId: server.ToSdkId(c.LocationId),
}
}

// FIXME store this after the proxy clinet is created
type ProxyClient struct {
ChangeId int `json:"change_id,omitempty"`
ProxyId server.Id `json:"proxy_id"`
SocksProxyUrl string `json:"socks_proxy_url"`
HttpProxyUrl string `json:"http_proxy_url"`
HttpsProxyUrl string `json:"https_proxy_url"`
ApiBaseUrl string `json:"api_base_url"`
AuthToken string `json:"auth_token"`
InstanceId server.Id `json:"instance_id"`
ProxyHost string `json:"proxy_host"`
HttpProxyPort int `json:"http_proxy_port"`
HttpsProxyPort int `json:"https_proxy_port"`
SocksProxyPort int `json:"socks_proxy_port"`
ApiPort int `json:"api_port"`

WgProxyPort int
WgClientPublicKey string
WgServerPublicKey string
WgClientIpv4 string
}

// FIXME allocate the change id
func CreateProxyClient(ctx context.Context, proxyClient *ProxyClient) (returnErr error) {
return nil
}

// FIXME
func GetProxyClientsSince(ctx context.Context, changeId int) ([]*ProxyClient, int) {
return nil, 0
}
155 changes: 71 additions & 84 deletions proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ package main

import (
"context"
"crypto/tls"
// "crypto/tls"
"encoding/base64"
"encoding/json"
// "encoding/json"
"fmt"
"io"
// "io"
"net"
"net/http"
"net/netip"
"os"
"strconv"
// "strconv"
"strings"
"sync"
"syscall"
"time"

Expand All @@ -24,7 +25,7 @@ import (
"github.com/urnetwork/proxy"
"github.com/urnetwork/server"
"github.com/urnetwork/server/model"
"github.com/urnetwork/server/router"
// "github.com/urnetwork/server/router"
)

// FIXME this is meant to be deployed with no lb and no containers
Expand All @@ -44,6 +45,7 @@ const ListenSocksPort = 8080
const ListenHttpPort = 8081
const ListenHttpsPort = 8082
const ListenApiPort = 8083
const ListenWgPort = 8084

func DefaultProxySettings() *ProxySettings {
return &ProxySettings{
Expand All @@ -59,6 +61,7 @@ type ProxySettings struct {
ProxyWriteTimeout time.Duration
ProxyIdleTimeout time.Duration
ProxyTlsHandshakeTimeout time.Duration
NotificationTimeout time.Duration
}

func main() {
Expand Down Expand Up @@ -121,8 +124,26 @@ func main() {
settings,
)

wg := newWgServer(
ctx,
cancel,
proxyDeviceManager,
settings,
)

newWatchdog(ctx, 5*time.Second)

notif := newProxyClientNotification(ctx, settings)
sub := notif.AddProxyClientsCallback(func(proxyClients []*model.ProxyClient) {
for _, proxyClient := range proxyClients {
// warm up the device
proxyDeviceManager.OpenProxyDevice(proxyClient.ProxyId)
}

wg.AddProxyClients(proxyClients)
})
defer sub()

select {
case <-ctx.Done():
}
Expand Down Expand Up @@ -325,115 +346,81 @@ func (self *httpServer) run() {
}
}

type apiServer struct {
var wgPrivateKey = sync.OnceValue(func() string {
wgKeys := server.Vault.RequireSimpleResource("wg.yml")
return wgKeys.RequireString("private_key")
})

var wgPublicKey = sync.OnceValue(func() string {
wgKeys := server.Vault.RequireSimpleResource("wg.yml")
return wgKeys.RequireString("public_key")
})

type wgServer struct {
ctx context.Context
cancel context.CancelFunc
proxyDeviceManager *ProxyDeviceManager
transportTls *server.TransportTls
settings *ProxySettings
wgProxy *proxy.WgProxy
}

func newApiServer(
func newWgServer(
ctx context.Context,
cancel context.CancelFunc,
proxyDeviceManager *ProxyDeviceManager,
transportTls *server.TransportTls,
settings *ProxySettings,
) *apiServer {
s := &apiServer{
) *wgServer {

wgProxySettings := proxy.DefaultWgProxySettings()
wgProxySettings.PrivateKey = wgPrivateKey()
wgProxy := proxy.NewWgProxy(ctx, wgProxySettings)

s := &wgServer{
ctx: ctx,
cancel: cancel,
proxyDeviceManager: proxyDeviceManager,
transportTls: transportTls,
settings: settings,
wgProxy: wgProxy,
}

go server.HandleError(s.run, cancel)

return s
}

func (self *apiServer) run() {
func (self *wgServer) run() {
defer self.cancel()

routes := []*router.Route{
router.NewRoute("POST", "/warmup", self.HandleWarmup),
}

reusePort := false

httpServerOptions := server.HttpServerOptions{
ReadTimeout: 15 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 5 * time.Minute,
}

tlsConfig := &tls.Config{
GetConfigForClient: self.transportTls.GetTlsConfigForClient,
}

err := server.HttpListenAndServeTlsWithReusePort(
self.ctx,
net.JoinHostPort("", strconv.Itoa(ListenApiPort)),
router.NewRouter(self.ctx, routes),
reusePort,
httpServerOptions,
tlsConfig,
)
err := self.wgProxy.ListenAndServe("udp", fmt.Sprintf(":%d", ListenWgPort))
if err != nil {
panic(err)
}
}

type WarmupRequest struct {
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
}

type WarmupResponse struct {
Ready bool `json:"ready"`
}

func (self *apiServer) HandleWarmup(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
proxyId, err := authHeaderProxyId(authHeader)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}

var warmupRequest WarmupRequest

defer r.Body.Close()
bodyBytes, err := io.ReadAll(r.Body)

if 0 < len(bodyBytes) {
err = json.Unmarshal(bodyBytes, &warmupRequest)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
func (self *wgServer) AddProxyClients(proxyClients []*model.ProxyClient) {
var clients map[netip.Addr]*proxy.WgClient
for _, proxyClient := range proxyClients {
if 0 < len(proxyClient.WgClientPublicKey) && proxyClient.WgServerPublicKey == wgPublicKey() {
// verify that the access token is still valid
proxyId, err := model.ParseSignedProxyId(proxyClient.AuthToken)
if err == nil && proxyId == proxyClient.ProxyId {
proxyDevice, err := self.proxyDeviceManager.OpenProxyDevice(proxyClient.ProxyId)
if err == nil {
addr, err := netip.ParseAddr(proxyClient.WgClientIpv4)
if err == nil {
client := &proxy.WgClient{
PublicKey: proxyClient.WgClientPublicKey,
PresharedKey: proxyClient.AuthToken,
ClientIpv4: addr,
Tun: proxyDevice.Tun(),
}
clients[addr] = client
}
}
}
}
}
// else use the default object

proxyDevice, err := self.proxyDeviceManager.OpenProxyDevice(proxyId)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

timeout := time.Duration(warmupRequest.TimeoutSeconds) * time.Second
ready := proxyDevice.WaitForReady(r.Context(), timeout)

warmupResponse := &WarmupResponse{
Ready: ready,
}

out, err := json.Marshal(warmupResponse)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(out)
self.wgProxy.AddClients(clients)
}

func authHeaderProxyId(authHeader string) (server.Id, error) {
Expand Down
Loading