diff --git a/client/configs/configs.go b/client/configs/configs.go index 8f9c3ba28..72e910527 100644 --- a/client/configs/configs.go +++ b/client/configs/configs.go @@ -6,19 +6,30 @@ import ( "runtime" ) -var StateDir string +var ( + // StateDir holds persistent state (config, profiles, install metadata). + StateDir string + // RuntimeDir holds ephemeral artifacts that should not survive reboot, + // such as Unix sockets for daemon and per-session IPC. Empty on + // platforms without a conventional /var/run-style location. + RuntimeDir string +) func init() { - StateDir = os.Getenv("NB_STATE_DIR") - if StateDir != "" { - return - } switch runtime.GOOS { case "windows": StateDir = filepath.Join(os.Getenv("PROGRAMDATA"), "Netbird") case "darwin", "linux": StateDir = "/var/lib/netbird" + RuntimeDir = "/var/run/netbird" case "freebsd", "openbsd", "netbsd", "dragonfly": StateDir = "/var/db/netbird" + RuntimeDir = "/var/run/netbird" + } + if v := os.Getenv("NB_STATE_DIR"); v != "" { + StateDir = v + } + if v := os.Getenv("NB_RUNTIME_DIR"); v != "" { + RuntimeDir = v } } diff --git a/client/vnc/server/agent_darwin.go b/client/vnc/server/agent_darwin.go index 3c470f9da..0d6b371b5 100644 --- a/client/vnc/server/agent_darwin.go +++ b/client/vnc/server/agent_darwin.go @@ -17,6 +17,8 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" + + "github.com/netbirdio/netbird/client/configs" ) // darwinAgentManager spawns a per-user VNC agent on demand and keeps it @@ -133,37 +135,32 @@ func (m *darwinAgentManager) Resolve(ctx context.Context) (string, string, uint3 return socketPath, token, consoleUID, nil } -// agentSocketParentDir is the root the daemon creates (as root, mode 0755) -// to hold per-uid agent-socket subdirectories. Keeping it under -// /var/run/netbird-vnc (rather than /tmp) means a non-root local user -// cannot squat the socket path: only root can create the parent, and -// only the target user (plus root) can write inside the per-uid subdir. -const agentSocketParentDir = "/var/run/netbird-vnc" - -// prepareAgentSocketDir creates (and tightens permissions on) a per-uid -// subdirectory the agent will bind its socket inside, returning the -// directory path. The subdirectory is owned by uid with mode 0700, so -// the only writers are the target user and root. The parent is created -// root-owned with mode 0755 if it doesn't already exist. Symlinks at -// the per-uid level are refused (replaced with a fresh directory) to -// avoid a low-priv user redirecting our chown. +// prepareAgentSocketDir creates a per-uid subdirectory under the netbird +// runtime directory where the agent will bind its Unix socket. The leaf is +// owned by uid with mode 0700, so only the target user and root can write +// there. The parent is created root-owned with mode 0755 if missing. +// Symlinks at the per-uid level are refused (replaced with a fresh +// directory) so a low-priv user cannot redirect the chown that follows. func prepareAgentSocketDir(uid uint32) (string, error) { - if err := os.MkdirAll(agentSocketParentDir, 0o755); err != nil { - return "", fmt.Errorf("mkdir %s: %w", agentSocketParentDir, err) + parent := configs.RuntimeDir + if parent == "" { + return "", fmt.Errorf("no runtime directory configured for this platform") } - // Refuse to use the parent if it's a symlink or not owned by root. - pInfo, err := os.Lstat(agentSocketParentDir) + if err := os.MkdirAll(parent, 0o755); err != nil { + return "", fmt.Errorf("mkdir %s: %w", parent, err) + } + pInfo, err := os.Lstat(parent) if err != nil { - return "", fmt.Errorf("lstat %s: %w", agentSocketParentDir, err) + return "", fmt.Errorf("lstat %s: %w", parent, err) } if pInfo.Mode()&os.ModeSymlink != 0 { - return "", fmt.Errorf("%s is a symlink", agentSocketParentDir) + return "", fmt.Errorf("%s is a symlink", parent) } if st, ok := pInfo.Sys().(*syscall.Stat_t); ok && st.Uid != 0 { - return "", fmt.Errorf("%s not owned by root (uid=%d)", agentSocketParentDir, st.Uid) + return "", fmt.Errorf("%s not owned by root (uid=%d)", parent, st.Uid) } - subdir := fmt.Sprintf("%s/%d", agentSocketParentDir, uid) + subdir := fmt.Sprintf("%s/vnc-%d", parent, uid) // If a leftover entry exists, refuse it unless it's a real dir owned // by the right uid with strict perms: otherwise remove and recreate // from scratch under our control. Using os.Lstat (not Stat) so a