mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-25 17:43:38 +01:00
client update of TURNs and STUNs (#106)
* feature: update STUNs and TURNs in engine * fix: setup TURN credentials request only when refresh enabled * feature: update TURNs and STUNs in teh client app on Management update * chore: disable peer reflexive candidates in ICE * chore: relocate management.json * chore: make TURN secret and pwd plain text in config
This commit is contained in:
parent
81c5aa1341
commit
a4db0b4e94
@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/pion/ice/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wiretrustee/wiretrustee/client/internal"
|
||||
@ -115,13 +114,7 @@ func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmP
|
||||
iFaceBlackList[config.IFaceBlackList[i]] = struct{}{}
|
||||
}
|
||||
|
||||
stunTurns, err := toStunTurnURLs(wtConfig)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, "failed parsing STUN and TURN URLs received from Management Service : %s", err)
|
||||
}
|
||||
|
||||
return &internal.EngineConfig{
|
||||
StunsTurns: stunTurns,
|
||||
WgIface: config.WgIface,
|
||||
WgAddr: peerConfig.Address,
|
||||
IFaceBlackList: iFaceBlackList,
|
||||
@ -129,30 +122,6 @@ func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmP
|
||||
}, nil
|
||||
}
|
||||
|
||||
// toStunTurnURLs converts Wiretrustee STUN and TURN configs to ice.URL array
|
||||
func toStunTurnURLs(wtConfig *mgmProto.WiretrusteeConfig) ([]*ice.URL, error) {
|
||||
|
||||
var stunsTurns []*ice.URL
|
||||
for _, stun := range wtConfig.Stuns {
|
||||
url, err := ice.ParseURL(stun.Uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stunsTurns = append(stunsTurns, url)
|
||||
}
|
||||
for _, turn := range wtConfig.Turns {
|
||||
url, err := ice.ParseURL(turn.HostConfig.Uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url.Username = turn.User
|
||||
url.Password = turn.Password
|
||||
stunsTurns = append(stunsTurns, url)
|
||||
}
|
||||
|
||||
return stunsTurns, nil
|
||||
}
|
||||
|
||||
// connectToSignal creates Signal Service client and established a connection
|
||||
func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, ourPrivateKey wgtypes.Key) (*signal.Client, error) {
|
||||
var sigTLSEnabled bool
|
||||
|
@ -127,8 +127,9 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
||||
// create an ice.Agent that will be responsible for negotiating and establishing actual peer-to-peer connection
|
||||
a, err := ice.NewAgent(&ice.AgentConfig{
|
||||
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
||||
Urls: conn.Config.StunTurnURLS,
|
||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
||||
Urls: conn.Config.StunTurnURLS,
|
||||
CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay},
|
||||
InterfaceFilter: func(s string) bool {
|
||||
if conn.Config.iFaceBlackList == nil {
|
||||
return true
|
||||
@ -200,6 +201,10 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
||||
}
|
||||
}
|
||||
|
||||
if pair.Remote.Type() == ice.CandidateTypeRelay || pair.Local.Type() == ice.CandidateTypeRelay {
|
||||
log.Infof("using relay with peer %s", conn.Config.RemoteWgKey)
|
||||
}
|
||||
|
||||
conn.Status = StatusConnected
|
||||
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
||||
case <-conn.closeCond.C:
|
||||
|
@ -18,13 +18,11 @@ import (
|
||||
|
||||
// PeerConnectionTimeout is a timeout of an initial connection attempt to a remote peer.
|
||||
// E.g. this peer will wait PeerConnectionTimeout for the remote peer to respond, if not successful then it will retry the connection attempt.
|
||||
const PeerConnectionTimeout = 60 * time.Second
|
||||
const PeerConnectionTimeout = 40 * time.Second
|
||||
|
||||
// EngineConfig is a config for the Engine
|
||||
type EngineConfig struct {
|
||||
// StunsTurns is a list of STUN and TURN servers used by ICE
|
||||
StunsTurns []*ice.URL
|
||||
WgIface string
|
||||
WgIface string
|
||||
// WgAddr is a Wireguard local address (Wiretrustee Network IP)
|
||||
WgAddr string
|
||||
// WgPrivateKey is a Wireguard private key of our peer (it MUST never leave the machine)
|
||||
@ -51,6 +49,11 @@ type Engine struct {
|
||||
|
||||
// wgPort is a Wireguard local listen port
|
||||
wgPort int
|
||||
|
||||
// STUNs is a list of STUN servers used by ICE
|
||||
STUNs []*ice.URL
|
||||
// TURNs is a list of STUN servers used by ICE
|
||||
TURNs []*ice.URL
|
||||
}
|
||||
|
||||
// Peer is an instance of the Connection Peer
|
||||
@ -187,7 +190,7 @@ func (e *Engine) openPeerConnection(wgPort int, myKey wgtypes.Key, peer Peer) (*
|
||||
WgAllowedIPs: peer.WgAllowedIps,
|
||||
WgKey: myKey,
|
||||
RemoteWgKey: remoteKey,
|
||||
StunTurnURLS: e.config.StunsTurns,
|
||||
StunTurnURLS: append(e.STUNs, e.TURNs...),
|
||||
iFaceBlackList: e.config.IFaceBlackList,
|
||||
}
|
||||
|
||||
@ -261,44 +264,26 @@ func (e *Engine) receiveManagementEvents() {
|
||||
log.Debugf("connecting to Management Service updates stream")
|
||||
|
||||
e.mgmClient.Sync(func(update *mgmProto.SyncResponse) error {
|
||||
// todo handle changes of global settings (in update.GetWiretrusteeConfig())
|
||||
// todo handle changes of peer settings (in update.GetPeerConfig())
|
||||
|
||||
e.syncMsgMux.Lock()
|
||||
defer e.syncMsgMux.Unlock()
|
||||
|
||||
remotePeers := update.GetRemotePeers()
|
||||
if len(remotePeers) != 0 {
|
||||
|
||||
remotePeerMap := make(map[string]struct{})
|
||||
for _, peer := range remotePeers {
|
||||
remotePeerMap[peer.GetWgPubKey()] = struct{}{}
|
||||
}
|
||||
|
||||
//remove peers that are no longer available for us
|
||||
toRemove := []string{}
|
||||
for p := range e.conns {
|
||||
if _, ok := remotePeerMap[p]; !ok {
|
||||
toRemove = append(toRemove, p)
|
||||
}
|
||||
}
|
||||
err := e.removePeerConnections(toRemove)
|
||||
if update.GetWiretrusteeConfig() != nil {
|
||||
err := e.updateTURNs(update.GetWiretrusteeConfig().GetTurns())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add new peers
|
||||
for _, peer := range remotePeers {
|
||||
peerKey := peer.GetWgPubKey()
|
||||
peerIPs := peer.GetAllowedIps()
|
||||
if _, ok := e.conns[peerKey]; !ok {
|
||||
go e.initializePeer(Peer{
|
||||
WgPubKey: peerKey,
|
||||
WgAllowedIps: strings.Join(peerIPs, ","),
|
||||
})
|
||||
}
|
||||
|
||||
err = e.updateSTUNs(update.GetWiretrusteeConfig().GetStuns())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//todo update signal
|
||||
}
|
||||
|
||||
err := e.updatePeers(update.GetRemotePeers())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -307,6 +292,82 @@ func (e *Engine) receiveManagementEvents() {
|
||||
log.Infof("connected to Management Service updates stream")
|
||||
}
|
||||
|
||||
func (e *Engine) updateSTUNs(stuns []*mgmProto.HostConfig) error {
|
||||
if len(stuns) == 0 {
|
||||
return nil
|
||||
}
|
||||
var newSTUNs []*ice.URL
|
||||
log.Debugf("got STUNs update from Management Service, updating")
|
||||
for _, stun := range stuns {
|
||||
url, err := ice.ParseURL(stun.Uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSTUNs = append(newSTUNs, url)
|
||||
}
|
||||
e.STUNs = newSTUNs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error {
|
||||
if len(turns) == 0 {
|
||||
return nil
|
||||
}
|
||||
var newTURNs []*ice.URL
|
||||
log.Debugf("got TURNs update from Management Service, updating")
|
||||
for _, turn := range turns {
|
||||
url, err := ice.ParseURL(turn.HostConfig.Uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
url.Username = turn.User
|
||||
url.Password = turn.Password
|
||||
newTURNs = append(newTURNs, url)
|
||||
}
|
||||
e.TURNs = newTURNs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) updatePeers(remotePeers []*mgmProto.RemotePeerConfig) error {
|
||||
if len(remotePeers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("got peers update from Management Service, updating")
|
||||
remotePeerMap := make(map[string]struct{})
|
||||
for _, peer := range remotePeers {
|
||||
remotePeerMap[peer.GetWgPubKey()] = struct{}{}
|
||||
}
|
||||
|
||||
//remove peers that are no longer available for us
|
||||
toRemove := []string{}
|
||||
for p := range e.conns {
|
||||
if _, ok := remotePeerMap[p]; !ok {
|
||||
toRemove = append(toRemove, p)
|
||||
}
|
||||
}
|
||||
err := e.removePeerConnections(toRemove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add new peers
|
||||
for _, peer := range remotePeers {
|
||||
peerKey := peer.GetWgPubKey()
|
||||
peerIPs := peer.GetAllowedIps()
|
||||
if _, ok := e.conns[peerKey]; !ok {
|
||||
go e.initializePeer(Peer{
|
||||
WgPubKey: peerKey,
|
||||
WgAllowedIps: strings.Join(peerIPs, ","),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveSignalEvents connects to the Signal Service event stream to negotiate connection with remote peers
|
||||
func (e *Engine) receiveSignalEvents() {
|
||||
// connect to a stream of messages coming from the signal server
|
||||
|
@ -7,14 +7,19 @@
|
||||
"Password": null
|
||||
}
|
||||
],
|
||||
"Turns": [
|
||||
{
|
||||
"Proto": "udp",
|
||||
"URI": "turn:stun.wiretrustee.com:3468",
|
||||
"Username": "some_user",
|
||||
"Password": "c29tZV9wYXNzd29yZA=="
|
||||
}
|
||||
],
|
||||
"TURNConfig": {
|
||||
"Turns": [
|
||||
{
|
||||
"Proto": "udp",
|
||||
"URI": "turn:stun.wiretrustee.com:3468",
|
||||
"Username": "some_user",
|
||||
"Password": "c29tZV9wYXNzd29yZA=="
|
||||
}
|
||||
],
|
||||
"CredentialsTTL": "1h",
|
||||
"Secret": "c29tZV9wYXNzd29yZA==",
|
||||
"TimeBasedCredentials": true
|
||||
},
|
||||
"Signal": {
|
||||
"Proto": "http",
|
||||
"URI": "signal.wiretrustee.com:10000",
|
||||
|
@ -38,7 +38,7 @@ func init() {
|
||||
|
||||
stopCh = make(chan int)
|
||||
|
||||
defaultConfigPath = "/etc/wiretrustee/config.json"
|
||||
defaultConfigPath = "/etc/wiretrustee/management.json"
|
||||
if runtime.GOOS == "windows" {
|
||||
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ type Config struct {
|
||||
type TURNConfig struct {
|
||||
TimeBasedCredentials bool
|
||||
CredentialsTTL util.Duration
|
||||
Secret []byte
|
||||
Secret string
|
||||
Turns []*Host
|
||||
}
|
||||
|
||||
@ -51,5 +51,5 @@ type Host struct {
|
||||
// URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000
|
||||
URI string
|
||||
Username string
|
||||
Password []byte
|
||||
Password string
|
||||
}
|
||||
|
@ -92,7 +92,9 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S
|
||||
log.Warnf("failed marking peer as connected %s %v", peerKey, err)
|
||||
}
|
||||
|
||||
s.turnCredentialsManager.SetupRefresh(peerKey.String())
|
||||
if s.config.TURNConfig.TimeBasedCredentials {
|
||||
s.turnCredentialsManager.SetupRefresh(peerKey.String())
|
||||
}
|
||||
// keep a connection to the peer and send updates when available
|
||||
for {
|
||||
select {
|
||||
|
2
management/server/testdata/management.json
vendored
2
management/server/testdata/management.json
vendored
@ -13,7 +13,7 @@
|
||||
"Proto": "udp",
|
||||
"URI": "turn:stun.wiretrustee.com:3468",
|
||||
"Username": "some_user",
|
||||
"Password": "c29tZV9wYXNzd29yZA=="
|
||||
"Password": "some_password"
|
||||
}
|
||||
],
|
||||
"CredentialsTTL": "1h",
|
||||
|
@ -42,7 +42,7 @@ func NewTimeBasedAuthSecretsManager(updateManager *PeersUpdateManager, config *T
|
||||
|
||||
//GenerateCredentials generates new time-based secret credentials - basically username is a unix timestamp and password is a HMAC hash of a timestamp with a preshared TURN secret
|
||||
func (m *TimeBasedAuthSecretsManager) GenerateCredentials() TURNCredentials {
|
||||
mac := hmac.New(sha1.New, m.config.Secret)
|
||||
mac := hmac.New(sha1.New, []byte(m.config.Secret))
|
||||
|
||||
timeAuth := time.Now().Add(m.config.CredentialsTTL.Duration).Unix()
|
||||
|
||||
|
@ -13,12 +13,12 @@ var TurnTestHost = &Host{
|
||||
Proto: UDP,
|
||||
URI: "turn:turn.wiretrustee.com:77777",
|
||||
Username: "username",
|
||||
Password: nil,
|
||||
Password: "",
|
||||
}
|
||||
|
||||
func TestTimeBasedAuthSecretsManager_GenerateCredentials(t *testing.T) {
|
||||
ttl := util.Duration{Duration: time.Hour}
|
||||
secret := []byte("some_secret")
|
||||
secret := "some_secret"
|
||||
peersManager := NewPeersUpdateManager()
|
||||
|
||||
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
|
||||
@ -36,13 +36,13 @@ func TestTimeBasedAuthSecretsManager_GenerateCredentials(t *testing.T) {
|
||||
t.Errorf("expected generated TURN password not to be empty, got empty")
|
||||
}
|
||||
|
||||
validateMAC(credentials.Username, credentials.Password, secret, t)
|
||||
validateMAC(credentials.Username, credentials.Password, []byte(secret), t)
|
||||
|
||||
}
|
||||
|
||||
func TestTimeBasedAuthSecretsManager_SetupRefresh(t *testing.T) {
|
||||
ttl := util.Duration{Duration: 2 * time.Second}
|
||||
secret := []byte("some_secret")
|
||||
secret := "some_secret"
|
||||
peersManager := NewPeersUpdateManager()
|
||||
peer := "some_peer"
|
||||
updateChannel := peersManager.CreateChannel(peer)
|
||||
@ -91,7 +91,7 @@ loop:
|
||||
|
||||
func TestTimeBasedAuthSecretsManager_CancelRefresh(t *testing.T) {
|
||||
ttl := util.Duration{Duration: time.Hour}
|
||||
secret := []byte("some_secret")
|
||||
secret := "some_secret"
|
||||
peersManager := NewPeersUpdateManager()
|
||||
peer := "some_peer"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user