mirror of
https://github.com/netbirdio/netbird.git
synced 2024-12-14 02:41:34 +01:00
223 lines
6.3 KiB
Go
223 lines
6.3 KiB
Go
|
package server
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto/sha1"
|
||
|
"crypto/sha256"
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
log "github.com/sirupsen/logrus"
|
||
|
|
||
|
"github.com/netbirdio/netbird/management/proto"
|
||
|
auth "github.com/netbirdio/netbird/relay/auth/hmac"
|
||
|
)
|
||
|
|
||
|
const defaultDuration = 12 * time.Hour
|
||
|
|
||
|
// SecretsManager used to manage TURN and relay secrets
|
||
|
type SecretsManager interface {
|
||
|
GenerateTurnToken() (*Token, error)
|
||
|
GenerateRelayToken() (*Token, error)
|
||
|
SetupRefresh(ctx context.Context, peerKey string)
|
||
|
CancelRefresh(peerKey string)
|
||
|
}
|
||
|
|
||
|
// TimeBasedAuthSecretsManager generates credentials with TTL and using pre-shared secret known to TURN server
|
||
|
type TimeBasedAuthSecretsManager struct {
|
||
|
mux sync.Mutex
|
||
|
turnCfg *TURNConfig
|
||
|
relayCfg *Relay
|
||
|
turnHmacToken *auth.TimedHMAC
|
||
|
relayHmacToken *auth.TimedHMAC
|
||
|
updateManager *PeersUpdateManager
|
||
|
turnCancelMap map[string]chan struct{}
|
||
|
relayCancelMap map[string]chan struct{}
|
||
|
}
|
||
|
|
||
|
type Token auth.Token
|
||
|
|
||
|
func NewTimeBasedAuthSecretsManager(updateManager *PeersUpdateManager, turnCfg *TURNConfig, relayCfg *Relay) *TimeBasedAuthSecretsManager {
|
||
|
mgr := &TimeBasedAuthSecretsManager{
|
||
|
updateManager: updateManager,
|
||
|
turnCfg: turnCfg,
|
||
|
relayCfg: relayCfg,
|
||
|
turnCancelMap: make(map[string]chan struct{}),
|
||
|
relayCancelMap: make(map[string]chan struct{}),
|
||
|
}
|
||
|
|
||
|
if turnCfg != nil {
|
||
|
duration := turnCfg.CredentialsTTL.Duration
|
||
|
if turnCfg.CredentialsTTL.Duration <= 0 {
|
||
|
log.Warnf("TURN credentials TTL is not set or invalid, using default value %s", defaultDuration)
|
||
|
duration = defaultDuration
|
||
|
}
|
||
|
mgr.turnHmacToken = auth.NewTimedHMAC(turnCfg.Secret, duration)
|
||
|
}
|
||
|
|
||
|
if relayCfg != nil {
|
||
|
duration := relayCfg.CredentialsTTL.Duration
|
||
|
if relayCfg.CredentialsTTL.Duration <= 0 {
|
||
|
log.Warnf("Relay credentials TTL is not set or invalid, using default value %s", defaultDuration)
|
||
|
duration = defaultDuration
|
||
|
}
|
||
|
|
||
|
mgr.relayHmacToken = auth.NewTimedHMAC(relayCfg.Secret, duration)
|
||
|
}
|
||
|
|
||
|
return mgr
|
||
|
}
|
||
|
|
||
|
// GenerateTurnToken generates new time-based secret credentials for TURN
|
||
|
func (m *TimeBasedAuthSecretsManager) GenerateTurnToken() (*Token, error) {
|
||
|
if m.turnHmacToken == nil {
|
||
|
return nil, fmt.Errorf("TURN configuration is not set")
|
||
|
}
|
||
|
turnToken, err := m.turnHmacToken.GenerateToken(sha1.New)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to generate TURN token: %s", err)
|
||
|
}
|
||
|
return (*Token)(turnToken), nil
|
||
|
}
|
||
|
|
||
|
// GenerateRelayToken generates new time-based secret credentials for relay
|
||
|
func (m *TimeBasedAuthSecretsManager) GenerateRelayToken() (*Token, error) {
|
||
|
if m.relayHmacToken == nil {
|
||
|
return nil, fmt.Errorf("relay configuration is not set")
|
||
|
}
|
||
|
relayToken, err := m.relayHmacToken.GenerateToken(sha256.New)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to generate relay token: %s", err)
|
||
|
}
|
||
|
return (*Token)(relayToken), nil
|
||
|
}
|
||
|
|
||
|
func (m *TimeBasedAuthSecretsManager) cancelTURN(peerID string) {
|
||
|
if channel, ok := m.turnCancelMap[peerID]; ok {
|
||
|
close(channel)
|
||
|
delete(m.turnCancelMap, peerID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *TimeBasedAuthSecretsManager) cancelRelay(peerID string) {
|
||
|
if channel, ok := m.relayCancelMap[peerID]; ok {
|
||
|
close(channel)
|
||
|
delete(m.relayCancelMap, peerID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CancelRefresh cancels scheduled peer credentials refresh
|
||
|
func (m *TimeBasedAuthSecretsManager) CancelRefresh(peerID string) {
|
||
|
m.mux.Lock()
|
||
|
defer m.mux.Unlock()
|
||
|
m.cancelTURN(peerID)
|
||
|
m.cancelRelay(peerID)
|
||
|
}
|
||
|
|
||
|
// SetupRefresh starts peer credentials refresh
|
||
|
func (m *TimeBasedAuthSecretsManager) SetupRefresh(ctx context.Context, peerID string) {
|
||
|
m.mux.Lock()
|
||
|
defer m.mux.Unlock()
|
||
|
|
||
|
m.cancelTURN(peerID)
|
||
|
m.cancelRelay(peerID)
|
||
|
|
||
|
if m.turnCfg != nil && m.turnCfg.TimeBasedCredentials {
|
||
|
turnCancel := make(chan struct{}, 1)
|
||
|
m.turnCancelMap[peerID] = turnCancel
|
||
|
go m.refreshTURNTokens(ctx, peerID, turnCancel)
|
||
|
log.WithContext(ctx).Debugf("starting TURN refresh for %s", peerID)
|
||
|
}
|
||
|
|
||
|
if m.relayCfg != nil {
|
||
|
relayCancel := make(chan struct{}, 1)
|
||
|
m.relayCancelMap[peerID] = relayCancel
|
||
|
go m.refreshRelayTokens(ctx, peerID, relayCancel)
|
||
|
log.WithContext(ctx).Debugf("starting relay refresh for %s", peerID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *TimeBasedAuthSecretsManager) refreshTURNTokens(ctx context.Context, peerID string, cancel chan struct{}) {
|
||
|
ticker := time.NewTicker(m.turnCfg.CredentialsTTL.Duration / 4 * 3)
|
||
|
defer ticker.Stop()
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-cancel:
|
||
|
log.WithContext(ctx).Debugf("stopping TURN refresh for %s", peerID)
|
||
|
return
|
||
|
case <-ticker.C:
|
||
|
m.pushNewTURNTokens(ctx, peerID)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *TimeBasedAuthSecretsManager) refreshRelayTokens(ctx context.Context, peerID string, cancel chan struct{}) {
|
||
|
ticker := time.NewTicker(m.relayCfg.CredentialsTTL.Duration / 4 * 3)
|
||
|
defer ticker.Stop()
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-cancel:
|
||
|
log.WithContext(ctx).Debugf("stopping relay refresh for %s", peerID)
|
||
|
return
|
||
|
case <-ticker.C:
|
||
|
m.pushNewRelayTokens(ctx, peerID)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *TimeBasedAuthSecretsManager) pushNewTURNTokens(ctx context.Context, peerID string) {
|
||
|
turnToken, err := m.turnHmacToken.GenerateToken(sha1.New)
|
||
|
if err != nil {
|
||
|
log.Errorf("failed to generate token for peer '%s': %s", peerID, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var turns []*proto.ProtectedHostConfig
|
||
|
for _, host := range m.turnCfg.Turns {
|
||
|
turn := &proto.ProtectedHostConfig{
|
||
|
HostConfig: &proto.HostConfig{
|
||
|
Uri: host.URI,
|
||
|
Protocol: ToResponseProto(host.Proto),
|
||
|
},
|
||
|
User: turnToken.Payload,
|
||
|
Password: turnToken.Signature,
|
||
|
}
|
||
|
turns = append(turns, turn)
|
||
|
}
|
||
|
|
||
|
update := &proto.SyncResponse{
|
||
|
WiretrusteeConfig: &proto.WiretrusteeConfig{
|
||
|
Turns: turns,
|
||
|
// omit Relay to avoid updates there
|
||
|
},
|
||
|
}
|
||
|
|
||
|
log.WithContext(ctx).Debugf("sending new TURN credentials to peer %s", peerID)
|
||
|
m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update})
|
||
|
}
|
||
|
|
||
|
func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, peerID string) {
|
||
|
relayToken, err := m.relayHmacToken.GenerateToken(sha256.New)
|
||
|
if err != nil {
|
||
|
log.Errorf("failed to generate relay token for peer '%s': %s", peerID, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
update := &proto.SyncResponse{
|
||
|
WiretrusteeConfig: &proto.WiretrusteeConfig{
|
||
|
Relay: &proto.RelayConfig{
|
||
|
Urls: m.relayCfg.Addresses,
|
||
|
TokenPayload: relayToken.Payload,
|
||
|
TokenSignature: relayToken.Signature,
|
||
|
},
|
||
|
// omit Turns to avoid updates there
|
||
|
},
|
||
|
}
|
||
|
|
||
|
log.WithContext(ctx).Debugf("sending new relay credentials to peer %s", peerID)
|
||
|
m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update})
|
||
|
}
|