mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-02 11:39:06 +02:00
Merge branch 'main' into embedded-vnc
This commit is contained in:
+89
-16
@@ -2,10 +2,10 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -48,6 +48,8 @@ const (
|
||||
profileNameDesc = "profile name to use for the login. If not specified, the last used profile will be used."
|
||||
)
|
||||
|
||||
var errDaemonActiveProfileUnsupported = errors.New("daemon does not support active profile lookup")
|
||||
|
||||
var (
|
||||
foregroundMode bool
|
||||
dnsLabels []string
|
||||
@@ -122,23 +124,25 @@ func upFunc(cmd *cobra.Command, args []string) error {
|
||||
|
||||
pm := profilemanager.NewProfileManager()
|
||||
|
||||
username, err := user.Current()
|
||||
username, err := profilemanager.InvokingUser()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current user: %v", err)
|
||||
}
|
||||
|
||||
var activeProf *profilemanager.Profile
|
||||
var profileSwitched bool
|
||||
// switch profile if provided
|
||||
if profileName != "" {
|
||||
if err := switchOrCreateProfile(cmd.Context(), pm, profileName, username.Username); err != nil {
|
||||
activeProf, err = switchOrCreateProfile(cmd.Context(), pm, profileName, username.Username)
|
||||
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)
|
||||
} else {
|
||||
activeProf, err = pm.GetActiveProfile()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get active profile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if foregroundMode {
|
||||
@@ -150,13 +154,15 @@ func upFunc(cmd *cobra.Command, args []string) error {
|
||||
// switchOrCreateProfile switches the active profile to the one identified by
|
||||
// handle, creating it first when it does not exist yet. This restores the
|
||||
// pre-0.73 behaviour where `netbird up --profile <name>` auto-creates a
|
||||
// missing profile instead of failing.
|
||||
func switchOrCreateProfile(ctx context.Context, pm *profilemanager.ProfileManager, handle, username string) error {
|
||||
// missing profile instead of failing. Returns the daemon-resolved profile so
|
||||
// callers act on it directly instead of re-reading the local state, which is
|
||||
// not updated under sudo.
|
||||
func switchOrCreateProfile(ctx context.Context, pm *profilemanager.ProfileManager, handle, username string) (*profilemanager.Profile, error) {
|
||||
resolvedID, err := switchProfile(ctx, handle, username)
|
||||
if err != nil {
|
||||
st, ok := gstatus.FromError(err)
|
||||
if !ok || st.Code() != codes.NotFound {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
// Don't fail immediately on a create error: a concurrent run may
|
||||
// have created the profile between the NotFound above and this
|
||||
@@ -165,16 +171,16 @@ func switchOrCreateProfile(ctx context.Context, pm *profilemanager.ProfileManage
|
||||
_, createErr := createProfile(ctx, handle, username)
|
||||
if resolvedID, err = switchProfile(ctx, handle, username); err != nil {
|
||||
if createErr != nil {
|
||||
return fmt.Errorf("create profile: %w", createErr)
|
||||
return nil, fmt.Errorf("create profile: %w", createErr)
|
||||
}
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := pm.SwitchProfile(resolvedID); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return &profilemanager.Profile{ID: resolvedID}, nil
|
||||
}
|
||||
|
||||
// createProfile dials the daemon and creates a new profile with the given
|
||||
@@ -302,6 +308,30 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command, pm *profilemanager
|
||||
return fmt.Errorf("unable to get daemon status: %v", err)
|
||||
}
|
||||
|
||||
// Under sudo the invoking user's local active-profile mirror is never
|
||||
// written (the SwitchProfile write is a no-op), and plain root has no
|
||||
// invoking user at all — so the mirror read into activeProf above is stale
|
||||
// or defaulted and must not drive the daemon. With no --profile to make the
|
||||
// choice explicit, take the profile the daemon already holds for this user
|
||||
// instead: it stays on the user's current profile rather than silently
|
||||
// switching to the mirror's default, and refuses when the daemon is on
|
||||
// another user's profile.
|
||||
if profileName == "" && !profilemanager.MirrorIsAuthoritative() {
|
||||
u, err := profilemanager.InvokingUser()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current user: %v", err)
|
||||
}
|
||||
resolved, err := daemonActiveProfileForUser(ctx, client, u.Username)
|
||||
switch {
|
||||
case errors.Is(err, errDaemonActiveProfileUnsupported):
|
||||
log.Warnf("keeping the locally resolved profile: %v", err)
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
activeProf = resolved
|
||||
}
|
||||
}
|
||||
|
||||
if status.Status == string(internal.StatusConnected) {
|
||||
if !profileSwitched {
|
||||
cmd.Println("Already connected")
|
||||
@@ -314,7 +344,7 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command, pm *profilemanager
|
||||
}
|
||||
}
|
||||
|
||||
username, err := user.Current()
|
||||
username, err := profilemanager.InvokingUser()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current user: %v", err)
|
||||
}
|
||||
@@ -398,6 +428,17 @@ func doDaemonUp(ctx context.Context, cmd *cobra.Command, client proto.DaemonServ
|
||||
return nil
|
||||
}
|
||||
|
||||
// setBoolPtrIfChanged points dst at a copy of val when the named bool flag was
|
||||
// explicitly set on cmd. It collapses the repeated
|
||||
// "if cmd.Flag(x).Changed { field = &val }" pattern in the request builders into
|
||||
// a single call, keeping their cognitive complexity within bounds.
|
||||
func setBoolPtrIfChanged(cmd *cobra.Command, name string, dst **bool, val bool) {
|
||||
if cmd.Flag(name).Changed {
|
||||
dst2 := val
|
||||
*dst = &dst2
|
||||
}
|
||||
}
|
||||
|
||||
// setSSHSetConfigFields copies the SSH server flags the user actually
|
||||
// passed into req, leaving the rest unset so the daemon keeps the
|
||||
// persisted values.
|
||||
@@ -457,6 +498,7 @@ func setupSetConfigReq(customDNSAddressConverted []byte, cmd *cobra.Command, pro
|
||||
req.RosenpassPermissive = &rosenpassPermissive
|
||||
}
|
||||
setSSHSetConfigFields(&req, cmd)
|
||||
setBoolPtrIfChanged(cmd, remoteJobsAllowedFlag, &req.RemoteJobsAllowed, remoteJobsAllowed)
|
||||
setVNCSetConfigFields(&req, cmd)
|
||||
|
||||
if cmd.Flag(interfaceNameFlag).Changed {
|
||||
@@ -553,6 +595,8 @@ func setupConfig(customDNSAddressConverted []byte, cmd *cobra.Command, configFil
|
||||
if cmd.Flag(serverSSHAllowedFlag).Changed {
|
||||
ic.ServerSSHAllowed = &serverSSHAllowed
|
||||
}
|
||||
setBoolPtrIfChanged(cmd, remoteJobsAllowedFlag, &ic.RemoteJobsAllowed, remoteJobsAllowed)
|
||||
|
||||
if cmd.Flag(serverVNCAllowedFlag).Changed {
|
||||
ic.ServerVNCAllowed = &serverVNCAllowed
|
||||
}
|
||||
@@ -727,6 +771,7 @@ func setupLoginRequest(providedSetupKey string, customDNSAddressConverted []byte
|
||||
}
|
||||
|
||||
setSSHLoginFields(&loginRequest, cmd)
|
||||
setBoolPtrIfChanged(cmd, remoteJobsAllowedFlag, &loginRequest.RemoteJobsAllowed, remoteJobsAllowed)
|
||||
setVNCLoginFields(&loginRequest, cmd)
|
||||
|
||||
if cmd.Flag(disableAutoConnectFlag).Changed {
|
||||
@@ -912,3 +957,31 @@ func isValidAddrPort(input string) bool {
|
||||
_, err := netip.ParseAddrPort(input)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// daemonActiveProfileForUser returns the profile the daemon currently holds for
|
||||
// username, for the no --profile case where the local mirror is not
|
||||
// authoritative (sudo or plain root). It returns that profile when the daemon
|
||||
// owns it for this user or when the profile is unowned (empty username, as on a
|
||||
// fresh install), so the caller acts on the daemon's real state instead of the
|
||||
// stale mirror. It denies with a --profile hint when the daemon is on another
|
||||
// user's profile, when the lookup fails, or when the daemon reports no active
|
||||
// profile. Returns errDaemonActiveProfileUnsupported when the daemon predates
|
||||
// the RPC; the caller keeps the mirror-derived profile in that case.
|
||||
func daemonActiveProfileForUser(ctx context.Context, client proto.DaemonServiceClient, username string) (*profilemanager.Profile, error) {
|
||||
active, err := client.GetActiveProfile(ctx, &proto.GetActiveProfileRequest{})
|
||||
if err != nil {
|
||||
if st, ok := gstatus.FromError(err); ok && st.Code() == codes.Unimplemented {
|
||||
return nil, fmt.Errorf("%w: %v", errDaemonActiveProfileUnsupported, err)
|
||||
}
|
||||
return nil, fmt.Errorf("pass --profile to choose the profile explicitly: the daemon's active profile could not be verified: %v", err)
|
||||
}
|
||||
if active.GetId() == "" {
|
||||
return nil, fmt.Errorf("pass --profile to choose the profile explicitly: the daemon reported no active profile")
|
||||
}
|
||||
if active.GetUsername() != "" && active.GetUsername() != username {
|
||||
return nil, fmt.Errorf(
|
||||
"pass --profile to choose the profile explicitly: the daemon's active profile is %q (user %q) but this invocation runs for %q",
|
||||
active.GetProfileName(), active.GetUsername(), username)
|
||||
}
|
||||
return &profilemanager.Profile{ID: profilemanager.ID(active.GetId())}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user