diff --git a/client/internal/updater/installer/installer_run_darwin.go b/client/internal/updater/installer/installer_run_darwin.go index 248a404aa..f67680774 100644 --- a/client/internal/updater/installer/installer_run_darwin.go +++ b/client/internal/updater/installer/installer_run_darwin.go @@ -98,39 +98,25 @@ func (u *Installer) startDaemon(daemonFolder string) error { func (u *Installer) startUIAsUser() error { log.Infof("starting netbird-ui: %s", uiBinary) - // Get the current console user - cmd := exec.Command("stat", "-f", "%Su", "/dev/console") - output, err := cmd.Output() + username, err := consoleUser() if err != nil { - return fmt.Errorf("failed to get console user: %w", err) + return err } - username := strings.TrimSpace(string(output)) - if username == "" || username == "root" { - return fmt.Errorf("no active user session found") - } - - log.Infof("starting UI for user: %s", username) - - // Get user's UID userInfo, err := user.Lookup(username) if err != nil { - return fmt.Errorf("failed to lookup user %s: %w", username, err) + return fmt.Errorf("lookup user %s: %w", username, err) } - // Start the UI process as the console user using launchctl - // This ensures the app runs in the user's context with proper GUI access - launchCmd := exec.Command("launchctl", "asuser", userInfo.Uid, "open", "-a", uiBinary) + log.Infof("starting UI for user: %s (uid %s)", username, userInfo.Uid) + + launchCmd := exec.Command("launchctl", "asuser", userInfo.Uid, "sudo", "-u", username, "-H", "open", "-a", uiBinary) log.Infof("launchCmd: %s", launchCmd.String()) - // Set the user's home directory for proper macOS app behavior - launchCmd.Env = append(os.Environ(), "HOME="+userInfo.HomeDir) - log.Infof("set HOME environment variable: %s", userInfo.HomeDir) if err := launchCmd.Start(); err != nil { - return fmt.Errorf("failed to start UI process: %w", err) + return fmt.Errorf("start UI process: %w", err) } - // Release the process so it can run independently if err := launchCmd.Process.Release(); err != nil { log.Warnf("failed to release UI process: %v", err) } @@ -139,6 +125,21 @@ func (u *Installer) startUIAsUser() error { return nil } +func consoleUser() (string, error) { + output, err := exec.Command("stat", "-f", "%Su", "/dev/console").Output() + if err != nil { + return "", fmt.Errorf("get console user: %w", err) + } + + username := strings.TrimSpace(string(output)) + switch username { + case "", "root", "loginwindow", "_mbsetupuser": + return "", fmt.Errorf("no active GUI user session, console user: %q", username) + } + + return username, nil +} + func (u *Installer) installPkgFile(ctx context.Context, path string) error { log.Infof("installing pkg file: %s", path) diff --git a/release_files/darwin_pkg/postinstall b/release_files/darwin_pkg/postinstall index a633d4d5c..2c96a80cd 100755 --- a/release_files/darwin_pkg/postinstall +++ b/release_files/darwin_pkg/postinstall @@ -30,10 +30,6 @@ mkdir -p /usr/local/bin/ $AGENT service install || true $AGENT service start || true - # Launch the GUI as the logged-in console user, NOT as the installer's - # (root) context. When there is no active GUI session - # (unattended MDM install, login window), there is nobody to launch as, skip it; - console_user=$(stat -f%Su /dev/console 2>/dev/null) case "$console_user" in ""|root|loginwindow|_mbsetupuser) @@ -41,13 +37,12 @@ mkdir -p /usr/local/bin/ ;; *) uid=$(id -u "$console_user" 2>/dev/null) - home_dir=$(dscl . -read "/Users/$console_user" NFSHomeDirectory 2>/dev/null | awk '{print $2}') - if [ -z "$uid" ] || [ -z "$home_dir" ]; then - echo "Could not resolve uid/home for console user '$console_user' (uid='$uid', home='$home_dir'); skipping UI launch." + if [ -z "$uid" ]; then + echo "Could not resolve uid for console user '$console_user'; skipping UI launch." else - echo "Launching NetBird UI as console user $console_user (uid $uid, home $home_dir)." - if ! HOME="$home_dir" launchctl asuser "$uid" open "$APP"; then - echo "Failed to launch NetBird UI; it will start at next login via the per-user LaunchAgent." + echo "Launching NetBird UI as console user $console_user (uid $uid)." + if ! launchctl asuser "$uid" sudo -u "$console_user" -H open "$APP"; then + echo "Failed to launch NetBird UI; if autostart is enabled it will start at next login." fi fi ;;