mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-29 03:23:56 +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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/pion/ice/v2"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/wiretrustee/wiretrustee/client/internal"
|
"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{}{}
|
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{
|
return &internal.EngineConfig{
|
||||||
StunsTurns: stunTurns,
|
|
||||||
WgIface: config.WgIface,
|
WgIface: config.WgIface,
|
||||||
WgAddr: peerConfig.Address,
|
WgAddr: peerConfig.Address,
|
||||||
IFaceBlackList: iFaceBlackList,
|
IFaceBlackList: iFaceBlackList,
|
||||||
@ -129,30 +122,6 @@ func createEngineConfig(key wgtypes.Key, config *internal.Config, wtConfig *mgmP
|
|||||||
}, nil
|
}, 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
|
// connectToSignal creates Signal Service client and established a connection
|
||||||
func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, ourPrivateKey wgtypes.Key) (*signal.Client, error) {
|
func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, ourPrivateKey wgtypes.Key) (*signal.Client, error) {
|
||||||
var sigTLSEnabled bool
|
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
|
// create an ice.Agent that will be responsible for negotiating and establishing actual peer-to-peer connection
|
||||||
a, err := ice.NewAgent(&ice.AgentConfig{
|
a, err := ice.NewAgent(&ice.AgentConfig{
|
||||||
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
||||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
||||||
Urls: conn.Config.StunTurnURLS,
|
Urls: conn.Config.StunTurnURLS,
|
||||||
|
CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay},
|
||||||
InterfaceFilter: func(s string) bool {
|
InterfaceFilter: func(s string) bool {
|
||||||
if conn.Config.iFaceBlackList == nil {
|
if conn.Config.iFaceBlackList == nil {
|
||||||
return true
|
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
|
conn.Status = StatusConnected
|
||||||
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
||||||
case <-conn.closeCond.C:
|
case <-conn.closeCond.C:
|
||||||
|
@ -18,13 +18,11 @@ import (
|
|||||||
|
|
||||||
// PeerConnectionTimeout is a timeout of an initial connection attempt to a remote peer.
|
// 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.
|
// 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
|
// EngineConfig is a config for the Engine
|
||||||
type EngineConfig struct {
|
type EngineConfig struct {
|
||||||
// StunsTurns is a list of STUN and TURN servers used by ICE
|
WgIface string
|
||||||
StunsTurns []*ice.URL
|
|
||||||
WgIface string
|
|
||||||
// WgAddr is a Wireguard local address (Wiretrustee Network IP)
|
// WgAddr is a Wireguard local address (Wiretrustee Network IP)
|
||||||
WgAddr string
|
WgAddr string
|
||||||
// WgPrivateKey is a Wireguard private key of our peer (it MUST never leave the machine)
|
// 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 is a Wireguard local listen port
|
||||||
wgPort int
|
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
|
// 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,
|
WgAllowedIPs: peer.WgAllowedIps,
|
||||||
WgKey: myKey,
|
WgKey: myKey,
|
||||||
RemoteWgKey: remoteKey,
|
RemoteWgKey: remoteKey,
|
||||||
StunTurnURLS: e.config.StunsTurns,
|
StunTurnURLS: append(e.STUNs, e.TURNs...),
|
||||||
iFaceBlackList: e.config.IFaceBlackList,
|
iFaceBlackList: e.config.IFaceBlackList,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,44 +264,26 @@ func (e *Engine) receiveManagementEvents() {
|
|||||||
log.Debugf("connecting to Management Service updates stream")
|
log.Debugf("connecting to Management Service updates stream")
|
||||||
|
|
||||||
e.mgmClient.Sync(func(update *mgmProto.SyncResponse) error {
|
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()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
remotePeers := update.GetRemotePeers()
|
if update.GetWiretrusteeConfig() != nil {
|
||||||
if len(remotePeers) != 0 {
|
err := e.updateTURNs(update.GetWiretrusteeConfig().GetTurns())
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new peers
|
err = e.updateSTUNs(update.GetWiretrusteeConfig().GetStuns())
|
||||||
for _, peer := range remotePeers {
|
if err != nil {
|
||||||
peerKey := peer.GetWgPubKey()
|
return err
|
||||||
peerIPs := peer.GetAllowedIps()
|
|
||||||
if _, ok := e.conns[peerKey]; !ok {
|
|
||||||
go e.initializePeer(Peer{
|
|
||||||
WgPubKey: peerKey,
|
|
||||||
WgAllowedIps: strings.Join(peerIPs, ","),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//todo update signal
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.updatePeers(update.GetRemotePeers())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -307,6 +292,82 @@ func (e *Engine) receiveManagementEvents() {
|
|||||||
log.Infof("connected to Management Service updates stream")
|
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
|
// receiveSignalEvents connects to the Signal Service event stream to negotiate connection with remote peers
|
||||||
func (e *Engine) receiveSignalEvents() {
|
func (e *Engine) receiveSignalEvents() {
|
||||||
// connect to a stream of messages coming from the signal server
|
// connect to a stream of messages coming from the signal server
|
||||||
|
@ -7,14 +7,19 @@
|
|||||||
"Password": null
|
"Password": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Turns": [
|
"TURNConfig": {
|
||||||
{
|
"Turns": [
|
||||||
"Proto": "udp",
|
{
|
||||||
"URI": "turn:stun.wiretrustee.com:3468",
|
"Proto": "udp",
|
||||||
"Username": "some_user",
|
"URI": "turn:stun.wiretrustee.com:3468",
|
||||||
"Password": "c29tZV9wYXNzd29yZA=="
|
"Username": "some_user",
|
||||||
}
|
"Password": "c29tZV9wYXNzd29yZA=="
|
||||||
],
|
}
|
||||||
|
],
|
||||||
|
"CredentialsTTL": "1h",
|
||||||
|
"Secret": "c29tZV9wYXNzd29yZA==",
|
||||||
|
"TimeBasedCredentials": true
|
||||||
|
},
|
||||||
"Signal": {
|
"Signal": {
|
||||||
"Proto": "http",
|
"Proto": "http",
|
||||||
"URI": "signal.wiretrustee.com:10000",
|
"URI": "signal.wiretrustee.com:10000",
|
||||||
|
@ -38,7 +38,7 @@ func init() {
|
|||||||
|
|
||||||
stopCh = make(chan int)
|
stopCh = make(chan int)
|
||||||
|
|
||||||
defaultConfigPath = "/etc/wiretrustee/config.json"
|
defaultConfigPath = "/etc/wiretrustee/management.json"
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
|
defaultConfigPath = os.Getenv("PROGRAMDATA") + "\\Wiretrustee\\" + "config.json"
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ type Config struct {
|
|||||||
type TURNConfig struct {
|
type TURNConfig struct {
|
||||||
TimeBasedCredentials bool
|
TimeBasedCredentials bool
|
||||||
CredentialsTTL util.Duration
|
CredentialsTTL util.Duration
|
||||||
Secret []byte
|
Secret string
|
||||||
Turns []*Host
|
Turns []*Host
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,5 +51,5 @@ type Host struct {
|
|||||||
// URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000
|
// URI e.g. turns://stun.wiretrustee.com:4430 or signal.wiretrustee.com:10000
|
||||||
URI string
|
URI string
|
||||||
Username 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)
|
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
|
// keep a connection to the peer and send updates when available
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
2
management/server/testdata/management.json
vendored
2
management/server/testdata/management.json
vendored
@ -13,7 +13,7 @@
|
|||||||
"Proto": "udp",
|
"Proto": "udp",
|
||||||
"URI": "turn:stun.wiretrustee.com:3468",
|
"URI": "turn:stun.wiretrustee.com:3468",
|
||||||
"Username": "some_user",
|
"Username": "some_user",
|
||||||
"Password": "c29tZV9wYXNzd29yZA=="
|
"Password": "some_password"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"CredentialsTTL": "1h",
|
"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
|
//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 {
|
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()
|
timeAuth := time.Now().Add(m.config.CredentialsTTL.Duration).Unix()
|
||||||
|
|
||||||
|
@ -13,12 +13,12 @@ var TurnTestHost = &Host{
|
|||||||
Proto: UDP,
|
Proto: UDP,
|
||||||
URI: "turn:turn.wiretrustee.com:77777",
|
URI: "turn:turn.wiretrustee.com:77777",
|
||||||
Username: "username",
|
Username: "username",
|
||||||
Password: nil,
|
Password: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeBasedAuthSecretsManager_GenerateCredentials(t *testing.T) {
|
func TestTimeBasedAuthSecretsManager_GenerateCredentials(t *testing.T) {
|
||||||
ttl := util.Duration{Duration: time.Hour}
|
ttl := util.Duration{Duration: time.Hour}
|
||||||
secret := []byte("some_secret")
|
secret := "some_secret"
|
||||||
peersManager := NewPeersUpdateManager()
|
peersManager := NewPeersUpdateManager()
|
||||||
|
|
||||||
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
|
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")
|
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) {
|
func TestTimeBasedAuthSecretsManager_SetupRefresh(t *testing.T) {
|
||||||
ttl := util.Duration{Duration: 2 * time.Second}
|
ttl := util.Duration{Duration: 2 * time.Second}
|
||||||
secret := []byte("some_secret")
|
secret := "some_secret"
|
||||||
peersManager := NewPeersUpdateManager()
|
peersManager := NewPeersUpdateManager()
|
||||||
peer := "some_peer"
|
peer := "some_peer"
|
||||||
updateChannel := peersManager.CreateChannel(peer)
|
updateChannel := peersManager.CreateChannel(peer)
|
||||||
@ -91,7 +91,7 @@ loop:
|
|||||||
|
|
||||||
func TestTimeBasedAuthSecretsManager_CancelRefresh(t *testing.T) {
|
func TestTimeBasedAuthSecretsManager_CancelRefresh(t *testing.T) {
|
||||||
ttl := util.Duration{Duration: time.Hour}
|
ttl := util.Duration{Duration: time.Hour}
|
||||||
secret := []byte("some_secret")
|
secret := "some_secret"
|
||||||
peersManager := NewPeersUpdateManager()
|
peersManager := NewPeersUpdateManager()
|
||||||
peer := "some_peer"
|
peer := "some_peer"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user