mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-17 03:59:56 +00:00
* [client] Add autostart preference marker and MDM disableAutostart key Adds the autostartInitialized marker to the Wails UI preferences store so the one-time autostart default decision can persist per OS user, and a UI-only disableAutostart MDM policy key that suppresses the default and flows into GetConfigResponse.mDMManagedFields like disableAutoConnect. * [client] Enable launch-on-login by default on fresh GUI installs On the first interactive run the GUI persists the autostartInitialized marker before any enable attempt, then enables autostart only when the platform supports it, MDM policy does not disable it, the process was not relaunched by an installer/updater (--post-update), and the installer's fresh-install breadcrumb is present. Upgrading users have no breadcrumb, so an update can never write login items, and a user's disable in Settings is never overridden. * [release] Write fresh-install breadcrumb from installers Installers write a .fresh-install breadcrumb on fresh installs only and delete stale breadcrumbs on upgrade; none of them writes login items or registry Run keys. Windows NSIS detects upgrades via the uninstall registry entry or an existing installed executable; the macOS pkg via the previous pkgutil receipt; Linux deb/rpm via the standard postinstall arguments. Post-update GUI relaunches (macOS open, Linux ui-post-install.sh) pass --post-update so the first-run autostart default cannot fire on updates. * Revert installer breadcrumb changes The real Windows installer does uninstall-then-install and deletes $INSTDIR, so a breadcrumb written there cannot survive or discriminate a fresh install from an upgrade. Restore the three installer files to their main versions; no installer or updater writes an autostart entry. * Detect fresh install from NetBird footprint instead of installer breadcrumb Replace the installer-written breadcrumb discriminator with a GUI-side check. netbirdFootprintExists inspects the daemon config/state files (default.json, legacy config.json, state.json) under profilemanager's default config dir; combined with whether the UI preferences file already existed, this tells a genuinely fresh machine from an existing or upgrading user. Only the signed GUI, via Wails, ever enables launch-on-login, and a user's later manual disable is never overridden. The preferences store now exposes ExistedAtLoad and the --post-update flag is dropped. * Update tests for footprint-based autostart default Table tests for shouldEnableAutostartDefault now cover supported, mdmDisabled, and priorInstall guards plus precedence; breadcrumb and post-update cases are removed. Add a store test asserting ExistedAtLoad is false with no file and true after persisting and reopening.
258 lines
10 KiB
Go
258 lines
10 KiB
Go
//go:build !android && !ios && !freebsd && !js
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/netbirdio/netbird/client/proto"
|
|
)
|
|
|
|
type MDMFields struct {
|
|
ManagementURL string `json:"managementURL"`
|
|
PreSharedKey bool `json:"preSharedKey"`
|
|
WireguardPort bool `json:"wireguardPort"`
|
|
RosenpassEnabled bool `json:"rosenpassEnabled"`
|
|
RosenpassPermissive bool `json:"rosenpassPermissive"`
|
|
DisableClientRoutes bool `json:"disableClientRoutes"`
|
|
DisableServerRoutes bool `json:"disableServerRoutes"`
|
|
AllowServerSSH *bool `json:"allowServerSSH"`
|
|
DisableAutoConnect bool `json:"disableAutoConnect"`
|
|
DisableAutostart bool `json:"disableAutostart"`
|
|
BlockInbound bool `json:"blockInbound"`
|
|
DisableMetricsCollection bool `json:"disableMetricsCollection"`
|
|
SplitTunnelMode bool `json:"splitTunnelMode"`
|
|
SplitTunnelApps bool `json:"splitTunnelApps"`
|
|
DisableAdvancedView bool `json:"disableAdvancedView"`
|
|
}
|
|
|
|
type Features struct {
|
|
DisableProfiles bool `json:"disableProfiles"`
|
|
DisableNetworks bool `json:"disableNetworks"`
|
|
DisableUpdateSettings bool `json:"disableUpdateSettings"`
|
|
}
|
|
|
|
type Restrictions struct {
|
|
MDM MDMFields `json:"mdm"`
|
|
Features Features `json:"features"`
|
|
}
|
|
|
|
type ConfigParams struct {
|
|
ProfileName string `json:"profileName"`
|
|
Username string `json:"username"`
|
|
}
|
|
|
|
type Config struct {
|
|
ManagementURL string `json:"managementUrl"`
|
|
AdminURL string `json:"adminUrl"`
|
|
ConfigFile string `json:"configFile"`
|
|
LogFile string `json:"logFile"`
|
|
PreSharedKeySet bool `json:"preSharedKeySet"`
|
|
InterfaceName string `json:"interfaceName"`
|
|
WireguardPort int64 `json:"wireguardPort"`
|
|
MTU int64 `json:"mtu"`
|
|
DisableAutoConnect bool `json:"disableAutoConnect"`
|
|
ServerSSHAllowed bool `json:"serverSshAllowed"`
|
|
RosenpassEnabled bool `json:"rosenpassEnabled"`
|
|
RosenpassPermissive bool `json:"rosenpassPermissive"`
|
|
DisableNotifications bool `json:"disableNotifications"`
|
|
BlockInbound bool `json:"blockInbound"`
|
|
NetworkMonitor bool `json:"networkMonitor"`
|
|
DisableClientRoutes bool `json:"disableClientRoutes"`
|
|
DisableServerRoutes bool `json:"disableServerRoutes"`
|
|
DisableDNS bool `json:"disableDns"`
|
|
DisableIPv6 bool `json:"disableIpv6"`
|
|
BlockLANAccess bool `json:"blockLanAccess"`
|
|
EnableSSHRoot bool `json:"enableSshRoot"`
|
|
EnableSSHSFTP bool `json:"enableSshSftp"`
|
|
EnableSSHLocalPortForwarding bool `json:"enableSshLocalPortForwarding"`
|
|
EnableSSHRemotePortForwarding bool `json:"enableSshRemotePortForwarding"`
|
|
DisableSSHAuth bool `json:"disableSshAuth"`
|
|
SSHJWTCacheTTL int32 `json:"sshJwtCacheTtl"`
|
|
}
|
|
|
|
// SetConfigParams is a partial update — only non-nil pointer fields are sent
|
|
// to the daemon; nil fields are preserved.
|
|
type SetConfigParams struct {
|
|
ProfileName string `json:"profileName"`
|
|
Username string `json:"username"`
|
|
ManagementURL string `json:"managementUrl"`
|
|
AdminURL string `json:"adminUrl"`
|
|
InterfaceName *string `json:"interfaceName,omitempty"`
|
|
WireguardPort *int64 `json:"wireguardPort,omitempty"`
|
|
MTU *int64 `json:"mtu,omitempty"`
|
|
PreSharedKey *string `json:"preSharedKey,omitempty"`
|
|
DisableAutoConnect *bool `json:"disableAutoConnect,omitempty"`
|
|
ServerSSHAllowed *bool `json:"serverSshAllowed,omitempty"`
|
|
RosenpassEnabled *bool `json:"rosenpassEnabled,omitempty"`
|
|
RosenpassPermissive *bool `json:"rosenpassPermissive,omitempty"`
|
|
DisableNotifications *bool `json:"disableNotifications,omitempty"`
|
|
BlockInbound *bool `json:"blockInbound,omitempty"`
|
|
NetworkMonitor *bool `json:"networkMonitor,omitempty"`
|
|
DisableClientRoutes *bool `json:"disableClientRoutes,omitempty"`
|
|
DisableServerRoutes *bool `json:"disableServerRoutes,omitempty"`
|
|
DisableDNS *bool `json:"disableDns,omitempty"`
|
|
DisableIPv6 *bool `json:"disableIpv6,omitempty"`
|
|
DisableFirewall *bool `json:"disableFirewall,omitempty"`
|
|
BlockLANAccess *bool `json:"blockLanAccess,omitempty"`
|
|
EnableSSHRoot *bool `json:"enableSshRoot,omitempty"`
|
|
EnableSSHSFTP *bool `json:"enableSshSftp,omitempty"`
|
|
EnableSSHLocalPortForwarding *bool `json:"enableSshLocalPortForwarding,omitempty"`
|
|
EnableSSHRemotePortForwarding *bool `json:"enableSshRemotePortForwarding,omitempty"`
|
|
DisableSSHAuth *bool `json:"disableSshAuth,omitempty"`
|
|
SSHJWTCacheTTL *int32 `json:"sshJwtCacheTtl,omitempty"`
|
|
}
|
|
|
|
type Settings struct {
|
|
conn DaemonConn
|
|
}
|
|
|
|
func NewSettings(conn DaemonConn) *Settings {
|
|
return &Settings{conn: conn}
|
|
}
|
|
|
|
func (s *Settings) GetConfig(ctx context.Context, p ConfigParams) (Config, error) {
|
|
cli, err := s.conn.Client()
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
resp, err := cli.GetConfig(ctx, &proto.GetConfigRequest{
|
|
ProfileName: p.ProfileName,
|
|
Username: p.Username,
|
|
})
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
return Config{
|
|
ManagementURL: resp.GetManagementUrl(),
|
|
AdminURL: resp.GetAdminURL(),
|
|
ConfigFile: resp.GetConfigFile(),
|
|
LogFile: resp.GetLogFile(),
|
|
PreSharedKeySet: resp.GetPreSharedKey() != "",
|
|
InterfaceName: resp.GetInterfaceName(),
|
|
WireguardPort: resp.GetWireguardPort(),
|
|
MTU: resp.GetMtu(),
|
|
DisableAutoConnect: resp.GetDisableAutoConnect(),
|
|
ServerSSHAllowed: resp.GetServerSSHAllowed(),
|
|
RosenpassEnabled: resp.GetRosenpassEnabled(),
|
|
RosenpassPermissive: resp.GetRosenpassPermissive(),
|
|
DisableNotifications: resp.GetDisableNotifications(),
|
|
BlockInbound: resp.GetBlockInbound(),
|
|
NetworkMonitor: resp.GetNetworkMonitor(),
|
|
DisableClientRoutes: resp.GetDisableClientRoutes(),
|
|
DisableServerRoutes: resp.GetDisableServerRoutes(),
|
|
DisableDNS: resp.GetDisableDns(),
|
|
DisableIPv6: resp.GetDisableIpv6(),
|
|
BlockLANAccess: resp.GetBlockLanAccess(),
|
|
EnableSSHRoot: resp.GetEnableSSHRoot(),
|
|
EnableSSHSFTP: resp.GetEnableSSHSFTP(),
|
|
EnableSSHLocalPortForwarding: resp.GetEnableSSHLocalPortForwarding(),
|
|
EnableSSHRemotePortForwarding: resp.GetEnableSSHRemotePortForwarding(),
|
|
DisableSSHAuth: resp.GetDisableSSHAuth(),
|
|
SSHJWTCacheTTL: resp.GetSshJWTCacheTTL(),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Settings) SetConfig(ctx context.Context, p SetConfigParams) error {
|
|
cli, err := s.conn.Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req := &proto.SetConfigRequest{
|
|
ProfileName: p.ProfileName,
|
|
Username: p.Username,
|
|
ManagementUrl: p.ManagementURL,
|
|
AdminURL: p.AdminURL,
|
|
InterfaceName: p.InterfaceName,
|
|
WireguardPort: p.WireguardPort,
|
|
Mtu: p.MTU,
|
|
OptionalPreSharedKey: p.PreSharedKey,
|
|
DisableAutoConnect: p.DisableAutoConnect,
|
|
ServerSSHAllowed: p.ServerSSHAllowed,
|
|
RosenpassEnabled: p.RosenpassEnabled,
|
|
RosenpassPermissive: p.RosenpassPermissive,
|
|
DisableNotifications: p.DisableNotifications,
|
|
BlockInbound: p.BlockInbound,
|
|
NetworkMonitor: p.NetworkMonitor,
|
|
DisableClientRoutes: p.DisableClientRoutes,
|
|
DisableServerRoutes: p.DisableServerRoutes,
|
|
DisableDns: p.DisableDNS,
|
|
DisableIpv6: p.DisableIPv6,
|
|
DisableFirewall: p.DisableFirewall,
|
|
BlockLanAccess: p.BlockLANAccess,
|
|
EnableSSHRoot: p.EnableSSHRoot,
|
|
EnableSSHSFTP: p.EnableSSHSFTP,
|
|
EnableSSHLocalPortForwarding: p.EnableSSHLocalPortForwarding,
|
|
EnableSSHRemotePortForwarding: p.EnableSSHRemotePortForwarding,
|
|
DisableSSHAuth: p.DisableSSHAuth,
|
|
SshJWTCacheTTL: p.SSHJWTCacheTTL,
|
|
}
|
|
_, err = cli.SetConfig(ctx, req)
|
|
return err
|
|
}
|
|
|
|
func (s *Settings) GetRestrictions(ctx context.Context) (Restrictions, error) {
|
|
cli, err := s.conn.Client()
|
|
if err != nil {
|
|
return Restrictions{}, err
|
|
}
|
|
active, err := cli.GetActiveProfile(ctx, &proto.GetActiveProfileRequest{})
|
|
if err != nil {
|
|
return Restrictions{}, fmt.Errorf("get active profile: %w", err)
|
|
}
|
|
cfgResp, err := cli.GetConfig(ctx, &proto.GetConfigRequest{
|
|
ProfileName: active.GetId(),
|
|
Username: active.GetUsername(),
|
|
})
|
|
if err != nil {
|
|
return Restrictions{}, err
|
|
}
|
|
featResp, err := cli.GetFeatures(ctx, &proto.GetFeaturesRequest{})
|
|
if err != nil {
|
|
return Restrictions{}, err
|
|
}
|
|
r := Restrictions{
|
|
Features: Features{
|
|
DisableProfiles: featResp.GetDisableProfiles(),
|
|
DisableNetworks: featResp.GetDisableNetworks(),
|
|
DisableUpdateSettings: featResp.GetDisableUpdateSettings(),
|
|
},
|
|
}
|
|
applyMDMRestrictions(&r.MDM, cfgResp)
|
|
r.MDM.DisableAdvancedView = featResp.GetDisableAdvancedView()
|
|
return r, nil
|
|
}
|
|
|
|
func applyMDMRestrictions(mdm *MDMFields, cfgResp *proto.GetConfigResponse) {
|
|
managed := cfgResp.GetMDMManagedFields()
|
|
if len(managed) == 0 {
|
|
return
|
|
}
|
|
set := make(map[string]struct{}, len(managed))
|
|
for _, k := range managed {
|
|
set[k] = struct{}{}
|
|
}
|
|
v := reflect.ValueOf(mdm).Elem()
|
|
t := v.Type()
|
|
for i := 0; i < t.NumField(); i++ {
|
|
if v.Field(i).Kind() != reflect.Bool {
|
|
continue
|
|
}
|
|
if t.Field(i).Name == "DisableAdvancedView" {
|
|
continue
|
|
}
|
|
if _, ok := set[t.Field(i).Tag.Get("json")]; ok {
|
|
v.Field(i).SetBool(true)
|
|
}
|
|
}
|
|
if _, ok := set["managementURL"]; ok {
|
|
mdm.ManagementURL = cfgResp.GetManagementUrl()
|
|
}
|
|
if _, ok := set["allowServerSSH"]; ok {
|
|
allowed := cfgResp.GetServerSSHAllowed()
|
|
mdm.AllowServerSSH = &allowed
|
|
}
|
|
}
|