From cafdfa70c1035462a607492dd1e69b0bd9df7b5b Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 22 Mar 2023 11:00:03 +0400 Subject: [PATCH] turn server poc --- compose/docker-compose.yml | 3 ++ config/config.go | 2 ++ docker/Caddyfile | 6 ++++ go.mod | 5 +++ go.sum | 16 +++++++++ logic/hosts.go | 2 ++ main.go | 14 ++++++++ models/structs.go | 2 ++ servercfg/serverconf.go | 29 ++++++++++++++- turnserver/turnserver.go | 73 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 turnserver/turnserver.go diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml index d27ae1ef..913d292e 100644 --- a/compose/docker-compose.yml +++ b/compose/docker-compose.yml @@ -28,9 +28,12 @@ services: MQ_PASSWORD: "REPLACE_MQ_PASSWORD" MQ_USERNAME: "REPLACE_MQ_USERNAME" STUN_PORT: "3478" + TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN" + TURN_PORT: "3479" DEFAULT_PROXY_MODE: "off" ports: - "3478:3478/udp" + - "3479:3479/udp" netmaker-ui: container_name: netmaker-ui image: gravitl/netmaker-ui:REPLACE_UI_IMAGE_TAG diff --git a/config/config.go b/config/config.go index 89979410..2476cc40 100644 --- a/config/config.go +++ b/config/config.go @@ -75,6 +75,8 @@ type ServerConfig struct { StunList string `yaml:"stun_list"` Proxy string `yaml:"proxy"` DefaultProxyMode ProxyMode `yaml:"defaultproxymode"` + TurnServer string `yaml:"turn_server"` + TurnPort int `yaml:"turn_port"` } // ProxyMode - default proxy mode for server diff --git a/docker/Caddyfile b/docker/Caddyfile index d8392b7d..42233f85 100644 --- a/docker/Caddyfile +++ b/docker/Caddyfile @@ -35,6 +35,12 @@ https://stun.NETMAKER_BASE_DOMAIN { reverse_proxy netmaker:3478 } +# TURN +https://stun.NETMAKER_BASE_DOMAIN { + reverse_proxy netmaker:3479 +} + + # MQ wss://broker.NETMAKER_BASE_DOMAIN { diff --git a/go.mod b/go.mod index c9dd7125..14b61e58 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/guumaster/tablewriter v0.0.10 github.com/matryer/is v1.4.1 github.com/olekukonko/tablewriter v0.0.5 + github.com/pion/turn/v2 v2.1.0 github.com/spf13/cobra v1.6.1 ) @@ -52,6 +53,10 @@ require ( github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/stun v0.4.0 // indirect + github.com/pion/transport/v2 v2.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index 0806a7dc..d36d08a9 100644 --- a/go.sum +++ b/go.sum @@ -101,6 +101,16 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/stun v0.4.0 h1:vgRrbBE2htWHy7l3Zsxckk7rkjnjOsSM7PHZnBwo8rk= +github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw= +github.com/pion/transport/v2 v2.0.0 h1:bsMYyqHCbkvHwj+eNCFBuxtlKndKfyGI2vaQmM3fIE4= +github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc= +github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI= +github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -139,6 +149,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E= @@ -170,6 +181,7 @@ golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= @@ -197,17 +209,21 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= diff --git a/logic/hosts.go b/logic/hosts.go index 19feda1a..8fd08147 100644 --- a/logic/hosts.go +++ b/logic/hosts.go @@ -11,6 +11,7 @@ import ( "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" + "github.com/gravitl/netmaker/turnserver" "golang.org/x/crypto/bcrypt" ) @@ -97,6 +98,7 @@ func CreateHost(h *models.Host) error { return err } h.HostPass = string(hash) + turnserver.RegisterNewHostWithTurn(h.ID.String(), h.HostPass) // if another server has already updated proxyenabled, leave it alone if !h.ProxyEnabledSet { log.Println("checking default proxy", servercfg.GetServerConfig().DefaultProxyMode) diff --git a/main.go b/main.go index 217defe3..b5858cec 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ import ( "github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/serverctl" stunserver "github.com/gravitl/netmaker/stun-server" + "github.com/gravitl/netmaker/turnserver" ) var version = "v0.18.4" @@ -112,6 +113,7 @@ func initialize() { // Client Mode Prereq Check logger.Log(0, "error occurred when notifying nodes of startup", err.Error()) } } + registerCurrHostsWithTurn() } func startControllers(wg *sync.WaitGroup, ctx context.Context) { @@ -146,6 +148,9 @@ func startControllers(wg *sync.WaitGroup, ctx context.Context) { // starts the stun server wg.Add(1) go stunserver.Start(wg, ctx) + // starts the turn server + wg.Add(1) + go turnserver.Start(wg, ctx) } // Should we be using a context vice a waitgroup???????????? @@ -185,3 +190,12 @@ func setGarbageCollection() { debug.SetGCPercent(ncutils.DEFAULT_GC_PERCENT) } } + +func registerCurrHostsWithTurn() { + hosts, err := logic.GetAllHosts() + if err == nil { + for _, hostI := range hosts { + turnserver.RegisterNewHostWithTurn(hostI.ID.String(), hostI.HostPass) + } + } +} diff --git a/models/structs.go b/models/structs.go index 6d1215fb..2d1d7018 100644 --- a/models/structs.go +++ b/models/structs.go @@ -1,6 +1,7 @@ package models import ( + "net" "strings" jwt "github.com/golang-jwt/jwt/v4" @@ -232,6 +233,7 @@ type ServerConfig struct { StunPort int `yaml:"stun_port"` StunList []StunServer `yaml:"stun_list"` TrafficKey []byte `yaml:"traffickey"` + TurnServer *net.UDPAddr `yaml:"turn_server"` } // User.NameInCharset - returns if name is in charset below or not diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index c59253d9..77e07e1b 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -2,7 +2,9 @@ package servercfg import ( "errors" + "fmt" "io" + "net" "net/http" "os" "strconv" @@ -102,7 +104,7 @@ func GetServerInfo() models.ServerConfig { cfg.Is_EE = Is_EE cfg.StunPort = GetStunPort() cfg.StunList = GetStunList() - + cfg.TurnServer, _ = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", GetTurnHost(), GetTurnPort())) return cfg } @@ -626,6 +628,31 @@ func GetStunPort() int { return port } +// GetTurnPort - Get the port to run the turn server on +func GetTurnPort() int { + port := 3479 //default + if os.Getenv("TURN_PORT") != "" { + portInt, err := strconv.Atoi(os.Getenv("TURN_PORT")) + if err == nil { + port = portInt + } + } else if config.Config.Server.TurnPort != 0 { + port = config.Config.Server.TurnPort + } + return port +} + +// GetTurnHost - fetches the turn host name +func GetTurnHost() string { + turnServer := "" + if os.Getenv("TURN_SERVER_HOST") != "" { + turnServer = os.Getenv("TURN_SERVER_HOST") + } else if config.Config.Server.TurnServer != "" { + turnServer = config.Config.Server.TurnServer + } + return turnServer +} + // IsProxyEnabled - is proxy on or off func IsProxyEnabled() bool { var enabled = false //default diff --git a/turnserver/turnserver.go b/turnserver/turnserver.go new file mode 100644 index 00000000..dea37b70 --- /dev/null +++ b/turnserver/turnserver.go @@ -0,0 +1,73 @@ +package turnserver + +import ( + "context" + "log" + "net" + "strconv" + "sync" + "time" + + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/servercfg" + "github.com/pion/turn/v2" +) + +var ( + UsersMap = make(map[string][]byte) +) + +func RegisterNewHostWithTurn(hostID, hostPass string) { + UsersMap[hostID] = turn.GenerateAuthKey(hostID, servercfg.GetTurnHost(), hostPass) +} + +func Start(wg *sync.WaitGroup, ctx context.Context) { + defer wg.Done() + // Create a UDP listener to pass into pion/turn + // pion/turn itself doesn't allocate any UDP sockets, but lets the user pass them in + // this allows us to add logging, storage or modify inbound/outbound traffic + udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(servercfg.GetTurnPort())) + if err != nil { + log.Panicf("Failed to create TURN server listener: %s", err) + } + + s, err := turn.NewServer(turn.ServerConfig{ + Realm: servercfg.GetTurnHost(), + // Set AuthHandler callback + // This is called every time a user tries to authenticate with the TURN server + // Return the key for that user, or false when no user is found + AuthHandler: func(username string, realm string, srcAddr net.Addr) ([]byte, bool) { + if key, ok := UsersMap[username]; ok { + return key, true + } + return nil, false + }, + // PacketConnConfigs is a list of UDP Listeners and the configuration around them + PacketConnConfigs: []turn.PacketConnConfig{ + { + PacketConn: udpListener, + RelayAddressGenerator: &turn.RelayAddressGeneratorStatic{ + RelayAddress: net.ParseIP("64.227.178.89"), // Claim that we are listening on IP passed by user (This should be your Public IP) + Address: "0.0.0.0", // But actually be listening on every interface + }, + }, + }, + }) + if err != nil { + log.Panic(err) + } + go func() { + for { + time.Sleep(time.Second * 10) + log.Print(s.AllocationCount()) + + } + }() + + // Block until user sends SIGINT or SIGTERM + <-ctx.Done() + logger.Log(0, "## Stopping Turn Server...") + if err = s.Close(); err != nil { + log.Panic(err) + } +}