Documentation
¶
Index ¶
- Constants
- Variables
- func CheckMDMConflicts(input ConfigInput, policy *mdm.Policy) error
- func ConfigToJSON(config *Config) (string, error)
- func DirectWriteOutConfig(path string, config *Config) error
- func GetLoginHint() string
- func InvokingUser() (*user.User, error)
- func IsPlainRoot() bool
- func IsValidProfileFilenameStem(id ID) bool
- func MDMConflicts(input ConfigInput, policy *mdm.Policy) []string
- func MirrorIsAuthoritative() bool
- func ParseServiceURL(serviceName, serviceURL string) (*url.URL, error)
- func SameServiceURL(a, b *url.URL) bool
- func SameServiceURLIncludingPath(a, b *url.URL) bool
- func StripCtrlChars(name string) string
- func ValidateBundleUploadURL(raw string) error
- func WriteOutConfig(path string, config *Config) error
- type ActiveProfileState
- type AmbiguityKind
- type Config
- func ConfigFromJSON(jsonStr string) (*Config, error)
- func CreateInMemoryConfig(input ConfigInput) (*Config, error)
- func DirectUpdateOrCreateConfig(input ConfigInput) (*Config, error)
- func GetExistingConfig(configPath string) (*Config, error)
- func ReadConfigOrDefault(configPath string) (*Config, error)
- func UpdateConfig(input ConfigInput) (*Config, error)
- func UpdateOldManagementURL(ctx context.Context, config *Config, configPath string) (*Config, error)
- func UpdateOrCreateConfig(input ConfigInput) (*Config, error)
- type ConfigInput
- type ErrAmbiguousHandle
- type ID
- type Prefs
- type Profile
- type ProfileManager
- func (pm *ProfileManager) GetActiveProfile() (*Profile, error)
- func (pm *ProfileManager) GetProfileState(id ID) (*ProfileState, error)
- func (pm *ProfileManager) RemoveProfileState(profileName string) error
- func (pm *ProfileManager) SetActiveProfileState(state *ProfileState) error
- func (pm *ProfileManager) SetProfileState(id ID, state *ProfileState) error
- func (pm *ProfileManager) SwitchProfile(id ID) error
- type ProfileState
- type ServiceManager
- func (s *ServiceManager) AddProfile(displayName, username string) (*Profile, error)
- func (s *ServiceManager) CopyDefaultProfileIfNotExists() (bool, error)
- func (s *ServiceManager) CreateDefaultProfile() error
- func (s *ServiceManager) DefaultProfilePath() string
- func (s *ServiceManager) GetActiveProfileState() (*ActiveProfileState, error)
- func (s *ServiceManager) GetStatePath() string
- func (s *ServiceManager) ListProfiles(username string) ([]Profile, error)
- func (s *ServiceManager) ProfilePrefs(id ID, username string) (*Prefs, error)
- func (s *ServiceManager) RemoveProfile(id ID, username string) error
- func (s *ServiceManager) RenameProfile(id ID, username string, newName string) error
- func (s *ServiceManager) ResolveProfile(handle, username string) (*Profile, error)
- func (s *ServiceManager) SetActiveProfileState(a *ActiveProfileState) error
- func (s *ServiceManager) SetActiveProfileStateToDefault() error
Constants ¶
const ( // DefaultManagementURL points to the NetBird's cloud management endpoint DefaultManagementURL = "https://api.netbird.io:443" // DefaultAdminURL points to NetBird's cloud management console DefaultAdminURL = "https://app.netbird.io:443" )
const (
DefaultProfileName = "default"
)
Variables ¶
var ( ErrProfileNotFound = errors.New("profile not found") ErrProfileAlreadyExists = errors.New("profile already exists") ErrNoActiveProfile = errors.New("no active profile set") )
var ( DefaultConfigPathDir = "" DefaultConfigPath = "" ActiveProfileStatePath = "" ErrorOldDefaultConfigNotFound = errors.New("old default config not found") )
var ConfigDirOverride string
var DefaultInterfaceBlacklist = []string{ iface.WgInterfaceDefault, "wt", "utun", "tun0", "zt", "ZeroTier", "wg", "ts", "Tailscale", "tailscale", "docker", "veth", "br-", "lo", }
var ErrMDMManagedFields = errors.New("fields managed by MDM cannot be modified")
ErrMDMManagedFields marks a config change rejected because it diverges from MDM-enforced values.
Functions ¶
func CheckMDMConflicts ¶ added in v0.79.0
func CheckMDMConflicts(input ConfigInput, policy *mdm.Policy) error
CheckMDMConflicts returns an ErrMDMManagedFields-wrapped error naming the conflicting keys, or nil when the input does not fight the policy.
func ConfigToJSON ¶ added in v0.61.1
ConfigToJSON serializes a Config struct to a JSON string. This is useful for exporting config to alternative storage mechanisms (e.g., UserDefaults on tvOS where file writes are blocked).
func DirectWriteOutConfig ¶ added in v0.61.1
DirectWriteOutConfig writes config directly without atomic temp file operations. Use this on platforms where atomic writes are blocked (e.g., tvOS sandbox).
func GetLoginHint ¶ added in v0.60.0
func GetLoginHint() string
GetLoginHint retrieves the email from the active profile to use as login_hint.
func InvokingUser ¶ added in v0.78.0
InvokingUser returns the user a CLI invocation acts for. Under sudo that is the user who ran sudo, not root: privileged flags force commands through sudo, and resolving profiles as root would silently switch the daemon to root's (default) profile instead of the invoking user's. An unmapped positive process UID uses its numeric kernel identity; root, sudo lookup failures, and unavailable platform identities still fail closed. Privilege decisions stay on the kernel credentials of the daemon connection, which SUDO_USER (a plain environment variable) can never influence; a forged value only selects a profile root could select anyway.
func IsPlainRoot ¶ added in v0.78.0
func IsPlainRoot() bool
IsPlainRoot reports that the process runs as root with no usable sudo context: there is no invoking user to act for, so per-user resolution falls back to root's own (empty) state. Callers use it to refuse ambiguous operations instead of silently acting on the wrong profile.
func IsValidProfileFilenameStem ¶ added in v0.73.0
IsValidProfileFilenameStem reports whether id is safe to use as the stem of a profile JSON filename.
func MDMConflicts ¶ added in v0.79.0
func MDMConflicts(input ConfigInput, policy *mdm.Policy) []string
MDMConflicts returns the names of MDM-managed keys whose requested value in the ConfigInput differs from the policy-enforced value; a field set to the enforced value is a no-op echo, not a conflict.
func MirrorIsAuthoritative ¶ added in v0.78.0
func MirrorIsAuthoritative() bool
MirrorIsAuthoritative reports whether the invoking user's local active-profile mirror can be trusted as the profile selector. It cannot under sudo (writes to it are skipped, so it goes stale) or as plain root (there is no invoking user, so it falls back to root's own default). Callers use it to decide whether to read the profile from the mirror or from the daemon.
func ParseServiceURL ¶ added in v0.76.0
parseURL parses and validates the URL for the named service. The URL must use the http or https scheme; if no port is present, ":443" is appended for https or ":80" for http. The serviceName parameter is used to contextualise error messages. On success returns the parsed *url.URL; on failure returns a non-nil error. ParseServiceURL normalises a service URL exactly as the config layer does when it stores one, so callers comparing a requested URL against a stored one do not have to reimplement the scheme validation and default-port handling.
func SameServiceURL ¶ added in v0.81.0
SameServiceURL reports whether two service URLs address the same endpoint: same scheme, same host compared case-insensitively as DNS names are, and same effective port, where an absent port means the scheme's default.
This is the one comparison every caller deciding "did this URL change?" must use. A string comparison answers a different question: "https://host", "https://host/" and "https://HOST:443" are one endpoint written three ways, and reading them as three values makes a client that restates its own management URL look like a client asking to be repointed. A nil operand matches only another nil one.
The path plays no part: a management URL is dialed, and only its host and port are. util.SameServiceURL is this comparison plus the path, which is what SameServiceURLIncludingPath needs and delegates to.
func SameServiceURLIncludingPath ¶ added in v0.81.0
SameServiceURLIncludingPath is SameServiceURL plus everything a URL carries past its endpoint: path, query, fragment and userinfo.
Use it for a URL that gets opened rather than dialed. The admin panel can live under a path, so two URLs with the same endpoint and different paths are two different panels — where for a URL the client dials over gRPC only the endpoint is ever used. Equivalent spellings still compare equal: a missing path and "/" are the same root, and so is a trailing slash on any path.
func StripCtrlChars ¶ added in v0.73.0
StripCtrlChars control characters from a name before printing it.
func ValidateBundleUploadURL ¶ added in v0.78.0
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 WriteOutConfig ¶
WriteOutConfig write put the prepared config to the given path
Types ¶
type ActiveProfileState ¶
type ActiveProfileState struct {
// ID is the on-disk filename stem of the active profile. The JSON tag stays
// as "name" for backwards compatibility with active state files written
// before the ID-based config files. Legacy values were profile names, which
// were also the legacy filename stems, so they still resolve to the correct
// file on disk.
ID ID `json:"name"`
Username string `json:"username"`
}
func (*ActiveProfileState) FilePath ¶
func (a *ActiveProfileState) FilePath() (string, error)
type AmbiguityKind ¶ added in v0.73.0
type AmbiguityKind int
AmbiguityKind describes which matcher produced the ambiguity, so callers can tailor the error message.
const ( AmbiguityKindIDPrefix AmbiguityKind = iota AmbiguityKindName )
type Config ¶
type Config struct {
// Name is the human-readable profile name shown in CLI/UI listings.
// It is independent of the profile's on-disk filename (which is the ID).
Name string
// Wireguard private key of local peer
PrivateKey string
ManagementURL *url.URL
AdminURL *url.URL
WgIface string
WgPort int
NetworkMonitor *bool
IFaceBlackList []string
DisableIPv6Discovery bool
RosenpassEnabled bool
RosenpassPermissive bool
ServerSSHAllowed *bool
RemoteJobsAllowed *bool
EnableSSHRoot *bool
EnableSSHSFTP *bool
EnableSSHLocalPortForwarding *bool
EnableSSHRemotePortForwarding *bool
DisableSSHAuth *bool
SSHJWTCacheTTL *int
DisableClientRoutes bool
DisableServerRoutes bool
DisableDNS bool
DisableFirewall bool
BlockLANAccess bool
BlockInbound bool
DisableIPv6 bool
SyncMessageVersion *int
DisableNotifications *bool
DNSLabels domain.List
// LocalMetricsEnabled enables the local Prometheus /metrics endpoint.
LocalMetricsEnabled bool
// LocalMetricsAddress is the listen address of the local /metrics endpoint.
LocalMetricsAddress string
// SSHKey is a private SSH key in a PEM format
SSHKey string
NATExternalIPs []string
// CustomDNSAddress sets the DNS resolver listening address in format ip:port
CustomDNSAddress string
// DisableAutoConnect determines whether the client should not start with the service
// it's set to false by default due to backwards compatibility
DisableAutoConnect bool
// DNSRouteInterval is the interval in which the DNS routes are updated
DNSRouteInterval time.Duration
// Path to a certificate used for mTLS authentication
ClientCertPath string
// Path to corresponding private key of ClientCertPath
ClientCertKeyPath string
ClientCertKeyPair *tls.Certificate `json:"-"`
// CertStoreDir is no longer read: certificate posture takes the directory from
// NB_CERT_STORE_DIR in the daemon's environment. The field is kept only to report a
// value left from an earlier version.
CertStoreDir string `json:",omitempty"`
// CertPKCS11URI is no longer read, as the URI may carry the token PIN: certificate
// posture takes it from NB_CERT_PKCS11_URI in the daemon's environment. The field is
// kept only to report a value left from an earlier version.
CertPKCS11URI string `json:",omitempty"`
// LazyConnection is the MDM-managed lazy-connection override ("on"/"off"/"").
// 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
// contains filtered or unexported fields
}
Config Configuration type
func ConfigFromJSON ¶ added in v0.61.1
ConfigFromJSON deserializes a JSON string to a Config struct. This is useful for restoring config from alternative storage mechanisms. After unmarshaling, defaults are applied to ensure the config is fully initialized.
The peer identity is deliberately none of its business, in either direction. It does not generate one: a read cannot hand back keys that nothing will write down (see ReadConfigOrDefault). Nor does it refuse a document that carries none, because a config legitimately has no identity between a logout and the next login — mobile logout clears both keys in place — and this is also the deserializer the iOS SDK copies a config through. Whoever goes on to connect is where an absent identity has to be answered.
func CreateInMemoryConfig ¶
func CreateInMemoryConfig(input ConfigInput) (*Config, error)
CreateInMemoryConfig generate a new config but do not write out it to the store. It carries an identity: callers connect with what they get back.
func DirectUpdateOrCreateConfig ¶ added in v0.61.1
func DirectUpdateOrCreateConfig(input ConfigInput) (*Config, error)
DirectUpdateOrCreateConfig is like UpdateOrCreateConfig but uses direct (non-atomic) writes. Use this on platforms where atomic writes are blocked (e.g., tvOS sandbox).
func GetExistingConfig ¶ added in v0.81.0
GetExistingConfig reads and returns the config if it exists on disk. Fails otherwise.
func ReadConfigOrDefault ¶ added in v0.81.0
ReadConfigOrDefault reads the profile config at configPath, or resolves the default config in memory when the file does not exist. It never writes, and never mints an identity — EnsureIdentity is where that happens, so the caller that provisions is also the one that persists.
func UpdateConfig ¶
func UpdateConfig(input ConfigInput) (*Config, error)
UpdateConfig update existing configuration according to input configuration and return with the configuration
func UpdateOldManagementURL ¶
func UpdateOldManagementURL(ctx context.Context, config *Config, configPath string) (*Config, error)
UpdateOldManagementURL checks whether client can switch to the new Management URL with port 443 and the management domain. If it can switch, then it updates the config and returns a new one. Otherwise, it returns the provided config. The check is performed only for the NetBird's managed version.
func UpdateOrCreateConfig ¶
func UpdateOrCreateConfig(input ConfigInput) (*Config, error)
UpdateOrCreateConfig reads existing config or generates a new one
func (*Config) ApplyMDMPolicy ¶ added in v0.79.0
ApplyMDMPolicy overlays the supplied MDM Policy on top of the current Config values and records it as Policy(). The overlay is not reversible: an empty Policy only clears the enforcement metadata, so resolve the base Config again (from disk or JSON) before applying a changed policy, the way the lifecycle owners do on every load.
func (*Config) EnsureIdentity ¶ added in v0.81.0
EnsureIdentity generates the keys that identify this peer if the config does not carry them yet, reporting whether it had to generate any.
It is deliberately not part of apply(). Everything apply() fills in is a default it can recompute on the next read, but a generated key is not: it has to be persisted, or the peer comes back with a different WireGuard identity and re-registers. Having apply() generate keys is what forced every read of a config to write it back — so identity provisioning is its own step now, and the callers that perform it write the result out explicitly.
func (*Config) Policy ¶ added in v0.73.0
Policy returns the MDM policy applied to this Config. Returns a non-nil empty Policy when MDM enforcement is inactive; callers can always invoke HasKey / ManagedKeys / IsEmpty without a nil check.
func (*Config) WouldChange ¶ added in v0.81.0
func (config *Config) WouldChange(input ConfigInput) (bool, error)
WouldChange reports whether applying input would modify any field the config persists, leaving the receiver untouched. It is the dry-run half of UpdateConfig and reuses the very same diff logic (Config.apply), so a caller asking "is this a settings change?" cannot drift from what an actual update would do, nor go stale when a new field is added.
A redacted pre-shared key is collapsed to "unset" exactly as UpdateOrCreateConfig does, so a UI that round-trips the mask is not read as a request for a new key.
A nil receiver means the profile holds no config yet, so the baseline is the config the daemon would create for it: input values matching those defaults change nothing, anything else does.
type ConfigInput ¶
type ConfigInput struct {
ManagementURL string
AdminURL string
ConfigPath string
StateFilePath string
ServerSSHAllowed *bool
RemoteJobsAllowed *bool
EnableSSHRoot *bool
EnableSSHSFTP *bool
EnableSSHLocalPortForwarding *bool
EnableSSHRemotePortForwarding *bool
DisableSSHAuth *bool
SSHJWTCacheTTL *int
NATExternalIPs []string
CustomDNSAddress []byte
RosenpassEnabled *bool
RosenpassPermissive *bool
InterfaceName *string
WireguardPort *int
NetworkMonitor *bool
DisableAutoConnect *bool
ExtraIFaceBlackList []string
DNSRouteInterval *time.Duration
ClientCertPath string
ClientCertKeyPath string
DisableClientRoutes *bool
DisableServerRoutes *bool
DisableDNS *bool
DisableFirewall *bool
BlockLANAccess *bool
BlockInbound *bool
DisableIPv6 *bool
SyncMessageVersion *int
DisableNotifications *bool
DNSLabels domain.List
MTU *uint16
LocalMetricsEnabled *bool
LocalMetricsAddress *string
}
ConfigInput carries configuration changes to the client
type ErrAmbiguousHandle ¶ added in v0.73.0
type ErrAmbiguousHandle struct {
Handle string
Candidates []Profile
Kind AmbiguityKind
}
ErrAmbiguousHandle is returned when a profile handle (ID prefix or name) matches more than one profile. Callers can render Candidates to help the user disambiguate.
func (*ErrAmbiguousHandle) Error ¶ added in v0.73.0
func (e *ErrAmbiguousHandle) Error() string
type ID ¶ added in v0.73.0
type ID string
type Prefs ¶ added in v0.77.1
type Prefs struct {
// contains filtered or unexported fields
}
Prefs is a namespaced per-profile preference store backed by a single JSON file next to the profile config; it is deleted together with the profile.
func (*Prefs) Get ¶ added in v0.77.1
Get unmarshals the namespace section into v and reports whether it exists.
type Profile ¶
type Profile struct {
// ID is the on-disk filename stem (without .json). For new profiles
// it is a 32-char hex string; legacy profiles created before the
// ID-keyed layout keep their original name as their ID. The reserved
// value "default" identifies the special default profile.
ID ID
// Name is the human-readable display name. Falls back to ID when the
// underlying JSON has no "name" field set.
Name string
// Path is the absolute path to the profile JSON. Populated by the
// loader so callers do not have to reconstruct it from ID + dir.
Path string
IsActive bool
}
type ProfileManager ¶
type ProfileManager struct {
// contains filtered or unexported fields
}
func NewProfileManager ¶
func NewProfileManager() *ProfileManager
func (*ProfileManager) GetActiveProfile ¶
func (pm *ProfileManager) GetActiveProfile() (*Profile, error)
GetActiveProfile returns the active profile as recorded in the local user state file. Only ID is populated.
func (*ProfileManager) GetProfileState ¶
func (pm *ProfileManager) GetProfileState(id ID) (*ProfileState, error)
GetProfileState reads the per-profile state file keyed by profile ID. The state file lives in the user's config directory. Legacy state files keyed by the old profile name remain readable.
func (*ProfileManager) RemoveProfileState ¶ added in v0.75.0
func (pm *ProfileManager) RemoveProfileState(profileName string) error
RemoveProfileState deletes the per-profile state file (which holds the account email used for the SSO login hint and the UI display). Called after profile removal; logout keeps the file so the next login can pass the email as the login_hint. The state file only stores the email, so deleting it is equivalent to clearing it; the next SSO login recreates it. A missing file is not an error.
func (*ProfileManager) SetActiveProfileState ¶
func (pm *ProfileManager) SetActiveProfileState(state *ProfileState) error
SetActiveProfileState writes the state file of whichever profile is active at call time. Use SetProfileState when the target profile is known.
func (*ProfileManager) SetProfileState ¶ added in v0.76.2
func (pm *ProfileManager) SetProfileState(id ID, state *ProfileState) error
SetProfileState writes the state file of the profile identified by id. Prefer it over SetActiveProfileState whenever the caller knows which profile the data belongs to: an SSO login spans seconds of user interaction, and the active profile can change during it, which would file the account email under whichever profile happened to be active when the flow returned.
func (*ProfileManager) SwitchProfile ¶
func (pm *ProfileManager) SwitchProfile(id ID) error
SwitchProfile records the given profile ID as active in the local user state file.
type ProfileState ¶
type ProfileState struct {
Email string `json:"email"`
}
type ServiceManager ¶
type ServiceManager struct {
// contains filtered or unexported fields
}
func NewServiceManager ¶ added in v0.53.0
func NewServiceManager(defaultConfigPath string) *ServiceManager
func NewServiceManagerWithProfilesDir ¶ added in v0.61.0
func NewServiceManagerWithProfilesDir(defaultConfigPath string, profilesDir string) *ServiceManager
NewServiceManagerWithProfilesDir creates a ServiceManager with a specific profiles directory This allows setting the profiles directory without modifying the global ConfigDirOverride
func (*ServiceManager) AddProfile ¶
func (s *ServiceManager) AddProfile(displayName, username string) (*Profile, error)
AddProfile creates a new profile with a generated ID. The user-supplied displayName is stored inside the JSON's name field, the on-disk filename uses the generated ID.
The returned Profile carries the freshly-generated ID so callers can show it to the user (and so the gRPC AddProfileResponse can include it).
func (*ServiceManager) CopyDefaultProfileIfNotExists ¶
func (s *ServiceManager) CopyDefaultProfileIfNotExists() (bool, error)
func (*ServiceManager) CreateDefaultProfile ¶
func (s *ServiceManager) CreateDefaultProfile() error
func (*ServiceManager) DefaultProfilePath ¶
func (s *ServiceManager) DefaultProfilePath() string
func (*ServiceManager) GetActiveProfileState ¶
func (s *ServiceManager) GetActiveProfileState() (*ActiveProfileState, error)
func (*ServiceManager) GetStatePath ¶
func (s *ServiceManager) GetStatePath() string
GetStatePath returns the path to the state file based on the operating system It returns an empty string if the path cannot be determined.
func (*ServiceManager) ListProfiles ¶
func (s *ServiceManager) ListProfiles(username string) ([]Profile, error)
ListProfiles returns every profile for the given user, including the default profile, with IsActive flags set.
func (*ServiceManager) ProfilePrefs ¶ added in v0.77.1
func (s *ServiceManager) ProfilePrefs(id ID, username string) (*Prefs, error)
ProfilePrefs returns the preference store of the profile identified by id.
func (*ServiceManager) RemoveProfile ¶
func (s *ServiceManager) RemoveProfile(id ID, username string) error
RemoveProfile deletes the profile identified by id. Callers must have already resolved any user-supplied handle to a concrete ID via ResolveProfile.
func (*ServiceManager) RenameProfile ¶ added in v0.73.0
func (s *ServiceManager) RenameProfile(id ID, username string, newName string) error
RenameProfile changes a profile's display name. It rewrites the whole profile file, not just the name: the config is read through the normalizing reader, so apply()'s resolved values — the optional booleans, the interface blacklist, the DNS route interval — are persisted along with the new name.
That is deliberate. A write that skipped apply() is what left profiles on disk carrying null where a value was meant, and made a diff of the config compare presence instead of value. Two consequences worth knowing: the platform-dependent defaults resolved here are the renaming host's (ServerSSHAllowed and the network monitor differ per OS), and a profile whose stored name does not survive sanitizeDisplayName now fails to rename rather than being rewritten — though apply() rejects such a profile on every other read too, so it was already unusable.
func (*ServiceManager) ResolveProfile ¶ added in v0.73.0
func (s *ServiceManager) ResolveProfile(handle, username string) (*Profile, error)
ResolveProfile turns a user-supplied handle into a Profile. Resolution precedence is: exact ID match, then unique exact name, then unique ID prefix. Ambiguous matches return *ErrAmbiguousHandle so callers can surface the candidates.
func (*ServiceManager) SetActiveProfileState ¶
func (s *ServiceManager) SetActiveProfileState(a *ActiveProfileState) error
func (*ServiceManager) SetActiveProfileStateToDefault ¶
func (s *ServiceManager) SetActiveProfileStateToDefault() error