diff --git a/client/cmd/up.go b/client/cmd/up.go index e426db0b8..c7fe7c336 100644 --- a/client/cmd/up.go +++ b/client/cmd/up.go @@ -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 diff --git a/client/internal/connection.go b/client/internal/connection.go index ca97e38f5..0790f5776 100644 --- a/client/internal/connection.go +++ b/client/internal/connection.go @@ -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: diff --git a/client/internal/engine.go b/client/internal/engine.go index 8754c58c8..77fc9f7d9 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -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 diff --git a/infrastructure_files/config.json b/infrastructure_files/config.json index 8d53d1ea0..bad60208c 100644 --- a/infrastructure_files/config.json +++ b/infrastructure_files/config.json @@ -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", diff --git a/management/cmd/root.go b/management/cmd/root.go index 122ce0ea3..cc3c6c4b8 100644 --- a/management/cmd/root.go +++ b/management/cmd/root.go @@ -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" } diff --git a/management/server/config.go b/management/server/config.go index 3dc003c27..c8da0388c 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -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 } diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index ac0d41e4a..a29fde440 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -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 { diff --git a/management/server/testdata/management.json b/management/server/testdata/management.json index 9224ff2c3..8de346724 100644 --- a/management/server/testdata/management.json +++ b/management/server/testdata/management.json @@ -13,7 +13,7 @@ "Proto": "udp", "URI": "turn:stun.wiretrustee.com:3468", "Username": "some_user", - "Password": "c29tZV9wYXNzd29yZA==" + "Password": "some_password" } ], "CredentialsTTL": "1h", diff --git a/management/server/turncredentials.go b/management/server/turncredentials.go index ca97f8625..98f1de700 100644 --- a/management/server/turncredentials.go +++ b/management/server/turncredentials.go @@ -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() diff --git a/management/server/turncredentials_test.go b/management/server/turncredentials_test.go index b02ed50f3..d3089fa77 100644 --- a/management/server/turncredentials_test.go +++ b/management/server/turncredentials_test.go @@ -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"