NET-814: Deprecating TURN (#2723)

* deprecate turn

* process signals through mq
This commit is contained in:
Abhishek K 2023-12-13 22:46:57 +04:00 committed by GitHub
parent 9f1b722c19
commit 98c01c4325
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 43 additions and 304 deletions

View file

@ -25,10 +25,6 @@ services:
- COREDNS_ADDR=${SERVER_HOST}
# Overrides SERVER_HOST if set. Useful for making HTTP available via different interfaces/networks.
- SERVER_HTTP_HOST=api.${NM_DOMAIN}
# domain for your turn server
- TURN_SERVER_HOST=turn.${NM_DOMAIN}
# domain of the turn api server
- TURN_SERVER_API_HOST=https://turnapi.${NM_DOMAIN}
netmaker-ui:
container_name: netmaker-ui
@ -82,22 +78,6 @@ services:
- ./wait.sh:/mosquitto/config/wait.sh
- mosquitto_logs:/mosquitto/log
- mosquitto_data:/mosquitto/data
turn:
container_name: turn
image: gravitl/turnserver:v1.0.0
env_file: ./netmaker.env
environment:
# config-dependant vars
- USERNAME=${TURN_USERNAME}
- PASSWORD=${TURN_PASSWORD}
# domain for your turn server
- TURN_SERVER_HOST=turn.${NM_DOMAIN}
network_mode: "host"
volumes:
- turn_server:/etc/config
restart: always
volumes:
caddy_data: { } # runtime data for caddy
caddy_conf: { } # configuration file for Caddy
@ -105,4 +85,4 @@ volumes:
dnsconfig: { } # storage for coredns
mosquitto_logs: { } # storage for mqtt logs
mosquitto_data: { } # storage for mqtt data
turn_server: { }

View file

@ -231,13 +231,6 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
// re-register host with turn just in case.
if servercfg.IsUsingTurn() {
err = logic.RegisterHostWithTurn(newHost.ID.String(), newHost.HostPass)
if err != nil {
logger.Log(0, "failed to register host with turn server: ", err.Error())
}
}
// check if host already exists
hostExists := false
if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 {

View file

@ -544,19 +544,14 @@ func signalPeer(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if signal.ToHostPubKey == "" || (!servercfg.IsPro && signal.TurnRelayEndpoint == "") {
if signal.ToHostPubKey == "" {
msg := "insufficient data to signal peer"
logger.Log(0, r.Header.Get("user"), msg)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New(msg), "badrequest"))
return
}
signal.IsPro = servercfg.IsPro
var peerHost *models.Host
if signal.ToHostID == "" {
peerHost, err = logic.GetHostByPubKey(signal.ToHostPubKey)
} else {
peerHost, err = logic.GetHost(signal.ToHostID)
}
peerHost, err := logic.GetHost(signal.ToHostID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to signal, peer not found"), "badrequest"))
return

View file

@ -24,16 +24,6 @@ https://api.{$NM_DOMAIN} {
reverse_proxy http://netmaker:8081
}
# TURN
https://turn.{$NM_DOMAIN} {
reverse_proxy host.docker.internal:3479
}
# TURN API
https://turnapi.{$NM_DOMAIN} {
reverse_proxy http://host.docker.internal:8089
}
# MQ
wss://broker.{$NM_DOMAIN} {
reverse_proxy ws://mq:8883 # For EMQX websockets use `reverse_proxy ws://mq:8083`

View file

@ -39,16 +39,6 @@ https://api.{$NM_DOMAIN} {
reverse_proxy http://netmaker:8081
}
# TURN
https://turn.{$NM_DOMAIN} {
reverse_proxy host.docker.internal:3479
}
# TURN API
https://turnapi.{$NM_DOMAIN} {
reverse_proxy http://host.docker.internal:8089
}
# MQ
wss://broker.{$NM_DOMAIN} {
reverse_proxy ws://mq:8883

View file

@ -2,16 +2,12 @@ package logic
import (
"crypto/md5"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
"strconv"
"sync"
"github.com/devilcove/httpclient"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
@ -197,12 +193,6 @@ func CreateHost(h *models.Host) error {
if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
return ErrHostExists
}
if servercfg.IsUsingTurn() {
err = RegisterHostWithTurn(h.ID.String(), h.HostPass)
if err != nil {
logger.Log(0, "failed to register host with turn server: ", err.Error())
}
}
// encrypt that password so we never see it
hash, err := bcrypt.GenerateFromPassword([]byte(h.HostPass), 5)
@ -306,10 +296,6 @@ func RemoveHost(h *models.Host, forceDelete bool) error {
return fmt.Errorf("host still has associated nodes")
}
if servercfg.IsUsingTurn() {
DeRegisterHostWithTurn(h.ID.String())
}
if len(h.Nodes) > 0 {
if err := DisassociateAllNodesFromHost(h.ID.String()); err != nil {
return err
@ -329,9 +315,6 @@ func RemoveHost(h *models.Host, forceDelete bool) error {
// RemoveHostByID - removes a given host by id from server
func RemoveHostByID(hostID string) error {
if servercfg.IsUsingTurn() {
DeRegisterHostWithTurn(hostID)
}
err := database.DeleteRecord(database.HOSTS_TABLE_NAME, hostID)
if err != nil {
@ -568,52 +551,6 @@ func ConvHostPassToHash(hostPass string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(hostPass)))
}
// RegisterHostWithTurn - registers the host with the given turn server
func RegisterHostWithTurn(hostID, hostPass string) error {
auth := servercfg.GetTurnUserName() + ":" + servercfg.GetTurnPassword()
api := httpclient.JSONEndpoint[models.SuccessResponse, models.ErrorResponse]{
URL: servercfg.GetTurnApiHost(),
Route: "/api/v1/host/register",
Method: http.MethodPost,
Authorization: fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth))),
Data: models.HostTurnRegister{
HostID: hostID,
HostPassHash: ConvHostPassToHash(hostPass),
},
Response: models.SuccessResponse{},
ErrorResponse: models.ErrorResponse{},
}
_, errData, err := api.GetJSON(models.SuccessResponse{}, models.ErrorResponse{})
if err != nil {
if errors.Is(err, httpclient.ErrStatus) {
logger.Log(1, "error server status", strconv.Itoa(errData.Code), errData.Message)
}
return err
}
return nil
}
// DeRegisterHostWithTurn - to be called when host need to be deregistered from a turn server
func DeRegisterHostWithTurn(hostID string) error {
auth := servercfg.GetTurnUserName() + ":" + servercfg.GetTurnPassword()
api := httpclient.JSONEndpoint[models.SuccessResponse, models.ErrorResponse]{
URL: servercfg.GetTurnApiHost(),
Route: fmt.Sprintf("/api/v1/host/deregister?host_id=%s", hostID),
Method: http.MethodPost,
Authorization: fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth))),
Response: models.SuccessResponse{},
ErrorResponse: models.ErrorResponse{},
}
_, errData, err := api.GetJSON(models.SuccessResponse{}, models.ErrorResponse{})
if err != nil {
if errors.Is(err, httpclient.ErrStatus) {
logger.Log(1, "error server status", strconv.Itoa(errData.Code), errData.Message)
}
return err
}
return nil
}
// SortApiHosts - Sorts slice of ApiHosts by their ID alphabetically with numbers first
func SortApiHosts(unsortedHosts []models.ApiHost) {
sort.Slice(unsortedHosts, func(i, j int) bool {

View file

@ -110,8 +110,6 @@ const (
RequestAck HostMqAction = "REQ_ACK"
// CheckIn - update last check in times and public address and interfaces
CheckIn HostMqAction = "CHECK_IN"
// RegisterWithTurn - registers host with turn server if configured
RegisterWithTurn HostMqAction = "REGISTER_WITH_TURN"
// UpdateKeys - update wireguard private/public keys
UpdateKeys HostMqAction = "UPDATE_KEYS"
// RequestPull - request a pull from a host
@ -122,8 +120,6 @@ const (
type SignalAction string
const (
// Disconnect - action to stop using turn connection
Disconnect SignalAction = "DISCONNECT"
// ConnNegotiation - action to negotiate connection between peers
ConnNegotiation SignalAction = "CONNECTION_NEGOTIATION"
// RelayME - action to relay the peer
@ -146,18 +142,17 @@ type HostTurnRegister struct {
// Signal - struct for signalling peer
type Signal struct {
Server string `json:"server"`
FromHostPubKey string `json:"from_host_pubkey"`
TurnRelayEndpoint string `json:"turn_relay_addr"`
ToHostPubKey string `json:"to_host_pubkey"`
FromHostID string `json:"from_host_id"`
ToHostID string `json:"to_host_id"`
FromNodeID string `json:"from_node_id"`
ToNodeID string `json:"to_node_id"`
Reply bool `json:"reply"`
Action SignalAction `json:"action"`
IsPro bool `json:"is_pro"`
TimeStamp int64 `json:"timestamp"`
Server string `json:"server"`
FromHostPubKey string `json:"from_host_pubkey"`
ToHostPubKey string `json:"to_host_pubkey"`
FromHostID string `json:"from_host_id"`
ToHostID string `json:"to_host_id"`
FromNodeID string `json:"from_node_id"`
ToNodeID string `json:"to_node_id"`
Reply bool `json:"reply"`
Action SignalAction `json:"action"`
IsPro bool `json:"is_pro"`
TimeStamp int64 `json:"timestamp"`
}
// RegisterMsg - login message struct for hosts to join via SSO login

View file

@ -264,11 +264,7 @@ type ServerConfig struct {
Server string `yaml:"server"`
Broker string `yaml:"broker"`
IsPro bool `yaml:"isee" json:"Is_EE"`
StunPort int `yaml:"stun_port"`
TrafficKey []byte `yaml:"traffickey"`
TurnDomain string `yaml:"turn_domain"`
TurnPort int `yaml:"turn_port"`
UseTurn bool `yaml:"use_turn"`
}
// User.NameInCharset - returns if name is in charset below or not

View file

@ -7,6 +7,7 @@ import (
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/hostactions"
"github.com/gravitl/netmaker/models"
@ -193,14 +194,8 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
return
}
sendPeerUpdate = true
case models.RegisterWithTurn:
if servercfg.IsUsingTurn() {
err = logic.RegisterHostWithTurn(hostUpdate.Host.ID.String(), hostUpdate.Host.HostPass)
if err != nil {
slog.Error("failed to register host with turn server", "id", currentHost.ID, "error", err)
return
}
}
case models.SignalHost:
signalPeer(hostUpdate.Signal)
}
@ -212,6 +207,29 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
}
}
func signalPeer(signal models.Signal) {
if signal.ToHostPubKey == "" {
msg := "insufficient data to signal peer"
logger.Log(0, msg)
return
}
signal.IsPro = servercfg.IsPro
peerHost, err := logic.GetHost(signal.ToHostID)
if err != nil {
slog.Error("failed to signal, peer not found", "error", err)
return
}
err = HostUpdate(&models.HostUpdate{
Action: models.SignalHost,
Host: *peerHost,
Signal: signal,
})
if err != nil {
slog.Error("failed to publish signal to peer", "error", err)
}
}
// ClientPeerUpdate message handler -- handles updating peers after signal from client nodes
func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
id, err := GetID(msg.Topic())

View file

@ -6,10 +6,6 @@ NM_DOMAIN=
SERVER_HOST=
# The admin master key for accessing the API. Change this in any production installation.
MASTER_KEY=
# The username to set for turn api access
TURN_USERNAME=
# The password to set for turn api access
TURN_PASSWORD=
# The username to set for MQ access
MQ_USERNAME=
# The password to set for MQ access
@ -42,18 +38,10 @@ DATABASE=sqlite
# If using "host networking", it will find and detect the IP of the mq container.
# For EMQX websockets use `SERVER_BROKER_ENDPOINT=ws://mq:8083/mqtt`
SERVER_BROKER_ENDPOINT=ws://mq:1883
# The reachable port of STUN on the server
STUN_PORT=3478
# Logging verbosity level - 1, 2, or 3
VERBOSITY=1
# Port to access turn server
TURN_PORT=3479
# Config for using turn, accepts either true/false
USE_TURN=true
DEBUG_MODE=off
TURN_API_PORT=8089
# Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST).
# Change to "off" to turn off.
REST_BACKEND=on
# If turned "on", Server will not set Host based on remote IP check.
# This is already overridden if SERVER_HOST is set. Turned "off" by default.

View file

@ -305,11 +305,10 @@ save_config() { (
save_config_item SERVER_IMAGE_TAG "$IMAGE_TAG"
fi
# copy entries from the previous config
local toCopy=("SERVER_HOST" "MASTER_KEY" "TURN_USERNAME" "TURN_PASSWORD" "MQ_USERNAME" "MQ_PASSWORD"
local toCopy=("SERVER_HOST" "MASTER_KEY" "MQ_USERNAME" "MQ_PASSWORD"
"INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
"TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
"DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "VERBOSITY"
"DEBUG_MODE" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
for name in "${toCopy[@]}"; do
save_config_item $name "${!name}"
@ -550,8 +549,6 @@ set_install_vars() {
echo " dashboard.$NETMAKER_BASE_DOMAIN"
echo " api.$NETMAKER_BASE_DOMAIN"
echo " broker.$NETMAKER_BASE_DOMAIN"
echo " turn.$NETMAKER_BASE_DOMAIN"
echo " turnapi.$NETMAKER_BASE_DOMAIN"
if [ "$INSTALL_TYPE" = "pro" ]; then
echo " prometheus.$NETMAKER_BASE_DOMAIN"
@ -657,55 +654,6 @@ set_install_vars() {
done
fi
unset GET_TURN_USERNAME
unset GET_TURN_PASSWORD
unset CONFIRM_TURN_PASSWORD
echo "Enter Credentials For TURN..."
if [ -z $AUTO_BUILD ]; then
read -p "TURN Username (click 'enter' to use 'netmaker'): " GET_TURN_USERNAME
fi
if [ -z "$GET_TURN_USERNAME" ]; then
echo "using default username for TURN"
TURN_USERNAME="netmaker"
else
TURN_USERNAME="$GET_TURN_USERNAME"
fi
if test -z "$TURN_PASSWORD"; then
TURN_PASSWORD=$(
tr -dc A-Za-z0-9 </dev/urandom | head -c 30
echo ''
)
fi
if [ -z $AUTO_BUILD ]; then
select domain_option in "Auto Generated / Config Password" "Input Your Own Password"; do
case $REPLY in
1)
echo "using random password for turn"
break
;;
2)
while true; do
echo "Enter your Password For TURN: "
read -s GET_TURN_PASSWORD
echo "Enter your password again to confirm: "
read -s CONFIRM_TURN_PASSWORD
if [ ${GET_TURN_PASSWORD} != ${CONFIRM_TURN_PASSWORD} ]; then
echo "wrong password entered, try again..."
continue
fi
TURN_PASSWORD="$GET_TURN_PASSWORD"
echo "TURN Password Saved Successfully!!"
break
done
break
;;
*) echo "invalid option $REPLY" ;;
esac
done
fi
wait_seconds 2
echo "-----------------------------------------------------------------"

View file

@ -356,8 +356,6 @@ set_install_vars() {
echo " dashboard.$NETMAKER_BASE_DOMAIN"
echo " api.$NETMAKER_BASE_DOMAIN"
echo " broker.$NETMAKER_BASE_DOMAIN"
echo " turn.$NETMAKER_BASE_DOMAIN"
echo " turnapi.$NETMAKER_BASE_DOMAIN"
if [ "$INSTALL_TYPE" = "pro" ]; then
echo " prometheus.$NETMAKER_BASE_DOMAIN"

View file

@ -45,7 +45,6 @@ func GetServerConfig() config.ServerConfig {
cfg.AllowedOrigin = GetAllowedOrigin()
cfg.RestBackend = "off"
cfg.NodeID = GetNodeID()
cfg.StunPort = GetStunPort()
cfg.BrokerType = GetBrokerType()
cfg.EmqxRestEndpoint = GetEmqxRestEndpoint()
if AutoUpdateEnabled() {
@ -125,45 +124,9 @@ func GetServerInfo() models.ServerConfig {
}
cfg.Version = GetVersion()
cfg.IsPro = IsPro
cfg.StunPort = GetStunPort()
cfg.TurnDomain = GetTurnHost()
cfg.TurnPort = GetTurnPort()
cfg.UseTurn = IsUsingTurn()
return cfg
}
// GetTurnHost - fetches the turn host domain
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
}
// IsUsingTurn - check if server has turn configured
func IsUsingTurn() (b bool) {
if os.Getenv("USE_TURN") != "" {
b = os.Getenv("USE_TURN") == "true"
} else {
b = config.Config.Server.UseTurn
}
return
}
// GetTurnApiHost - fetches the turn api host domain
func GetTurnApiHost() string {
turnApiServer := ""
if os.Getenv("TURN_SERVER_API_HOST") != "" {
turnApiServer = os.Getenv("TURN_SERVER_API_HOST")
} else if config.Config.Server.TurnApiServer != "" {
turnApiServer = config.Config.Server.TurnApiServer
}
return turnApiServer
}
// GetFrontendURL - gets the frontend url
func GetFrontendURL() string {
var frontend = ""
@ -646,58 +609,6 @@ func GetNetmakerTenantID() string {
return netmakerTenantID
}
// GetStunPort - Get the port to run the stun server on
func GetStunPort() int {
port := 3478 //default
if os.Getenv("STUN_PORT") != "" {
portInt, err := strconv.Atoi(os.Getenv("STUN_PORT"))
if err == nil {
port = portInt
}
} else if config.Config.Server.StunPort != 0 {
port = config.Config.Server.StunPort
}
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
}
// GetTurnUserName - fetches the turn server username
func GetTurnUserName() string {
userName := ""
if os.Getenv("TURN_USERNAME") != "" {
userName = os.Getenv("TURN_USERNAME")
} else {
userName = config.Config.Server.TurnUserName
}
return userName
}
// GetTurnPassword - fetches the turn server password
func GetTurnPassword() string {
pass := ""
if os.Getenv("TURN_PASSWORD") != "" {
pass = os.Getenv("TURN_PASSWORD")
} else {
pass = config.Config.Server.TurnPassword
}
return pass
}
// GetNetworkLimit - fetches free tier limits on users
func GetUserLimit() int {
var userslimit int