2022-03-08 14:47:55 +01:00
package internal
import (
"context"
2024-01-30 09:58:56 +01:00
"errors"
2022-07-02 12:02:17 +02:00
"fmt"
2024-05-22 18:42:56 +02:00
"net"
2024-04-09 13:25:14 +02:00
"runtime"
2024-04-09 20:27:27 +02:00
"runtime/debug"
2022-07-02 12:02:17 +02:00
"strings"
2024-05-10 10:47:16 +02:00
"sync"
2022-03-08 14:47:55 +01:00
"time"
2023-03-02 13:28:14 +01:00
"github.com/cenkalti/backoff/v4"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
2023-07-14 21:56:22 +02:00
"github.com/netbirdio/netbird/client/internal/dns"
2023-11-02 19:04:33 +01:00
"github.com/netbirdio/netbird/client/internal/listener"
2023-03-03 19:49:18 +01:00
"github.com/netbirdio/netbird/client/internal/peer"
2023-03-24 08:40:39 +01:00
"github.com/netbirdio/netbird/client/internal/stdnet"
2022-11-23 08:42:12 +01:00
"github.com/netbirdio/netbird/client/ssh"
2022-05-25 23:25:02 +02:00
"github.com/netbirdio/netbird/client/system"
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/iface"
mgm "github.com/netbirdio/netbird/management/client"
mgmProto "github.com/netbirdio/netbird/management/proto"
signal "github.com/netbirdio/netbird/signal/client"
2024-02-20 11:13:27 +01:00
"github.com/netbirdio/netbird/util"
2023-10-25 00:47:40 +02:00
"github.com/netbirdio/netbird/version"
2022-03-08 14:47:55 +01:00
)
2024-05-10 10:47:16 +02:00
type ConnectClient struct {
ctx context . Context
config * Config
statusRecorder * peer . Status
engine * Engine
engineMutex sync . Mutex
2024-01-22 12:20:24 +01:00
}
2024-05-10 10:47:16 +02:00
func NewConnectClient (
2024-01-22 12:20:24 +01:00
ctx context . Context ,
config * Config ,
statusRecorder * peer . Status ,
2024-05-10 10:47:16 +02:00
) * ConnectClient {
return & ConnectClient {
ctx : ctx ,
config : config ,
statusRecorder : statusRecorder ,
engineMutex : sync . Mutex { } ,
}
}
// Run with main logic.
func ( c * ConnectClient ) Run ( ) error {
return c . run ( MobileDependency { } , nil , nil , nil , nil )
}
// RunWithProbes runs the client's main logic with probes attached
func ( c * ConnectClient ) RunWithProbes (
2024-01-22 12:20:24 +01:00
mgmProbe * Probe ,
signalProbe * Probe ,
relayProbe * Probe ,
wgProbe * Probe ,
) error {
2024-05-10 10:47:16 +02:00
return c . run ( MobileDependency { } , mgmProbe , signalProbe , relayProbe , wgProbe )
2023-07-14 21:56:22 +02:00
}
2024-05-10 10:47:16 +02:00
// RunOnAndroid with main logic on mobile system
func ( c * ConnectClient ) RunOnAndroid (
2024-01-22 12:20:24 +01:00
tunAdapter iface . TunAdapter ,
iFaceDiscover stdnet . ExternalIFaceDiscover ,
networkChangeListener listener . NetworkChangeListener ,
dnsAddresses [ ] string ,
dnsReadyListener dns . ReadyListener ,
) error {
2023-07-14 21:56:22 +02:00
// in case of non Android os these variables will be nil
mobileDependency := MobileDependency {
2023-11-02 19:04:33 +01:00
TunAdapter : tunAdapter ,
IFaceDiscover : iFaceDiscover ,
NetworkChangeListener : networkChangeListener ,
HostDNSAddresses : dnsAddresses ,
DnsReadyListener : dnsReadyListener ,
2023-07-14 21:56:22 +02:00
}
2024-05-10 10:47:16 +02:00
return c . run ( mobileDependency , nil , nil , nil , nil )
2023-07-14 21:56:22 +02:00
}
2024-05-10 10:47:16 +02:00
func ( c * ConnectClient ) RunOniOS (
2024-01-22 12:20:24 +01:00
fileDescriptor int32 ,
networkChangeListener listener . NetworkChangeListener ,
dnsManager dns . IosDnsManager ,
) error {
2024-05-17 15:58:29 +02:00
// Set GC percent to 5% to reduce memory usage as iOS only allows 50MB of memory for the extension.
debug . SetGCPercent ( 5 )
2023-12-18 11:46:58 +01:00
mobileDependency := MobileDependency {
FileDescriptor : fileDescriptor ,
NetworkChangeListener : networkChangeListener ,
DnsManager : dnsManager ,
}
2024-05-10 10:47:16 +02:00
return c . run ( mobileDependency , nil , nil , nil , nil )
2023-12-18 11:46:58 +01:00
}
2024-05-10 10:47:16 +02:00
func ( c * ConnectClient ) run (
2024-01-22 12:20:24 +01:00
mobileDependency MobileDependency ,
mgmProbe * Probe ,
signalProbe * Probe ,
relayProbe * Probe ,
wgProbe * Probe ,
) error {
2024-04-09 20:27:27 +02:00
defer func ( ) {
if r := recover ( ) ; r != nil {
log . Panicf ( "Panic occurred: %v, stack trace: %s" , r , string ( debug . Stack ( ) ) )
}
} ( )
2024-04-09 13:25:14 +02:00
log . Infof ( "starting NetBird client version %s on %s/%s" , version . NetbirdVersion ( ) , runtime . GOOS , runtime . GOARCH )
2023-10-25 00:47:40 +02:00
2024-01-30 09:58:56 +01:00
// Check if client was not shut down in a clean way and restore DNS config if required.
// Otherwise, we might not be able to connect to the management server to retrieve new config.
2024-05-10 10:47:16 +02:00
if err := dns . CheckUncleanShutdown ( c . config . WgIface ) ; err != nil {
2024-01-30 09:58:56 +01:00
log . Errorf ( "checking unclean shutdown error: %s" , err )
}
2022-03-08 14:47:55 +01:00
backOff := & backoff . ExponentialBackOff {
InitialInterval : time . Second ,
2022-07-02 20:38:16 +02:00
RandomizationFactor : 1 ,
Multiplier : 1.7 ,
MaxInterval : 15 * time . Second ,
MaxElapsedTime : 3 * 30 * 24 * time . Hour , // 3 months
2022-03-08 14:47:55 +01:00
Stop : backoff . Stop ,
Clock : backoff . SystemClock ,
}
2024-05-10 10:47:16 +02:00
state := CtxGetState ( c . ctx )
2022-05-12 11:17:24 +02:00
defer func ( ) {
s , err := state . Status ( )
if err != nil || s != StatusNeedsLogin {
state . Set ( StatusIdle )
}
} ( )
2022-03-08 14:47:55 +01:00
wrapErr := state . Wrap
2024-05-10 10:47:16 +02:00
myPrivateKey , err := wgtypes . ParseKey ( c . config . PrivateKey )
2022-07-02 12:02:17 +02:00
if err != nil {
2024-05-10 10:47:16 +02:00
log . Errorf ( "failed parsing Wireguard key %s: [%s]" , c . config . PrivateKey , err . Error ( ) )
2022-07-02 12:02:17 +02:00
return wrapErr ( err )
}
var mgmTlsEnabled bool
2024-05-10 10:47:16 +02:00
if c . config . ManagementURL . Scheme == "https" {
2022-07-02 12:02:17 +02:00
mgmTlsEnabled = true
}
2024-05-10 10:47:16 +02:00
publicSSHKey , err := ssh . GeneratePublicKey ( [ ] byte ( c . config . SSHKey ) )
2022-07-02 12:02:17 +02:00
if err != nil {
return err
}
2024-05-10 10:47:16 +02:00
defer c . statusRecorder . ClientStop ( )
2022-03-08 14:47:55 +01:00
operation := func ( ) error {
// if context cancelled we not start new backoff cycle
select {
2024-05-10 10:47:16 +02:00
case <- c . ctx . Done ( ) :
2022-03-08 14:47:55 +01:00
return nil
default :
}
state . Set ( StatusConnecting )
2024-05-10 10:47:16 +02:00
engineCtx , cancel := context . WithCancel ( c . ctx )
2022-07-02 12:02:17 +02:00
defer func ( ) {
2024-05-10 10:47:16 +02:00
c . statusRecorder . MarkManagementDisconnected ( state . err )
c . statusRecorder . CleanLocalPeerState ( )
2022-07-02 12:02:17 +02:00
cancel ( )
} ( )
2022-05-27 15:54:51 +02:00
2024-05-10 10:47:16 +02:00
log . Debugf ( "connecting to the Management service %s" , c . config . ManagementURL . Host )
mgmClient , err := mgm . NewClient ( engineCtx , c . config . ManagementURL . Host , myPrivateKey , mgmTlsEnabled )
2022-09-01 18:28:45 +02:00
if err != nil {
return wrapErr ( gstatus . Errorf ( codes . FailedPrecondition , "failed connecting to Management Service : %s" , err ) )
}
2024-05-10 10:47:16 +02:00
mgmNotifier := statusRecorderToMgmConnStateNotifier ( c . statusRecorder )
2023-04-06 18:15:55 +02:00
mgmClient . SetConnStateListener ( mgmNotifier )
2024-05-10 10:47:16 +02:00
log . Debugf ( "connected to the Management service %s" , c . config . ManagementURL . Host )
2022-09-01 18:28:45 +02:00
defer func ( ) {
err = mgmClient . Close ( )
if err != nil {
log . Warnf ( "failed to close the Management service client %v" , err )
}
} ( )
2022-03-08 14:47:55 +01:00
// connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config
2022-09-01 18:28:45 +02:00
loginResp , err := loginToManagement ( engineCtx , mgmClient , publicSSHKey )
2022-03-08 14:47:55 +01:00
if err != nil {
2022-05-26 10:09:11 +02:00
log . Debug ( err )
2022-07-02 20:38:16 +02:00
if s , ok := gstatus . FromError ( err ) ; ok && ( s . Code ( ) == codes . PermissionDenied ) {
2022-05-12 11:17:24 +02:00
state . Set ( StatusNeedsLogin )
2022-07-02 20:38:16 +02:00
return backoff . Permanent ( wrapErr ( err ) ) // unrecoverable error
2022-05-12 11:17:24 +02:00
}
2022-03-08 14:47:55 +01:00
return wrapErr ( err )
}
2024-05-10 10:47:16 +02:00
c . statusRecorder . MarkManagementConnected ( )
2022-07-02 12:02:17 +02:00
2023-03-03 19:49:18 +01:00
localPeerState := peer . LocalPeerState {
2022-07-02 12:02:17 +02:00
IP : loginResp . GetPeerConfig ( ) . GetAddress ( ) ,
PubKey : myPrivateKey . PublicKey ( ) . String ( ) ,
2023-04-13 17:00:01 +02:00
KernelInterface : iface . WireGuardModuleIsLoaded ( ) ,
2022-11-26 13:29:50 +01:00
FQDN : loginResp . GetPeerConfig ( ) . GetFqdn ( ) ,
2022-07-02 12:02:17 +02:00
}
2024-05-10 10:47:16 +02:00
c . statusRecorder . UpdateLocalPeerState ( localPeerState )
2022-07-02 12:02:17 +02:00
signalURL := fmt . Sprintf ( "%s://%s" ,
strings . ToLower ( loginResp . GetWiretrusteeConfig ( ) . GetSignal ( ) . GetProtocol ( ) . String ( ) ) ,
loginResp . GetWiretrusteeConfig ( ) . GetSignal ( ) . GetUri ( ) ,
)
2024-05-10 10:47:16 +02:00
c . statusRecorder . UpdateSignalAddress ( signalURL )
2023-03-16 17:22:36 +01:00
2024-05-10 10:47:16 +02:00
c . statusRecorder . MarkSignalDisconnected ( nil )
2024-01-22 12:20:24 +01:00
defer func ( ) {
2024-05-10 10:47:16 +02:00
c . statusRecorder . MarkSignalDisconnected ( state . err )
2024-01-22 12:20:24 +01:00
} ( )
2022-03-08 14:47:55 +01:00
// with the global Wiretrustee config in hand connect (just a connection, no stream yet) Signal
2022-05-27 15:54:51 +02:00
signalClient , err := connectToSignal ( engineCtx , loginResp . GetWiretrusteeConfig ( ) , myPrivateKey )
2022-03-08 14:47:55 +01:00
if err != nil {
log . Error ( err )
return wrapErr ( err )
}
2022-09-01 18:28:45 +02:00
defer func ( ) {
err = signalClient . Close ( )
if err != nil {
log . Warnf ( "failed closing Signal service client %v" , err )
}
} ( )
2022-03-08 14:47:55 +01:00
2024-05-10 10:47:16 +02:00
signalNotifier := statusRecorderToSignalConnStateNotifier ( c . statusRecorder )
2023-03-16 17:22:36 +01:00
signalClient . SetConnStateListener ( signalNotifier )
2024-05-10 10:47:16 +02:00
c . statusRecorder . MarkSignalConnected ( )
2022-07-02 12:02:17 +02:00
2022-03-08 14:47:55 +01:00
peerConfig := loginResp . GetPeerConfig ( )
2024-05-10 10:47:16 +02:00
engineConfig , err := createEngineConfig ( myPrivateKey , c . config , peerConfig )
2022-03-08 14:47:55 +01:00
if err != nil {
log . Error ( err )
return wrapErr ( err )
}
2024-06-13 13:24:24 +02:00
checks := loginResp . GetChecks ( )
2024-05-10 10:47:16 +02:00
c . engineMutex . Lock ( )
2024-06-13 13:24:24 +02:00
c . engine = NewEngineWithProbes ( engineCtx , cancel , signalClient , mgmClient , engineConfig , mobileDependency , c . statusRecorder , mgmProbe , signalProbe , relayProbe , wgProbe , checks )
2024-05-10 10:47:16 +02:00
c . engineMutex . Unlock ( )
err = c . engine . Start ( )
2022-03-08 14:47:55 +01:00
if err != nil {
2022-05-26 10:09:11 +02:00
log . Errorf ( "error while starting Netbird Connection Engine: %s" , err )
2022-03-08 14:47:55 +01:00
return wrapErr ( err )
}
2024-05-07 18:50:34 +02:00
log . Infof ( "Netbird engine started, the IP is: %s" , peerConfig . GetAddress ( ) )
2022-03-08 14:47:55 +01:00
state . Set ( StatusConnected )
2022-05-25 23:25:02 +02:00
<- engineCtx . Done ( )
2024-05-10 10:47:16 +02:00
c . statusRecorder . ClientTeardown ( )
2022-03-08 14:47:55 +01:00
backOff . Reset ( )
2024-05-10 10:47:16 +02:00
err = c . engine . Stop ( )
2022-03-08 14:47:55 +01:00
if err != nil {
log . Errorf ( "failed stopping engine %v" , err )
return wrapErr ( err )
}
2022-07-02 20:38:16 +02:00
log . Info ( "stopped NetBird client" )
2022-03-08 14:47:55 +01:00
2024-01-30 09:58:56 +01:00
if _ , err := state . Status ( ) ; errors . Is ( err , ErrResetConnection ) {
2022-03-08 14:47:55 +01:00
return err
}
return nil
}
2024-05-10 10:47:16 +02:00
c . statusRecorder . ClientStart ( )
2022-07-02 12:02:17 +02:00
err = backoff . Retry ( operation , backOff )
2022-03-08 14:47:55 +01:00
if err != nil {
2022-07-02 20:38:16 +02:00
log . Debugf ( "exiting client retry loop due to unrecoverable error: %s" , err )
2022-12-06 15:37:30 +01:00
if s , ok := gstatus . FromError ( err ) ; ok && ( s . Code ( ) == codes . PermissionDenied ) {
state . Set ( StatusNeedsLogin )
}
2022-03-08 14:47:55 +01:00
return err
}
return nil
}
2024-05-10 10:47:16 +02:00
func ( c * ConnectClient ) Engine ( ) * Engine {
var e * Engine
c . engineMutex . Lock ( )
e = c . engine
c . engineMutex . Unlock ( )
return e
}
2022-03-08 14:47:55 +01:00
// createEngineConfig converts configuration received from Management Service to EngineConfig
2023-04-17 11:15:37 +02:00
func createEngineConfig ( key wgtypes . Key , config * Config , peerConfig * mgmProto . PeerConfig ) ( * EngineConfig , error ) {
2024-06-14 14:22:49 +02:00
nm := false
if config . NetworkMonitor != nil {
nm = * config . NetworkMonitor
}
2022-03-08 14:47:55 +01:00
engineConf := & EngineConfig {
2022-11-23 11:03:29 +01:00
WgIfaceName : config . WgIface ,
WgAddr : peerConfig . Address ,
IFaceBlackList : config . IFaceBlackList ,
DisableIPv6Discovery : config . DisableIPv6Discovery ,
WgPrivateKey : key ,
WgPort : config . WgPort ,
2024-06-14 14:22:49 +02:00
NetworkMonitor : nm ,
2022-11-23 11:03:29 +01:00
SSHKey : [ ] byte ( config . SSHKey ) ,
2022-11-26 13:29:50 +01:00
NATExternalIPs : config . NATExternalIPs ,
2023-01-17 19:16:50 +01:00
CustomDNSAddress : config . CustomDNSAddress ,
2024-01-08 12:25:35 +01:00
RosenpassEnabled : config . RosenpassEnabled ,
2024-02-21 17:23:17 +01:00
RosenpassPermissive : config . RosenpassPermissive ,
2024-02-20 11:13:27 +01:00
ServerSSHAllowed : util . ReturnBoolWithDefaultTrue ( config . ServerSSHAllowed ) ,
2024-06-13 13:24:24 +02:00
DNSRouteInterval : config . DNSRouteInterval ,
2022-03-08 14:47:55 +01:00
}
if config . PreSharedKey != "" {
preSharedKey , err := wgtypes . ParseKey ( config . PreSharedKey )
if err != nil {
return nil , err
}
engineConf . PreSharedKey = & preSharedKey
}
2024-05-22 18:42:56 +02:00
port , err := freePort ( config . WgPort )
if err != nil {
return nil , err
}
if port != config . WgPort {
log . Infof ( "using %d as wireguard port: %d is in use" , port , config . WgPort )
}
engineConf . WgPort = port
2022-03-08 14:47:55 +01:00
return engineConf , nil
}
// connectToSignal creates Signal Service client and established a connection
func connectToSignal ( ctx context . Context , wtConfig * mgmProto . WiretrusteeConfig , ourPrivateKey wgtypes . Key ) ( * signal . GrpcClient , error ) {
var sigTLSEnabled bool
if wtConfig . Signal . Protocol == mgmProto . HostConfig_HTTPS {
sigTLSEnabled = true
} else {
sigTLSEnabled = false
}
signalClient , err := signal . NewClient ( ctx , wtConfig . Signal . Uri , ourPrivateKey , sigTLSEnabled )
if err != nil {
log . Errorf ( "error while connecting to the Signal Exchange Service %s: %s" , wtConfig . Signal . Uri , err )
2022-07-02 12:02:17 +02:00
return nil , gstatus . Errorf ( codes . FailedPrecondition , "failed connecting to Signal Service : %s" , err )
2022-03-08 14:47:55 +01:00
}
return signalClient , nil
}
2022-09-01 18:28:45 +02:00
// loginToManagement creates Management Services client, establishes a connection, logs-in and gets a global Wiretrustee config (signal, turn, stun hosts, etc)
func loginToManagement ( ctx context . Context , client mgm . Client , pubSSHKey [ ] byte ) ( * mgmProto . LoginResponse , error ) {
2022-03-08 14:47:55 +01:00
serverPublicKey , err := client . GetServerPublicKey ( )
if err != nil {
2022-09-01 18:28:45 +02:00
return nil , gstatus . Errorf ( codes . FailedPrecondition , "failed while getting Management Service public key: %s" , err )
2022-03-08 14:47:55 +01:00
}
2022-05-25 23:25:02 +02:00
sysInfo := system . GetInfo ( ctx )
2022-06-23 17:04:53 +02:00
loginResp , err := client . Login ( * serverPublicKey , sysInfo , pubSSHKey )
2022-03-08 14:47:55 +01:00
if err != nil {
2022-09-01 18:28:45 +02:00
return nil , err
2022-03-08 14:47:55 +01:00
}
2022-09-01 18:28:45 +02:00
return loginResp , nil
2022-03-08 14:47:55 +01:00
}
2022-07-30 19:17:18 +02:00
2023-03-16 17:22:36 +01:00
func statusRecorderToMgmConnStateNotifier ( statusRecorder * peer . Status ) mgm . ConnStateNotifier {
var sri interface { } = statusRecorder
mgmNotifier , _ := sri . ( mgm . ConnStateNotifier )
return mgmNotifier
}
func statusRecorderToSignalConnStateNotifier ( statusRecorder * peer . Status ) signal . ConnStateNotifier {
var sri interface { } = statusRecorder
notifier , _ := sri . ( signal . ConnStateNotifier )
return notifier
}
2024-05-22 18:42:56 +02:00
2024-08-21 19:24:40 +02:00
// freePort attempts to determine if the provided port is available, if not it will ask the system for a free port.
func freePort ( initPort int ) ( int , error ) {
2024-05-22 18:42:56 +02:00
addr := net . UDPAddr { }
2024-08-21 19:24:40 +02:00
if initPort == 0 {
initPort = iface . DefaultWgPort
2024-05-22 18:42:56 +02:00
}
2024-08-21 19:24:40 +02:00
addr . Port = initPort
conn , err := net . ListenUDP ( "udp" , & addr )
if err == nil {
closeConnWithLog ( conn )
return initPort , nil
}
// if the port is already in use, ask the system for a free port
addr . Port = 0
conn , err = net . ListenUDP ( "udp" , & addr )
if err != nil {
return 0 , fmt . Errorf ( "unable to get a free port: %v" , err )
}
udpAddr , ok := conn . LocalAddr ( ) . ( * net . UDPAddr )
if ! ok {
return 0 , errors . New ( "wrong address type when getting a free port" )
}
closeConnWithLog ( conn )
return udpAddr . Port , nil
}
func closeConnWithLog ( conn * net . UDPConn ) {
startClosing := time . Now ( )
err := conn . Close ( )
if err != nil {
log . Warnf ( "closing probe port %d failed: %v. NetBird will still attempt to use this port for connection." , conn . LocalAddr ( ) . ( * net . UDPAddr ) . Port , err )
}
if time . Since ( startClosing ) > time . Second {
log . Warnf ( "closing the testing port %d took %s. Usually it is safe to ignore, but continuous warnings may indicate a problem." , conn . LocalAddr ( ) . ( * net . UDPAddr ) . Port , time . Since ( startClosing ) )
2024-05-22 18:42:56 +02:00
}
}