mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-09 07:15:15 +02:00
[client] Feat: Support Multiple Profiles (#3980)
[client] Feat: Support Multiple Profiles (#3980)
This commit is contained in:
201
client/cmd/up.go
201
client/cmd/up.go
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@ -18,6 +19,7 @@ import (
|
||||
"github.com/netbirdio/netbird/client/iface"
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
"github.com/netbirdio/netbird/management/domain"
|
||||
@ -35,6 +37,9 @@ const (
|
||||
|
||||
noBrowserFlag = "no-browser"
|
||||
noBrowserDesc = "do not open the browser for SSO login"
|
||||
|
||||
profileNameFlag = "profile"
|
||||
profileNameDesc = "profile name to use for the login. If not specified, the last used profile will be used."
|
||||
)
|
||||
|
||||
var (
|
||||
@ -42,6 +47,8 @@ var (
|
||||
dnsLabels []string
|
||||
dnsLabelsValidated domain.List
|
||||
noBrowser bool
|
||||
profileName string
|
||||
configPath string
|
||||
|
||||
upCmd = &cobra.Command{
|
||||
Use: "up",
|
||||
@ -70,6 +77,8 @@ func init() {
|
||||
)
|
||||
|
||||
upCmd.PersistentFlags().BoolVar(&noBrowser, noBrowserFlag, false, noBrowserDesc)
|
||||
upCmd.PersistentFlags().StringVar(&profileName, profileNameFlag, "", profileNameDesc)
|
||||
upCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "Netbird config file location")
|
||||
|
||||
}
|
||||
|
||||
@ -101,13 +110,41 @@ func upFunc(cmd *cobra.Command, args []string) error {
|
||||
ctx = context.WithValue(ctx, system.DeviceNameCtxKey, hostName)
|
||||
}
|
||||
|
||||
if foregroundMode {
|
||||
return runInForegroundMode(ctx, cmd)
|
||||
pm := profilemanager.NewProfileManager()
|
||||
|
||||
username, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current user: %v", err)
|
||||
}
|
||||
return runInDaemonMode(ctx, cmd)
|
||||
|
||||
var profileSwitched bool
|
||||
// switch profile if provided
|
||||
if profileName != "" {
|
||||
err = switchProfile(cmd.Context(), profileName, username.Username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("switch profile: %v", err)
|
||||
}
|
||||
|
||||
err = pm.SwitchProfile(profileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("switch profile: %v", err)
|
||||
}
|
||||
|
||||
profileSwitched = true
|
||||
}
|
||||
|
||||
activeProf, err := pm.GetActiveProfile()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get active profile: %v", err)
|
||||
}
|
||||
|
||||
if foregroundMode {
|
||||
return runInForegroundMode(ctx, cmd, activeProf)
|
||||
}
|
||||
return runInDaemonMode(ctx, cmd, pm, activeProf, profileSwitched)
|
||||
}
|
||||
|
||||
func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
func runInForegroundMode(ctx context.Context, cmd *cobra.Command, activeProf *profilemanager.Profile) error {
|
||||
err := handleRebrand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -118,7 +155,18 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ic, err := setupConfig(customDNSAddressConverted, cmd)
|
||||
var configFilePath string
|
||||
if configPath != "" {
|
||||
configFilePath = configPath
|
||||
} else {
|
||||
var err error
|
||||
configFilePath, err = activeProf.FilePath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get active profile file path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ic, err := setupConfig(customDNSAddressConverted, cmd, configFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setup config: %v", err)
|
||||
}
|
||||
@ -128,12 +176,12 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := internal.UpdateOrCreateConfig(*ic)
|
||||
config, err := profilemanager.UpdateOrCreateConfig(*ic)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get config file: %v", err)
|
||||
}
|
||||
|
||||
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
|
||||
_, _ = profilemanager.UpdateOldManagementURL(ctx, config, configFilePath)
|
||||
|
||||
err = foregroundLogin(ctx, cmd, config, providedSetupKey)
|
||||
if err != nil {
|
||||
@ -153,10 +201,10 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
return connectClient.Run(nil)
|
||||
}
|
||||
|
||||
func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
func runInDaemonMode(ctx context.Context, cmd *cobra.Command, pm *profilemanager.ProfileManager, activeProf *profilemanager.Profile, profileSwitched bool) error {
|
||||
customDNSAddressConverted, err := parseCustomDNSAddress(cmd.Flag(dnsResolverAddress).Changed)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("parse custom DNS address: %v", err)
|
||||
}
|
||||
|
||||
conn, err := DialClientGRPCServer(ctx, daemonAddr)
|
||||
@ -181,10 +229,37 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
}
|
||||
|
||||
if status.Status == string(internal.StatusConnected) {
|
||||
cmd.Println("Already connected")
|
||||
return nil
|
||||
if !profileSwitched {
|
||||
cmd.Println("Already connected")
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := client.Down(ctx, &proto.DownRequest{}); err != nil {
|
||||
log.Errorf("call service down method: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
username, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current user: %v", err)
|
||||
}
|
||||
|
||||
// set the new config
|
||||
req := setupSetConfigReq(customDNSAddressConverted, cmd, activeProf.Name, username.Username)
|
||||
if _, err := client.SetConfig(ctx, req); err != nil {
|
||||
return fmt.Errorf("call service set config method: %v", err)
|
||||
}
|
||||
|
||||
if err := doDaemonUp(ctx, cmd, client, pm, activeProf, customDNSAddressConverted, username.Username); err != nil {
|
||||
return fmt.Errorf("daemon up failed: %v", err)
|
||||
}
|
||||
cmd.Println("Connected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func doDaemonUp(ctx context.Context, cmd *cobra.Command, client proto.DaemonServiceClient, pm *profilemanager.ProfileManager, activeProf *profilemanager.Profile, customDNSAddressConverted []byte, username string) error {
|
||||
|
||||
providedSetupKey, err := getSetupKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get setup key: %v", err)
|
||||
@ -195,6 +270,9 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
return fmt.Errorf("setup login request: %v", err)
|
||||
}
|
||||
|
||||
loginRequest.ProfileName = &activeProf.Name
|
||||
loginRequest.Username = &username
|
||||
|
||||
var loginErr error
|
||||
var loginResp *proto.LoginResponse
|
||||
|
||||
@ -219,26 +297,105 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
}
|
||||
|
||||
if loginResp.NeedsSSOLogin {
|
||||
|
||||
openURL(cmd, loginResp.VerificationURIComplete, loginResp.UserCode, noBrowser)
|
||||
|
||||
_, err = client.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode, Hostname: hostName})
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting sso login failed with: %v", err)
|
||||
if err := handleSSOLogin(ctx, cmd, loginResp, client, pm); err != nil {
|
||||
return fmt.Errorf("sso login failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := client.Up(ctx, &proto.UpRequest{}); err != nil {
|
||||
if _, err := client.Up(ctx, &proto.UpRequest{
|
||||
ProfileName: &activeProf.Name,
|
||||
Username: &username,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("call service up method: %v", err)
|
||||
}
|
||||
cmd.Println("Connected")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupConfig(customDNSAddressConverted []byte, cmd *cobra.Command) (*internal.ConfigInput, error) {
|
||||
ic := internal.ConfigInput{
|
||||
func setupSetConfigReq(customDNSAddressConverted []byte, cmd *cobra.Command, profileName, username string) *proto.SetConfigRequest {
|
||||
var req proto.SetConfigRequest
|
||||
req.ProfileName = profileName
|
||||
req.Username = username
|
||||
|
||||
req.ManagementUrl = managementURL
|
||||
req.AdminURL = adminURL
|
||||
req.NatExternalIPs = natExternalIPs
|
||||
req.CustomDNSAddress = customDNSAddressConverted
|
||||
req.ExtraIFaceBlacklist = extraIFaceBlackList
|
||||
req.DnsLabels = dnsLabelsValidated.ToPunycodeList()
|
||||
req.CleanDNSLabels = dnsLabels != nil && len(dnsLabels) == 0
|
||||
req.CleanNATExternalIPs = natExternalIPs != nil && len(natExternalIPs) == 0
|
||||
|
||||
if cmd.Flag(enableRosenpassFlag).Changed {
|
||||
req.RosenpassEnabled = &rosenpassEnabled
|
||||
}
|
||||
if cmd.Flag(rosenpassPermissiveFlag).Changed {
|
||||
req.RosenpassPermissive = &rosenpassPermissive
|
||||
}
|
||||
if cmd.Flag(serverSSHAllowedFlag).Changed {
|
||||
req.ServerSSHAllowed = &serverSSHAllowed
|
||||
}
|
||||
if cmd.Flag(interfaceNameFlag).Changed {
|
||||
if err := parseInterfaceName(interfaceName); err != nil {
|
||||
log.Errorf("parse interface name: %v", err)
|
||||
return nil
|
||||
}
|
||||
req.InterfaceName = &interfaceName
|
||||
}
|
||||
if cmd.Flag(wireguardPortFlag).Changed {
|
||||
p := int64(wireguardPort)
|
||||
req.WireguardPort = &p
|
||||
}
|
||||
|
||||
if cmd.Flag(networkMonitorFlag).Changed {
|
||||
req.NetworkMonitor = &networkMonitor
|
||||
}
|
||||
if rootCmd.PersistentFlags().Changed(preSharedKeyFlag) {
|
||||
req.OptionalPreSharedKey = &preSharedKey
|
||||
}
|
||||
if cmd.Flag(disableAutoConnectFlag).Changed {
|
||||
req.DisableAutoConnect = &autoConnectDisabled
|
||||
}
|
||||
|
||||
if cmd.Flag(dnsRouteIntervalFlag).Changed {
|
||||
req.DnsRouteInterval = durationpb.New(dnsRouteInterval)
|
||||
}
|
||||
|
||||
if cmd.Flag(disableClientRoutesFlag).Changed {
|
||||
req.DisableClientRoutes = &disableClientRoutes
|
||||
}
|
||||
|
||||
if cmd.Flag(disableServerRoutesFlag).Changed {
|
||||
req.DisableServerRoutes = &disableServerRoutes
|
||||
}
|
||||
|
||||
if cmd.Flag(disableDNSFlag).Changed {
|
||||
req.DisableDns = &disableDNS
|
||||
}
|
||||
|
||||
if cmd.Flag(disableFirewallFlag).Changed {
|
||||
req.DisableFirewall = &disableFirewall
|
||||
}
|
||||
|
||||
if cmd.Flag(blockLANAccessFlag).Changed {
|
||||
req.BlockLanAccess = &blockLANAccess
|
||||
}
|
||||
|
||||
if cmd.Flag(blockInboundFlag).Changed {
|
||||
req.BlockInbound = &blockInbound
|
||||
}
|
||||
|
||||
if cmd.Flag(enableLazyConnectionFlag).Changed {
|
||||
req.LazyConnectionEnabled = &lazyConnEnabled
|
||||
}
|
||||
|
||||
return &req
|
||||
}
|
||||
|
||||
func setupConfig(customDNSAddressConverted []byte, cmd *cobra.Command, configFilePath string) (*profilemanager.ConfigInput, error) {
|
||||
ic := profilemanager.ConfigInput{
|
||||
ManagementURL: managementURL,
|
||||
ConfigPath: configPath,
|
||||
ConfigPath: configFilePath,
|
||||
NATExternalIPs: natExternalIPs,
|
||||
CustomDNSAddress: customDNSAddressConverted,
|
||||
ExtraIFaceBlackList: extraIFaceBlackList,
|
||||
|
Reference in New Issue
Block a user