2022-03-08 14:47:55 +01:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
2022-05-12 11:17:24 +02:00
|
|
|
"time"
|
2022-03-08 14:47:55 +01:00
|
|
|
|
2023-09-04 17:03:44 +02:00
|
|
|
"github.com/netbirdio/netbird/client/internal/auth"
|
2023-11-29 15:01:27 +01:00
|
|
|
"github.com/netbirdio/netbird/client/system"
|
2023-09-04 17:03:44 +02:00
|
|
|
|
2023-03-02 13:28:14 +01:00
|
|
|
log "github.com/sirupsen/logrus"
|
2022-05-25 23:25:02 +02:00
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
gstatus "google.golang.org/grpc/status"
|
2023-03-02 13:28:14 +01:00
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
2022-03-08 14:47:55 +01:00
|
|
|
|
2022-03-26 12:08:54 +01:00
|
|
|
"github.com/netbirdio/netbird/client/internal"
|
2023-03-03 19:49:18 +01:00
|
|
|
"github.com/netbirdio/netbird/client/internal/peer"
|
2022-03-26 12:08:54 +01:00
|
|
|
"github.com/netbirdio/netbird/client/proto"
|
2023-03-15 07:54:51 +01:00
|
|
|
"github.com/netbirdio/netbird/version"
|
2022-03-08 14:47:55 +01:00
|
|
|
)
|
|
|
|
|
2024-01-22 12:20:24 +01:00
|
|
|
const probeThreshold = time.Second * 5
|
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
// Server for service control.
|
|
|
|
type Server struct {
|
|
|
|
rootCtx context.Context
|
|
|
|
actCancel context.CancelFunc
|
|
|
|
|
2023-01-17 19:16:50 +01:00
|
|
|
latestConfigInput internal.ConfigInput
|
|
|
|
|
|
|
|
logFile string
|
2022-03-08 14:47:55 +01:00
|
|
|
|
2022-05-28 18:37:08 +02:00
|
|
|
oauthAuthFlow oauthAuthFlow
|
2022-05-12 11:17:24 +02:00
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
mutex sync.Mutex
|
|
|
|
config *internal.Config
|
|
|
|
proto.UnimplementedDaemonServiceServer
|
2022-07-02 12:02:17 +02:00
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
statusRecorder *peer.Status
|
2024-01-22 12:20:24 +01:00
|
|
|
|
|
|
|
mgmProbe *internal.Probe
|
|
|
|
signalProbe *internal.Probe
|
|
|
|
relayProbe *internal.Probe
|
|
|
|
wgProbe *internal.Probe
|
|
|
|
lastProbe time.Time
|
2022-03-08 14:47:55 +01:00
|
|
|
}
|
|
|
|
|
2022-05-28 18:37:08 +02:00
|
|
|
type oauthAuthFlow struct {
|
|
|
|
expiresAt time.Time
|
2023-07-27 11:31:07 +02:00
|
|
|
flow auth.OAuthFlow
|
|
|
|
info auth.AuthFlowInfo
|
2022-05-28 18:37:08 +02:00
|
|
|
waitCancel context.CancelFunc
|
|
|
|
}
|
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
// New server instance constructor.
|
2023-02-07 11:40:05 +01:00
|
|
|
func New(ctx context.Context, configPath, logFile string) *Server {
|
2022-03-08 14:47:55 +01:00
|
|
|
return &Server{
|
2023-01-17 19:16:50 +01:00
|
|
|
rootCtx: ctx,
|
|
|
|
latestConfigInput: internal.ConfigInput{
|
2023-02-07 11:40:05 +01:00
|
|
|
ConfigPath: configPath,
|
2023-01-17 19:16:50 +01:00
|
|
|
},
|
2024-01-22 12:20:24 +01:00
|
|
|
logFile: logFile,
|
|
|
|
mgmProbe: internal.NewProbe(),
|
|
|
|
signalProbe: internal.NewProbe(),
|
|
|
|
relayProbe: internal.NewProbe(),
|
|
|
|
wgProbe: internal.NewProbe(),
|
2022-03-08 14:47:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Start() error {
|
2022-07-02 12:02:17 +02:00
|
|
|
s.mutex.Lock()
|
|
|
|
defer s.mutex.Unlock()
|
2022-03-08 14:47:55 +01:00
|
|
|
state := internal.CtxGetState(s.rootCtx)
|
|
|
|
|
|
|
|
// if current state contains any error, return it
|
|
|
|
// in all other cases we can continue execution only if status is idle and up command was
|
2022-05-12 11:17:24 +02:00
|
|
|
// not in the progress or already successfully established connection.
|
2022-03-08 14:47:55 +01:00
|
|
|
status, err := state.Status()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if status != internal.StatusIdle {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(s.rootCtx)
|
|
|
|
s.actCancel = cancel
|
|
|
|
|
2022-05-12 11:17:24 +02:00
|
|
|
// if configuration exists, we just start connections. if is new config we skip and set status NeedsLogin
|
|
|
|
// on failure we return error to retry
|
2023-03-02 13:28:14 +01:00
|
|
|
config, err := internal.UpdateConfig(s.latestConfigInput)
|
2022-05-12 11:17:24 +02:00
|
|
|
if errorStatus, ok := gstatus.FromError(err); ok && errorStatus.Code() == codes.NotFound {
|
2023-03-27 15:37:58 +02:00
|
|
|
s.config, err = internal.UpdateOrCreateConfig(s.latestConfigInput)
|
2022-05-12 11:17:24 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Warnf("unable to create configuration file: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
state.Set(internal.StatusNeedsLogin)
|
2022-03-08 14:47:55 +01:00
|
|
|
return nil
|
2022-05-12 11:17:24 +02:00
|
|
|
} else if err != nil {
|
|
|
|
log.Warnf("unable to create configuration file: %v", err)
|
|
|
|
return err
|
2022-03-08 14:47:55 +01:00
|
|
|
}
|
2022-05-12 11:17:24 +02:00
|
|
|
|
|
|
|
// if configuration exists, we just start connections.
|
2023-12-27 20:56:04 +01:00
|
|
|
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
|
2022-05-12 11:17:24 +02:00
|
|
|
|
2022-03-13 15:16:35 +01:00
|
|
|
s.config = config
|
2022-03-08 14:47:55 +01:00
|
|
|
|
2022-07-07 13:54:47 +02:00
|
|
|
if s.statusRecorder == nil {
|
2023-03-16 17:22:36 +01:00
|
|
|
s.statusRecorder = peer.NewRecorder(config.ManagementURL.String())
|
2022-07-07 13:54:47 +02:00
|
|
|
}
|
2024-02-24 12:41:13 +01:00
|
|
|
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
|
|
|
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
2022-07-02 12:02:17 +02:00
|
|
|
|
2024-02-20 10:10:05 +01:00
|
|
|
if !config.DisableAutoConnect {
|
|
|
|
go func() {
|
|
|
|
if err := internal.RunClientWithProbes(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
|
|
|
|
log.Errorf("init connections: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2022-03-08 14:47:55 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-18 00:22:47 +02:00
|
|
|
// loginAttempt attempts to login using the provided information. it returns a status in case something fails
|
|
|
|
func (s *Server) loginAttempt(ctx context.Context, setupKey, jwtToken string) (internal.StatusType, error) {
|
|
|
|
var status internal.StatusType
|
|
|
|
err := internal.Login(ctx, s.config, setupKey, jwtToken)
|
|
|
|
if err != nil {
|
|
|
|
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
|
|
|
|
log.Warnf("failed login: %v", err)
|
|
|
|
status = internal.StatusNeedsLogin
|
|
|
|
} else {
|
|
|
|
log.Errorf("failed login: %v", err)
|
|
|
|
status = internal.StatusLoginFailed
|
|
|
|
}
|
|
|
|
return status, err
|
|
|
|
}
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
// Login uses setup key to prepare configuration for the daemon.
|
2022-05-25 23:25:02 +02:00
|
|
|
func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*proto.LoginResponse, error) {
|
2022-03-08 14:47:55 +01:00
|
|
|
s.mutex.Lock()
|
2022-04-15 17:30:12 +02:00
|
|
|
if s.actCancel != nil {
|
|
|
|
s.actCancel()
|
|
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(s.rootCtx)
|
2022-05-25 23:25:02 +02:00
|
|
|
|
|
|
|
md, ok := metadata.FromIncomingContext(callerCtx)
|
|
|
|
if ok {
|
|
|
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
|
|
|
}
|
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
s.actCancel = cancel
|
|
|
|
s.mutex.Unlock()
|
|
|
|
|
|
|
|
state := internal.CtxGetState(ctx)
|
2022-05-12 11:17:24 +02:00
|
|
|
defer func() {
|
2022-05-18 00:22:47 +02:00
|
|
|
status, err := state.Status()
|
|
|
|
if err != nil || (status != internal.StatusNeedsLogin && status != internal.StatusLoginFailed) {
|
2022-05-12 11:17:24 +02:00
|
|
|
state.Set(internal.StatusIdle)
|
|
|
|
}
|
|
|
|
}()
|
2022-03-08 14:47:55 +01:00
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
s.mutex.Lock()
|
2023-01-17 19:16:50 +01:00
|
|
|
inputConfig := s.latestConfigInput
|
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
if msg.ManagementUrl != "" {
|
2023-01-17 19:16:50 +01:00
|
|
|
inputConfig.ManagementURL = msg.ManagementUrl
|
|
|
|
s.latestConfigInput.ManagementURL = msg.ManagementUrl
|
2022-03-08 14:47:55 +01:00
|
|
|
}
|
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
if msg.AdminURL != "" {
|
2023-01-17 19:16:50 +01:00
|
|
|
inputConfig.AdminURL = msg.AdminURL
|
|
|
|
s.latestConfigInput.AdminURL = msg.AdminURL
|
2022-04-15 17:30:12 +02:00
|
|
|
}
|
2023-01-17 19:16:50 +01:00
|
|
|
|
|
|
|
if msg.CleanNATExternalIPs {
|
|
|
|
inputConfig.NATExternalIPs = make([]string, 0)
|
|
|
|
s.latestConfigInput.NATExternalIPs = nil
|
|
|
|
} else if msg.NatExternalIPs != nil {
|
|
|
|
inputConfig.NATExternalIPs = msg.NatExternalIPs
|
|
|
|
s.latestConfigInput.NATExternalIPs = msg.NatExternalIPs
|
|
|
|
}
|
|
|
|
|
|
|
|
inputConfig.CustomDNSAddress = msg.CustomDNSAddress
|
|
|
|
s.latestConfigInput.CustomDNSAddress = msg.CustomDNSAddress
|
|
|
|
if string(msg.CustomDNSAddress) == "empty" {
|
|
|
|
inputConfig.CustomDNSAddress = []byte{}
|
|
|
|
s.latestConfigInput.CustomDNSAddress = []byte{}
|
|
|
|
}
|
|
|
|
|
2023-11-29 15:01:27 +01:00
|
|
|
if msg.Hostname != "" {
|
|
|
|
// nolint
|
|
|
|
ctx = context.WithValue(ctx, system.DeviceNameCtxKey, msg.Hostname)
|
|
|
|
}
|
|
|
|
|
2024-01-08 12:25:35 +01:00
|
|
|
if msg.RosenpassEnabled != nil {
|
|
|
|
inputConfig.RosenpassEnabled = msg.RosenpassEnabled
|
|
|
|
s.latestConfigInput.RosenpassEnabled = msg.RosenpassEnabled
|
|
|
|
}
|
|
|
|
|
2024-02-21 17:23:17 +01:00
|
|
|
if msg.RosenpassPermissive != nil {
|
|
|
|
inputConfig.RosenpassPermissive = msg.RosenpassPermissive
|
|
|
|
s.latestConfigInput.RosenpassPermissive = msg.RosenpassPermissive
|
|
|
|
}
|
|
|
|
|
2024-02-20 11:13:27 +01:00
|
|
|
if msg.ServerSSHAllowed != nil {
|
|
|
|
inputConfig.ServerSSHAllowed = msg.ServerSSHAllowed
|
|
|
|
s.latestConfigInput.ServerSSHAllowed = msg.ServerSSHAllowed
|
|
|
|
}
|
|
|
|
|
2024-02-20 10:10:05 +01:00
|
|
|
if msg.DisableAutoConnect != nil {
|
|
|
|
inputConfig.DisableAutoConnect = msg.DisableAutoConnect
|
|
|
|
s.latestConfigInput.DisableAutoConnect = msg.DisableAutoConnect
|
|
|
|
}
|
|
|
|
|
2024-01-15 15:53:23 +01:00
|
|
|
if msg.InterfaceName != nil {
|
|
|
|
inputConfig.InterfaceName = msg.InterfaceName
|
|
|
|
s.latestConfigInput.InterfaceName = msg.InterfaceName
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg.WireguardPort != nil {
|
|
|
|
port := int(*msg.WireguardPort)
|
|
|
|
inputConfig.WireguardPort = &port
|
|
|
|
s.latestConfigInput.WireguardPort = &port
|
|
|
|
}
|
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
s.mutex.Unlock()
|
|
|
|
|
2024-01-19 10:30:41 +01:00
|
|
|
if msg.OptionalPreSharedKey != nil {
|
|
|
|
inputConfig.PreSharedKey = msg.OptionalPreSharedKey
|
|
|
|
}
|
2023-01-17 19:16:50 +01:00
|
|
|
|
2023-03-02 13:28:14 +01:00
|
|
|
config, err := internal.UpdateOrCreateConfig(inputConfig)
|
2022-03-08 14:47:55 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-04-15 17:30:12 +02:00
|
|
|
|
2022-07-30 19:17:18 +02:00
|
|
|
if msg.ManagementUrl == "" {
|
2023-12-27 20:56:04 +01:00
|
|
|
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
|
2022-07-30 19:17:18 +02:00
|
|
|
s.config = config
|
2023-01-17 19:16:50 +01:00
|
|
|
s.latestConfigInput.ManagementURL = config.ManagementURL.String()
|
2022-07-30 19:17:18 +02:00
|
|
|
}
|
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
s.mutex.Lock()
|
2022-03-08 14:47:55 +01:00
|
|
|
s.config = config
|
2022-04-15 17:30:12 +02:00
|
|
|
s.mutex.Unlock()
|
2022-03-08 14:47:55 +01:00
|
|
|
|
2022-05-18 00:22:47 +02:00
|
|
|
if _, err := s.loginAttempt(ctx, "", ""); err == nil {
|
|
|
|
state.Set(internal.StatusIdle)
|
|
|
|
return &proto.LoginResponse{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
state.Set(internal.StatusConnecting)
|
|
|
|
|
2022-05-12 11:17:24 +02:00
|
|
|
if msg.SetupKey == "" {
|
2023-09-28 14:02:37 +02:00
|
|
|
oAuthFlow, err := auth.NewOAuthFlow(ctx, config, msg.IsLinuxDesktopClient)
|
2022-05-12 11:17:24 +02:00
|
|
|
if err != nil {
|
|
|
|
state.Set(internal.StatusLoginFailed)
|
2023-07-27 11:31:07 +02:00
|
|
|
return nil, err
|
2022-05-12 11:17:24 +02:00
|
|
|
}
|
|
|
|
|
2023-07-27 11:31:07 +02:00
|
|
|
if s.oauthAuthFlow.flow != nil && s.oauthAuthFlow.flow.GetClientID(ctx) == oAuthFlow.GetClientID(context.TODO()) {
|
2022-05-28 18:37:08 +02:00
|
|
|
if s.oauthAuthFlow.expiresAt.After(time.Now().Add(90 * time.Second)) {
|
2023-07-27 11:31:07 +02:00
|
|
|
log.Debugf("using previous oauth flow info")
|
2022-05-28 18:37:08 +02:00
|
|
|
return &proto.LoginResponse{
|
|
|
|
NeedsSSOLogin: true,
|
|
|
|
VerificationURI: s.oauthAuthFlow.info.VerificationURI,
|
|
|
|
VerificationURIComplete: s.oauthAuthFlow.info.VerificationURIComplete,
|
|
|
|
UserCode: s.oauthAuthFlow.info.UserCode,
|
|
|
|
}, nil
|
|
|
|
} else {
|
|
|
|
log.Warnf("canceling previous waiting execution")
|
2023-05-23 17:54:47 +02:00
|
|
|
if s.oauthAuthFlow.waitCancel != nil {
|
|
|
|
s.oauthAuthFlow.waitCancel()
|
|
|
|
}
|
2022-05-28 18:37:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-27 11:31:07 +02:00
|
|
|
authInfo, err := oAuthFlow.RequestAuthInfo(context.TODO())
|
2022-05-12 11:17:24 +02:00
|
|
|
if err != nil {
|
2023-07-27 11:31:07 +02:00
|
|
|
log.Errorf("getting a request OAuth flow failed: %v", err)
|
2022-05-12 11:17:24 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.mutex.Lock()
|
2023-07-27 11:31:07 +02:00
|
|
|
s.oauthAuthFlow.flow = oAuthFlow
|
|
|
|
s.oauthAuthFlow.info = authInfo
|
|
|
|
s.oauthAuthFlow.expiresAt = time.Now().Add(time.Duration(authInfo.ExpiresIn) * time.Second)
|
2022-05-12 11:17:24 +02:00
|
|
|
s.mutex.Unlock()
|
|
|
|
|
|
|
|
state.Set(internal.StatusNeedsLogin)
|
|
|
|
|
|
|
|
return &proto.LoginResponse{
|
|
|
|
NeedsSSOLogin: true,
|
2023-07-27 11:31:07 +02:00
|
|
|
VerificationURI: authInfo.VerificationURI,
|
|
|
|
VerificationURIComplete: authInfo.VerificationURIComplete,
|
|
|
|
UserCode: authInfo.UserCode,
|
2022-05-12 11:17:24 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-05-18 00:22:47 +02:00
|
|
|
if loginStatus, err := s.loginAttempt(ctx, msg.SetupKey, ""); err != nil {
|
|
|
|
state.Set(loginStatus)
|
2022-03-08 14:47:55 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &proto.LoginResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2022-05-12 11:17:24 +02:00
|
|
|
// WaitSSOLogin uses the userCode to validate the TokenInfo and
|
|
|
|
// waits for the user to continue with the login on a browser
|
2022-05-25 23:25:02 +02:00
|
|
|
func (s *Server) WaitSSOLogin(callerCtx context.Context, msg *proto.WaitSSOLoginRequest) (*proto.WaitSSOLoginResponse, error) {
|
2022-05-12 11:17:24 +02:00
|
|
|
s.mutex.Lock()
|
|
|
|
if s.actCancel != nil {
|
|
|
|
s.actCancel()
|
|
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(s.rootCtx)
|
2022-05-25 23:25:02 +02:00
|
|
|
|
|
|
|
md, ok := metadata.FromIncomingContext(callerCtx)
|
|
|
|
if ok {
|
|
|
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
|
|
|
}
|
|
|
|
|
2023-11-29 15:01:27 +01:00
|
|
|
if msg.Hostname != "" {
|
|
|
|
// nolint
|
|
|
|
ctx = context.WithValue(ctx, system.DeviceNameCtxKey, msg.Hostname)
|
|
|
|
}
|
|
|
|
|
2022-05-12 11:17:24 +02:00
|
|
|
s.actCancel = cancel
|
|
|
|
s.mutex.Unlock()
|
|
|
|
|
2023-07-27 11:31:07 +02:00
|
|
|
if s.oauthAuthFlow.flow == nil {
|
|
|
|
return nil, gstatus.Errorf(codes.Internal, "oauth flow is not initialized")
|
2022-05-12 11:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
state := internal.CtxGetState(ctx)
|
|
|
|
defer func() {
|
|
|
|
s, err := state.Status()
|
|
|
|
if err != nil || (s != internal.StatusNeedsLogin && s != internal.StatusLoginFailed) {
|
|
|
|
state.Set(internal.StatusIdle)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
state.Set(internal.StatusConnecting)
|
|
|
|
|
|
|
|
s.mutex.Lock()
|
2023-07-27 11:31:07 +02:00
|
|
|
flowInfo := s.oauthAuthFlow.info
|
2022-05-12 11:17:24 +02:00
|
|
|
s.mutex.Unlock()
|
|
|
|
|
2023-07-27 11:31:07 +02:00
|
|
|
if flowInfo.UserCode != msg.UserCode {
|
2022-05-12 11:17:24 +02:00
|
|
|
state.Set(internal.StatusLoginFailed)
|
|
|
|
return nil, gstatus.Errorf(codes.InvalidArgument, "sso user code is invalid")
|
|
|
|
}
|
|
|
|
|
2022-05-28 18:37:08 +02:00
|
|
|
if s.oauthAuthFlow.waitCancel != nil {
|
|
|
|
s.oauthAuthFlow.waitCancel()
|
|
|
|
}
|
|
|
|
|
|
|
|
waitTimeout := time.Until(s.oauthAuthFlow.expiresAt)
|
|
|
|
waitCTX, cancel := context.WithTimeout(ctx, waitTimeout)
|
2022-05-12 11:17:24 +02:00
|
|
|
defer cancel()
|
|
|
|
|
2022-05-28 18:37:08 +02:00
|
|
|
s.mutex.Lock()
|
|
|
|
s.oauthAuthFlow.waitCancel = cancel
|
|
|
|
s.mutex.Unlock()
|
|
|
|
|
2023-07-27 11:31:07 +02:00
|
|
|
tokenInfo, err := s.oauthAuthFlow.flow.WaitToken(waitCTX, flowInfo)
|
2022-05-12 11:17:24 +02:00
|
|
|
if err != nil {
|
2022-05-28 18:37:08 +02:00
|
|
|
if err == context.Canceled {
|
2023-09-04 17:03:44 +02:00
|
|
|
return nil, nil //nolint:nilnil
|
2022-05-28 18:37:08 +02:00
|
|
|
}
|
|
|
|
s.mutex.Lock()
|
|
|
|
s.oauthAuthFlow.expiresAt = time.Now()
|
|
|
|
s.mutex.Unlock()
|
2022-05-12 11:17:24 +02:00
|
|
|
state.Set(internal.StatusLoginFailed)
|
|
|
|
log.Errorf("waiting for browser login failed: %v", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-06-09 02:14:31 +02:00
|
|
|
s.mutex.Lock()
|
|
|
|
s.oauthAuthFlow.expiresAt = time.Now()
|
|
|
|
s.mutex.Unlock()
|
|
|
|
|
2023-04-05 17:46:34 +02:00
|
|
|
if loginStatus, err := s.loginAttempt(ctx, "", tokenInfo.GetTokenToUse()); err != nil {
|
2022-05-18 00:22:47 +02:00
|
|
|
state.Set(loginStatus)
|
2022-05-12 11:17:24 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &proto.WaitSSOLoginResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
// Up starts engine work in the daemon.
|
2022-12-06 15:37:30 +01:00
|
|
|
func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpResponse, error) {
|
2022-03-08 14:47:55 +01:00
|
|
|
s.mutex.Lock()
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
state := internal.CtxGetState(s.rootCtx)
|
|
|
|
|
|
|
|
// if current state contains any error, return it
|
|
|
|
// in all other cases we can continue execution only if status is idle and up command was
|
2022-05-05 20:02:15 +02:00
|
|
|
// not in the progress or already successfully established connection.
|
2022-03-08 14:47:55 +01:00
|
|
|
status, err := state.Status()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if status != internal.StatusIdle {
|
|
|
|
return nil, fmt.Errorf("up already in progress: current status %s", status)
|
|
|
|
}
|
|
|
|
|
2022-05-12 11:17:24 +02:00
|
|
|
// it should be nil here, but .
|
2022-03-08 14:47:55 +01:00
|
|
|
if s.actCancel != nil {
|
|
|
|
s.actCancel()
|
|
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(s.rootCtx)
|
2022-05-25 23:25:02 +02:00
|
|
|
|
|
|
|
md, ok := metadata.FromIncomingContext(callerCtx)
|
|
|
|
if ok {
|
|
|
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
|
|
|
}
|
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
s.actCancel = cancel
|
|
|
|
|
|
|
|
if s.config == nil {
|
|
|
|
return nil, fmt.Errorf("config is not defined, please call login command first")
|
|
|
|
}
|
|
|
|
|
2022-07-02 12:02:17 +02:00
|
|
|
if s.statusRecorder == nil {
|
2023-03-16 17:22:36 +01:00
|
|
|
s.statusRecorder = peer.NewRecorder(s.config.ManagementURL.String())
|
2022-07-02 12:02:17 +02:00
|
|
|
}
|
2024-02-24 12:41:13 +01:00
|
|
|
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
|
|
|
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
2022-07-02 12:02:17 +02:00
|
|
|
|
2022-03-08 14:47:55 +01:00
|
|
|
go func() {
|
2024-01-22 12:20:24 +01:00
|
|
|
if err := internal.RunClientWithProbes(ctx, s.config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
|
2022-12-06 15:37:30 +01:00
|
|
|
log.Errorf("run client connection: %v", err)
|
2022-03-08 14:47:55 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return &proto.UpResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2022-05-12 11:17:24 +02:00
|
|
|
// Down engine work in the daemon.
|
2022-12-06 15:37:30 +01:00
|
|
|
func (s *Server) Down(_ context.Context, _ *proto.DownRequest) (*proto.DownResponse, error) {
|
2022-03-08 14:47:55 +01:00
|
|
|
s.mutex.Lock()
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
if s.actCancel == nil {
|
|
|
|
return nil, fmt.Errorf("service is not up")
|
|
|
|
}
|
|
|
|
s.actCancel()
|
2022-12-06 15:37:30 +01:00
|
|
|
state := internal.CtxGetState(s.rootCtx)
|
|
|
|
state.Set(internal.StatusIdle)
|
2022-03-08 14:47:55 +01:00
|
|
|
|
|
|
|
return &proto.DownResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2024-01-22 12:20:24 +01:00
|
|
|
// Status returns the daemon status
|
2022-05-05 20:00:28 +02:00
|
|
|
func (s *Server) Status(
|
2022-07-05 19:47:50 +02:00
|
|
|
_ context.Context,
|
2022-05-05 20:00:28 +02:00
|
|
|
msg *proto.StatusRequest,
|
|
|
|
) (*proto.StatusResponse, error) {
|
2022-03-08 14:47:55 +01:00
|
|
|
s.mutex.Lock()
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
status, err := internal.CtxGetState(s.rootCtx).Status()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-03-15 07:54:51 +01:00
|
|
|
statusResponse := proto.StatusResponse{Status: string(status), DaemonVersion: version.NetbirdVersion()}
|
2022-07-05 19:47:50 +02:00
|
|
|
|
2022-07-07 13:54:47 +02:00
|
|
|
if s.statusRecorder == nil {
|
2023-03-16 17:22:36 +01:00
|
|
|
s.statusRecorder = peer.NewRecorder(s.config.ManagementURL.String())
|
2022-07-07 13:54:47 +02:00
|
|
|
}
|
2024-02-24 12:41:13 +01:00
|
|
|
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
|
|
|
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
2022-07-07 13:54:47 +02:00
|
|
|
|
2022-07-05 19:47:50 +02:00
|
|
|
if msg.GetFullPeerStatus {
|
2024-01-22 12:20:24 +01:00
|
|
|
s.runProbes()
|
|
|
|
|
2022-07-05 19:47:50 +02:00
|
|
|
fullStatus := s.statusRecorder.GetFullStatus()
|
|
|
|
pbFullStatus := toProtoFullStatus(fullStatus)
|
|
|
|
statusResponse.FullStatus = pbFullStatus
|
|
|
|
}
|
|
|
|
|
|
|
|
return &statusResponse, nil
|
2022-03-08 14:47:55 +01:00
|
|
|
}
|
2022-04-15 17:30:12 +02:00
|
|
|
|
2024-01-22 12:20:24 +01:00
|
|
|
func (s *Server) runProbes() {
|
|
|
|
if time.Since(s.lastProbe) > probeThreshold {
|
|
|
|
managementHealthy := s.mgmProbe.Probe()
|
|
|
|
signalHealthy := s.signalProbe.Probe()
|
|
|
|
relayHealthy := s.relayProbe.Probe()
|
|
|
|
wgProbe := s.wgProbe.Probe()
|
|
|
|
|
|
|
|
// Update last time only if all probes were successful
|
|
|
|
if managementHealthy && signalHealthy && relayHealthy && wgProbe {
|
|
|
|
s.lastProbe = time.Now()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
// GetConfig of the daemon.
|
2022-12-06 15:37:30 +01:00
|
|
|
func (s *Server) GetConfig(_ context.Context, _ *proto.GetConfigRequest) (*proto.GetConfigResponse, error) {
|
2022-04-15 17:30:12 +02:00
|
|
|
s.mutex.Lock()
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
2023-01-17 19:16:50 +01:00
|
|
|
managementURL := s.latestConfigInput.ManagementURL
|
|
|
|
adminURL := s.latestConfigInput.AdminURL
|
2022-05-05 20:00:28 +02:00
|
|
|
preSharedKey := ""
|
|
|
|
|
|
|
|
if s.config != nil {
|
|
|
|
if managementURL == "" && s.config.ManagementURL != nil {
|
|
|
|
managementURL = s.config.ManagementURL.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.config.AdminURL != nil {
|
|
|
|
adminURL = s.config.AdminURL.String()
|
|
|
|
}
|
2022-04-15 17:30:12 +02:00
|
|
|
|
2022-05-05 20:00:28 +02:00
|
|
|
preSharedKey = s.config.PreSharedKey
|
|
|
|
if preSharedKey != "" {
|
|
|
|
preSharedKey = "**********"
|
|
|
|
}
|
2022-05-12 11:17:24 +02:00
|
|
|
|
2022-04-15 17:30:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &proto.GetConfigResponse{
|
|
|
|
ManagementUrl: managementURL,
|
|
|
|
AdminURL: adminURL,
|
2023-01-17 19:16:50 +01:00
|
|
|
ConfigFile: s.latestConfigInput.ConfigPath,
|
2022-04-15 17:30:12 +02:00
|
|
|
LogFile: s.logFile,
|
|
|
|
PreSharedKey: preSharedKey,
|
|
|
|
}, nil
|
|
|
|
}
|
2022-07-05 19:47:50 +02:00
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
2022-07-05 19:47:50 +02:00
|
|
|
pbFullStatus := proto.FullStatus{
|
|
|
|
ManagementState: &proto.ManagementState{},
|
|
|
|
SignalState: &proto.SignalState{},
|
|
|
|
LocalPeerState: &proto.LocalPeerState{},
|
|
|
|
Peers: []*proto.PeerState{},
|
2024-01-22 12:20:24 +01:00
|
|
|
Relays: []*proto.RelayState{},
|
2022-07-05 19:47:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pbFullStatus.ManagementState.URL = fullStatus.ManagementState.URL
|
|
|
|
pbFullStatus.ManagementState.Connected = fullStatus.ManagementState.Connected
|
2024-01-22 12:20:24 +01:00
|
|
|
if err := fullStatus.ManagementState.Error; err != nil {
|
|
|
|
pbFullStatus.ManagementState.Error = err.Error()
|
|
|
|
}
|
2022-07-05 19:47:50 +02:00
|
|
|
|
|
|
|
pbFullStatus.SignalState.URL = fullStatus.SignalState.URL
|
|
|
|
pbFullStatus.SignalState.Connected = fullStatus.SignalState.Connected
|
2024-01-22 12:20:24 +01:00
|
|
|
if err := fullStatus.SignalState.Error; err != nil {
|
|
|
|
pbFullStatus.SignalState.Error = err.Error()
|
|
|
|
}
|
2022-07-05 19:47:50 +02:00
|
|
|
|
|
|
|
pbFullStatus.LocalPeerState.IP = fullStatus.LocalPeerState.IP
|
|
|
|
pbFullStatus.LocalPeerState.PubKey = fullStatus.LocalPeerState.PubKey
|
|
|
|
pbFullStatus.LocalPeerState.KernelInterface = fullStatus.LocalPeerState.KernelInterface
|
2022-11-26 13:29:50 +01:00
|
|
|
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
2024-02-24 12:41:13 +01:00
|
|
|
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
|
|
|
|
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
2022-07-05 19:47:50 +02:00
|
|
|
|
|
|
|
for _, peerState := range fullStatus.Peers {
|
|
|
|
pbPeerState := &proto.PeerState{
|
2024-01-22 12:20:24 +01:00
|
|
|
IP: peerState.IP,
|
|
|
|
PubKey: peerState.PubKey,
|
|
|
|
ConnStatus: peerState.ConnStatus.String(),
|
|
|
|
ConnStatusUpdate: timestamppb.New(peerState.ConnStatusUpdate),
|
|
|
|
Relayed: peerState.Relayed,
|
|
|
|
Direct: peerState.Direct,
|
|
|
|
LocalIceCandidateType: peerState.LocalIceCandidateType,
|
|
|
|
RemoteIceCandidateType: peerState.RemoteIceCandidateType,
|
|
|
|
LocalIceCandidateEndpoint: peerState.LocalIceCandidateEndpoint,
|
|
|
|
RemoteIceCandidateEndpoint: peerState.RemoteIceCandidateEndpoint,
|
|
|
|
Fqdn: peerState.FQDN,
|
|
|
|
LastWireguardHandshake: timestamppb.New(peerState.LastWireguardHandshake),
|
|
|
|
BytesRx: peerState.BytesRx,
|
|
|
|
BytesTx: peerState.BytesTx,
|
2024-02-24 12:41:13 +01:00
|
|
|
RosenpassEnabled: peerState.RosenpassEnabled,
|
2022-07-05 19:47:50 +02:00
|
|
|
}
|
|
|
|
pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState)
|
|
|
|
}
|
2024-01-22 12:20:24 +01:00
|
|
|
|
|
|
|
for _, relayState := range fullStatus.Relays {
|
|
|
|
pbRelayState := &proto.RelayState{
|
|
|
|
URI: relayState.URI.String(),
|
|
|
|
Available: relayState.Err == nil,
|
|
|
|
}
|
|
|
|
if err := relayState.Err; err != nil {
|
|
|
|
pbRelayState.Error = err.Error()
|
|
|
|
}
|
|
|
|
pbFullStatus.Relays = append(pbFullStatus.Relays, pbRelayState)
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:47:50 +02:00
|
|
|
return &pbFullStatus
|
|
|
|
}
|