Merge branch 'main' into embedded-vnc

This commit is contained in:
Viktor Liu
2026-09-02 19:10:03 +02:00
153 changed files with 9089 additions and 1934 deletions
+98 -1
View File
@@ -70,6 +70,7 @@ type ConfigInput struct {
StateFilePath string
PreSharedKey *string
ServerSSHAllowed *bool
RemoteJobsAllowed *bool
ServerVNCAllowed *bool
DisableVNCApproval *bool
EnableSSHRoot *bool
@@ -129,6 +130,7 @@ type Config struct {
RosenpassEnabled bool
RosenpassPermissive bool
ServerSSHAllowed *bool
RemoteJobsAllowed *bool
ServerVNCAllowed *bool
DisableVNCApproval *bool
EnableSSHRoot *bool
@@ -196,6 +198,12 @@ type Config struct {
// Runtime-only: re-derived from MDM policy on each load, never persisted.
LazyConnection string `json:"-"`
// DebugBundleUploadURL is the MDM-managed debug-bundle upload URL override.
// When set, it takes precedence over the management-supplied upload URL for
// remote debug bundle jobs. Runtime-only: re-derived from MDM policy on each
// load, never persisted.
DebugBundleUploadURL string `json:"-"`
MTU uint16
// policy is the MDM policy that produced the currently-set values for
@@ -229,6 +237,12 @@ func getConfigDir() (string, error) {
}
configDir := filepath.Join(base, "netbird")
// Under sudo this is the invoking user's directory and strictly read-only:
// anything root creates in it would be root-owned and break the user's own
// runs. Reads of a missing directory fall through to defaults.
if sudoActive() {
return configDir, nil
}
if err := os.MkdirAll(configDir, 0o755); err != nil {
return "", err
}
@@ -236,6 +250,16 @@ func getConfigDir() (string, error) {
}
func baseConfigDir() (string, error) {
if u, ok := sudoInvokingUser(); ok {
return userBaseConfigDir(u)
}
// Fail closed instead of falling through to root's own config directory:
// reading root's active-profile and email state for what is actually the
// invoking user's invocation is the very confusion this resolution exists
// to prevent.
if sudoActive() {
return "", fmt.Errorf("resolve sudo invoking user %q: refusing to fall back to root's config directory", os.Getenv(envSudoUser))
}
if runtime.GOOS == "darwin" {
if u, err := user.Current(); err == nil && u.HomeDir != "" {
return filepath.Join(u.HomeDir, "Library", "Application Support"), nil
@@ -277,7 +301,10 @@ func createNewConfig(input ConfigInput) (*Config, error) {
config := &Config{
// defaults to false only for new (post 0.26) configurations
ServerSSHAllowed: util.False(),
WgPort: iface.DefaultWgPort,
// Remote jobs are an explicit opt-in and default off, including for
// legacy configs (a nil value materializes to false at connect time).
RemoteJobsAllowed: util.False(),
WgPort: iface.DefaultWgPort,
}
if _, err := config.apply(input); err != nil {
@@ -507,6 +534,21 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
}
}
if input.RemoteJobsAllowed != nil && (config.RemoteJobsAllowed == nil || *input.RemoteJobsAllowed != *config.RemoteJobsAllowed) {
if *input.RemoteJobsAllowed {
log.Infof("enabling remote jobs")
} else {
log.Infof("disabling remote jobs")
}
config.RemoteJobsAllowed = input.RemoteJobsAllowed
updated = true
} else if config.RemoteJobsAllowed == nil {
// Remote jobs are an explicit opt-in: unlike SSH, a pre-existing config
// with no value defaults to disabled rather than being turned on.
config.RemoteJobsAllowed = util.False()
updated = true
}
if input.EnableSSHRoot != nil && (config.EnableSSHRoot == nil || *input.EnableSSHRoot != *config.EnableSSHRoot) {
if *input.EnableSSHRoot {
log.Infof("enabling SSH root login")
@@ -716,6 +758,14 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
// for the key, so per-field rejection of user writes still applies).
func (config *Config) applyMDMPolicy(policy *mdm.Policy) {
config.policy = policy
// DebugBundleUploadURL is a runtime-only override re-derived from MDM on
// every apply. Resolve it unconditionally (before the IsEmpty early return)
// so a policy that drops the key, becomes empty, or carries an invalid
// value can never leave a previously-enforced upload target active on a
// reused Config instance.
config.DebugBundleUploadURL = mdmDebugBundleUploadURL(policy)
if policy.IsEmpty() {
return
}
@@ -763,6 +813,7 @@ func (config *Config) applyMDMPolicy(policy *mdm.Policy) {
}
applyBool(mdm.KeyAllowServerSSH, func(v bool) { bv := v; config.ServerSSHAllowed = &bv })
applyBool(mdm.KeyRemoteJobsAllowed, func(v bool) { bv := v; config.RemoteJobsAllowed = &bv })
applyBool(mdm.KeyAllowServerVNC, func(v bool) { bv := v; config.ServerVNCAllowed = &bv })
applyBool(mdm.KeyDisableVNCApproval, func(v bool) { bv := v; config.DisableVNCApproval = &bv })
applyBool(mdm.KeyDisableClientRoutes, func(v bool) { config.DisableClientRoutes = v })
@@ -798,6 +849,52 @@ func (config *Config) applyMDMPolicy(policy *mdm.Policy) {
config.LazyConnection = state
logApplied(mdm.KeyLazyConnection, state)
}
}
// ValidateBundleUploadURL sanity-checks a debug-bundle upload URL. An empty
// value is accepted — the executor falls back to the default upload service. A
// non-empty value must be a well-formed https URL with a host; a malformed
// value or a plaintext scheme is rejected. It deliberately does not constrain
// which host may receive the bundle. This is the single source of truth for the
// rule, shared by the remote-job executor (client/internal) and the MDM policy
// override below so the two validation paths cannot drift.
func ValidateBundleUploadURL(raw string) error {
if raw == "" {
return nil
}
parsed, err := url.Parse(raw)
if err != nil {
return fmt.Errorf("parse upload URL: %w", err)
}
// Hostname(), not Host: an authority like ":443" is non-empty but has no
// host, and would fail the actual upload.
if parsed.Scheme != "https" || parsed.Hostname() == "" {
return fmt.Errorf("upload URL must be an https URL with a host")
}
return nil
}
// mdmDebugBundleUploadURL resolves the MDM-enforced debug-bundle upload URL
// override from the policy, returning the empty string when the policy does
// not carry a valid KeyBundleUploadURL. An absent or invalid value fails
// closed to "" so it falls back to the management-supplied or default upload
// target rather than a previously-enforced one. The URL is never logged: it
// can embed credentials or signed query tokens (KeyBundleUploadURL is in
// mdm.SecretKeys).
func mdmDebugBundleUploadURL(policy *mdm.Policy) string {
v, ok := policy.GetString(mdm.KeyBundleUploadURL)
if !ok || v == "" {
return ""
}
// Must be a well-formed https URL with a host, matching the client's
// remote-job upload-URL validation (shared validator, single source of truth).
if err := ValidateBundleUploadURL(v); err != nil {
log.Warnf("MDM debug bundle upload URL is invalid (must be an https URL with a host); ignoring the override")
return ""
}
log.Infof("MDM override %s = ********** (secret)", mdm.KeyBundleUploadURL)
return v
}
// parseURL parses and validates the URL for the named service. The URL