Merge branch 'main' into fix/ice-handshake

This commit is contained in:
Zoltán Papp
2025-08-14 09:58:25 +02:00
23 changed files with 379 additions and 95 deletions

View File

@@ -25,8 +25,7 @@ jobs:
release: "14.2"
prepare: |
pkg install -y curl pkgconf xorg
LATEST_VERSION=$(curl -s https://go.dev/VERSION?m=text|head -n 1)
GO_TARBALL="$LATEST_VERSION.freebsd-amd64.tar.gz"
GO_TARBALL="go1.23.12.freebsd-amd64.tar.gz"
GO_URL="https://go.dev/dl/$GO_TARBALL"
curl -vLO "$GO_URL"
tar -C /usr/local -vxzf "$GO_TARBALL"

View File

@@ -33,7 +33,7 @@ var (
var debugCmd = &cobra.Command{
Use: "debug",
Short: "Debugging commands",
Long: "Provides commands for debugging and logging control within the NetBird daemon.",
Long: "Commands for debugging and logging within the NetBird daemon.",
}
var debugBundleCmd = &cobra.Command{

View File

@@ -14,7 +14,8 @@ import (
var downCmd = &cobra.Command{
Use: "down",
Short: "down netbird connections",
Short: "Disconnect from the NetBird network",
Long: "Disconnect the NetBird client from the network and management service. This will terminate all active connections with the remote peers.",
RunE: func(cmd *cobra.Command, args []string) error {
SetFlagsFromEnvVars(rootCmd)

View File

@@ -31,7 +31,8 @@ func init() {
var loginCmd = &cobra.Command{
Use: "login",
Short: "login to the NetBird Management Service (first run)",
Short: "Log in to the NetBird network",
Long: "Log in to the NetBird network using a setup key or SSO",
RunE: func(cmd *cobra.Command, args []string) error {
if err := setEnvAndFlags(cmd); err != nil {
return fmt.Errorf("set env and flags: %v", err)

View File

@@ -14,7 +14,8 @@ import (
var logoutCmd = &cobra.Command{
Use: "deregister",
Aliases: []string{"logout"},
Short: "deregister from the NetBird Management Service and delete peer",
Short: "Deregister from the NetBird management service and delete this peer",
Long: "This command will deregister the current peer from the NetBird management service and all associated configuration. Use with caution as this will remove the peer from the network.",
RunE: func(cmd *cobra.Command, args []string) error {
SetFlagsFromEnvVars(rootCmd)

View File

@@ -15,7 +15,7 @@ var appendFlag bool
var networksCMD = &cobra.Command{
Use: "networks",
Aliases: []string{"routes"},
Short: "Manage networks",
Short: "Manage connections to NetBird Networks and Resources",
Long: `Commands to list, select, or deselect networks. Replaces the "routes" command.`,
}

View File

@@ -16,13 +16,13 @@ import (
var profileCmd = &cobra.Command{
Use: "profile",
Short: "manage NetBird profiles",
Long: `Manage NetBird profiles, allowing you to list, switch, and remove profiles.`,
Short: "Manage NetBird client profiles",
Long: `Commands to list, add, remove, and switch profiles. Profiles allow you to maintain different accounts in one client app.`,
}
var profileListCmd = &cobra.Command{
Use: "list",
Short: "list all profiles",
Short: "List all profiles",
Long: `List all available profiles in the NetBird client.`,
Aliases: []string{"ls"},
RunE: listProfilesFunc,
@@ -30,7 +30,7 @@ var profileListCmd = &cobra.Command{
var profileAddCmd = &cobra.Command{
Use: "add <profile_name>",
Short: "add a new profile",
Short: "Add a new profile",
Long: `Add a new profile to the NetBird client. The profile name must be unique.`,
Args: cobra.ExactArgs(1),
RunE: addProfileFunc,
@@ -38,16 +38,16 @@ var profileAddCmd = &cobra.Command{
var profileRemoveCmd = &cobra.Command{
Use: "remove <profile_name>",
Short: "remove a profile",
Long: `Remove a profile from the NetBird client. The profile must not be active.`,
Short: "Remove a profile",
Long: `Remove a profile from the NetBird client. The profile must not be inactive.`,
Args: cobra.ExactArgs(1),
RunE: removeProfileFunc,
}
var profileSelectCmd = &cobra.Command{
Use: "select <profile_name>",
Short: "select a profile",
Long: `Select a profile to be the active profile in the NetBird client. The profile must exist.`,
Short: "Select a profile",
Long: `Make the specified profile active. This will switch the client to use the selected profile's configuration.`,
Args: cobra.ExactArgs(1),
RunE: selectProfileFunc,
}

View File

@@ -73,6 +73,7 @@ var (
dnsRouteInterval time.Duration
lazyConnEnabled bool
profilesDisabled bool
updateSettingsDisabled bool
rootCmd = &cobra.Command{
Use: "netbird",

View File

@@ -19,7 +19,7 @@ import (
var serviceCmd = &cobra.Command{
Use: "service",
Short: "manages NetBird service",
Short: "Manage the NetBird daemon service",
}
var (
@@ -42,7 +42,8 @@ func init() {
}
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd, svcStatusCmd, installCmd, uninstallCmd, reconfigureCmd)
serviceCmd.PersistentFlags().BoolVar(&profilesDisabled, "disable-profiles", false, "Disables profiles feature. If enabled, the client will not be able to change or edit any profile.")
serviceCmd.PersistentFlags().BoolVar(&profilesDisabled, "disable-profiles", false, "Disables profiles feature. If enabled, the client will not be able to change or edit any profile. To persist this setting, use: netbird service install --disable-profiles")
serviceCmd.PersistentFlags().BoolVar(&updateSettingsDisabled, "disable-update-settings", false, "Disables update settings feature. If enabled, the client will not be able to change or edit any settings. To persist this setting, use: netbird service install --disable-update-settings")
rootCmd.PersistentFlags().StringVarP(&serviceName, "service", "s", defaultServiceName, "Netbird system service name")
serviceEnvDesc := `Sets extra environment variables for the service. ` +

View File

@@ -61,7 +61,7 @@ func (p *program) Start(svc service.Service) error {
}
}
serverInstance := server.New(p.ctx, util.FindFirstLogPath(logFiles), configPath, profilesDisabled)
serverInstance := server.New(p.ctx, util.FindFirstLogPath(logFiles), configPath, profilesDisabled, updateSettingsDisabled)
if err := serverInstance.Start(); err != nil {
log.Fatalf("failed to start daemon: %v", err)
}

View File

@@ -49,6 +49,14 @@ func buildServiceArguments() []string {
args = append(args, "--log-file", logFile)
}
if profilesDisabled {
args = append(args, "--disable-profiles")
}
if updateSettingsDisabled {
args = append(args, "--disable-update-settings")
}
return args
}
@@ -99,7 +107,7 @@ func createServiceConfigForInstall() (*service.Config, error) {
var installCmd = &cobra.Command{
Use: "install",
Short: "installs NetBird service",
Short: "Install NetBird service",
RunE: func(cmd *cobra.Command, args []string) error {
if err := setupServiceCommand(cmd); err != nil {
return err

View File

@@ -40,7 +40,7 @@ var sshCmd = &cobra.Command{
return nil
},
Short: "connect to a remote SSH server",
Short: "Connect to a remote SSH server",
RunE: func(cmd *cobra.Command, args []string) error {
SetFlagsFromEnvVars(rootCmd)
SetFlagsFromEnvVars(cmd)

View File

@@ -32,7 +32,8 @@ var (
var statusCmd = &cobra.Command{
Use: "status",
Short: "status of the Netbird Service",
Short: "Display NetBird client status",
Long: "Display the current status of the NetBird client, including connection status, peer information, and network details.",
RunE: statusFunc,
}

View File

@@ -27,8 +27,8 @@ import (
clientProto "github.com/netbirdio/netbird/client/proto"
client "github.com/netbirdio/netbird/client/server"
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
mgmt "github.com/netbirdio/netbird/management/server"
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
sigProto "github.com/netbirdio/netbird/shared/signal/proto"
sig "github.com/netbirdio/netbird/signal/server"
)
@@ -136,7 +136,7 @@ func startClientDaemon(
s := grpc.NewServer()
server := client.New(ctx,
"", "", false)
"", "", false, false)
if err := server.Start(); err != nil {
t.Fatal(err)
}

View File

@@ -53,7 +53,8 @@ var (
upCmd = &cobra.Command{
Use: "up",
Short: "install, login and start NetBird client",
Short: "Connect to the NetBird network",
Long: "Connect to the NetBird network using the provided setup key or SSO auth. This command will bring up the WireGuard interface, connect to the management server, and establish peer-to-peer connections with other peers in the network if required.",
RunE: upFunc,
}
)

View File

@@ -9,7 +9,7 @@ import (
var (
versionCmd = &cobra.Command{
Use: "version",
Short: "prints NetBird version",
Short: "Print the NetBird's client application version",
Run: func(cmd *cobra.Command, args []string) {
cmd.SetOut(cmd.OutOrStdout())
cmd.Println(version.NetbirdVersion())

View File

@@ -4430,6 +4430,94 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{66}
}
type GetFeaturesRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetFeaturesRequest) Reset() {
*x = GetFeaturesRequest{}
mi := &file_daemon_proto_msgTypes[67]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetFeaturesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetFeaturesRequest) ProtoMessage() {}
func (x *GetFeaturesRequest) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[67]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetFeaturesRequest.ProtoReflect.Descriptor instead.
func (*GetFeaturesRequest) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{67}
}
type GetFeaturesResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
DisableProfiles bool `protobuf:"varint,1,opt,name=disable_profiles,json=disableProfiles,proto3" json:"disable_profiles,omitempty"`
DisableUpdateSettings bool `protobuf:"varint,2,opt,name=disable_update_settings,json=disableUpdateSettings,proto3" json:"disable_update_settings,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetFeaturesResponse) Reset() {
*x = GetFeaturesResponse{}
mi := &file_daemon_proto_msgTypes[68]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetFeaturesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetFeaturesResponse) ProtoMessage() {}
func (x *GetFeaturesResponse) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[68]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetFeaturesResponse.ProtoReflect.Descriptor instead.
func (*GetFeaturesResponse) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{68}
}
func (x *GetFeaturesResponse) GetDisableProfiles() bool {
if x != nil {
return x.DisableProfiles
}
return false
}
func (x *GetFeaturesResponse) GetDisableUpdateSettings() bool {
if x != nil {
return x.DisableUpdateSettings
}
return false
}
type PortInfo_Range struct {
state protoimpl.MessageState `protogen:"open.v1"`
Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"`
@@ -4440,7 +4528,7 @@ type PortInfo_Range struct {
func (x *PortInfo_Range) Reset() {
*x = PortInfo_Range{}
mi := &file_daemon_proto_msgTypes[68]
mi := &file_daemon_proto_msgTypes[70]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4452,7 +4540,7 @@ func (x *PortInfo_Range) String() string {
func (*PortInfo_Range) ProtoMessage() {}
func (x *PortInfo_Range) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[68]
mi := &file_daemon_proto_msgTypes[70]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4872,7 +4960,11 @@ const file_daemon_proto_rawDesc = "" +
"\busername\x18\x02 \x01(\tH\x01R\busername\x88\x01\x01B\x0e\n" +
"\f_profileNameB\v\n" +
"\t_username\"\x10\n" +
"\x0eLogoutResponse*b\n" +
"\x0eLogoutResponse\"\x14\n" +
"\x12GetFeaturesRequest\"x\n" +
"\x13GetFeaturesResponse\x12)\n" +
"\x10disable_profiles\x18\x01 \x01(\bR\x0fdisableProfiles\x126\n" +
"\x17disable_update_settings\x18\x02 \x01(\bR\x15disableUpdateSettings*b\n" +
"\bLogLevel\x12\v\n" +
"\aUNKNOWN\x10\x00\x12\t\n" +
"\x05PANIC\x10\x01\x12\t\n" +
@@ -4881,7 +4973,7 @@ const file_daemon_proto_rawDesc = "" +
"\x04WARN\x10\x04\x12\b\n" +
"\x04INFO\x10\x05\x12\t\n" +
"\x05DEBUG\x10\x06\x12\t\n" +
"\x05TRACE\x10\a2\xc5\x0f\n" +
"\x05TRACE\x10\a2\x8f\x10\n" +
"\rDaemonService\x126\n" +
"\x05Login\x12\x14.daemon.LoginRequest\x1a\x15.daemon.LoginResponse\"\x00\x12K\n" +
"\fWaitSSOLogin\x12\x1b.daemon.WaitSSOLoginRequest\x1a\x1c.daemon.WaitSSOLoginResponse\"\x00\x12-\n" +
@@ -4912,7 +5004,8 @@ const file_daemon_proto_rawDesc = "" +
"\rRemoveProfile\x12\x1c.daemon.RemoveProfileRequest\x1a\x1d.daemon.RemoveProfileResponse\"\x00\x12K\n" +
"\fListProfiles\x12\x1b.daemon.ListProfilesRequest\x1a\x1c.daemon.ListProfilesResponse\"\x00\x12W\n" +
"\x10GetActiveProfile\x12\x1f.daemon.GetActiveProfileRequest\x1a .daemon.GetActiveProfileResponse\"\x00\x129\n" +
"\x06Logout\x12\x15.daemon.LogoutRequest\x1a\x16.daemon.LogoutResponse\"\x00B\bZ\x06/protob\x06proto3"
"\x06Logout\x12\x15.daemon.LogoutRequest\x1a\x16.daemon.LogoutResponse\"\x00\x12H\n" +
"\vGetFeatures\x12\x1a.daemon.GetFeaturesRequest\x1a\x1b.daemon.GetFeaturesResponse\"\x00B\bZ\x06/protob\x06proto3"
var (
file_daemon_proto_rawDescOnce sync.Once
@@ -4927,7 +5020,7 @@ func file_daemon_proto_rawDescGZIP() []byte {
}
var file_daemon_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 70)
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 72)
var file_daemon_proto_goTypes = []any{
(LogLevel)(0), // 0: daemon.LogLevel
(SystemEvent_Severity)(0), // 1: daemon.SystemEvent.Severity
@@ -4999,18 +5092,20 @@ var file_daemon_proto_goTypes = []any{
(*GetActiveProfileResponse)(nil), // 67: daemon.GetActiveProfileResponse
(*LogoutRequest)(nil), // 68: daemon.LogoutRequest
(*LogoutResponse)(nil), // 69: daemon.LogoutResponse
nil, // 70: daemon.Network.ResolvedIPsEntry
(*PortInfo_Range)(nil), // 71: daemon.PortInfo.Range
nil, // 72: daemon.SystemEvent.MetadataEntry
(*durationpb.Duration)(nil), // 73: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 74: google.protobuf.Timestamp
(*GetFeaturesRequest)(nil), // 70: daemon.GetFeaturesRequest
(*GetFeaturesResponse)(nil), // 71: daemon.GetFeaturesResponse
nil, // 72: daemon.Network.ResolvedIPsEntry
(*PortInfo_Range)(nil), // 73: daemon.PortInfo.Range
nil, // 74: daemon.SystemEvent.MetadataEntry
(*durationpb.Duration)(nil), // 75: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 76: google.protobuf.Timestamp
}
var file_daemon_proto_depIdxs = []int32{
73, // 0: daemon.LoginRequest.dnsRouteInterval:type_name -> google.protobuf.Duration
75, // 0: daemon.LoginRequest.dnsRouteInterval:type_name -> google.protobuf.Duration
22, // 1: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus
74, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
74, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
73, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration
76, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
76, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
75, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration
19, // 5: daemon.FullStatus.managementState:type_name -> daemon.ManagementState
18, // 6: daemon.FullStatus.signalState:type_name -> daemon.SignalState
17, // 7: daemon.FullStatus.localPeerState:type_name -> daemon.LocalPeerState
@@ -5019,8 +5114,8 @@ var file_daemon_proto_depIdxs = []int32{
21, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState
52, // 11: daemon.FullStatus.events:type_name -> daemon.SystemEvent
28, // 12: daemon.ListNetworksResponse.routes:type_name -> daemon.Network
70, // 13: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry
71, // 14: daemon.PortInfo.range:type_name -> daemon.PortInfo.Range
72, // 13: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry
73, // 14: daemon.PortInfo.range:type_name -> daemon.PortInfo.Range
29, // 15: daemon.ForwardingRule.destinationPort:type_name -> daemon.PortInfo
29, // 16: daemon.ForwardingRule.translatedPort:type_name -> daemon.PortInfo
30, // 17: daemon.ForwardingRulesResponse.rules:type_name -> daemon.ForwardingRule
@@ -5031,10 +5126,10 @@ var file_daemon_proto_depIdxs = []int32{
49, // 22: daemon.TracePacketResponse.stages:type_name -> daemon.TraceStage
1, // 23: daemon.SystemEvent.severity:type_name -> daemon.SystemEvent.Severity
2, // 24: daemon.SystemEvent.category:type_name -> daemon.SystemEvent.Category
74, // 25: daemon.SystemEvent.timestamp:type_name -> google.protobuf.Timestamp
72, // 26: daemon.SystemEvent.metadata:type_name -> daemon.SystemEvent.MetadataEntry
76, // 25: daemon.SystemEvent.timestamp:type_name -> google.protobuf.Timestamp
74, // 26: daemon.SystemEvent.metadata:type_name -> daemon.SystemEvent.MetadataEntry
52, // 27: daemon.GetEventsResponse.events:type_name -> daemon.SystemEvent
73, // 28: daemon.SetConfigRequest.dnsRouteInterval:type_name -> google.protobuf.Duration
75, // 28: daemon.SetConfigRequest.dnsRouteInterval:type_name -> google.protobuf.Duration
65, // 29: daemon.ListProfilesResponse.profiles:type_name -> daemon.Profile
27, // 30: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList
4, // 31: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
@@ -5064,35 +5159,37 @@ var file_daemon_proto_depIdxs = []int32{
63, // 55: daemon.DaemonService.ListProfiles:input_type -> daemon.ListProfilesRequest
66, // 56: daemon.DaemonService.GetActiveProfile:input_type -> daemon.GetActiveProfileRequest
68, // 57: daemon.DaemonService.Logout:input_type -> daemon.LogoutRequest
5, // 58: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
7, // 59: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
9, // 60: daemon.DaemonService.Up:output_type -> daemon.UpResponse
11, // 61: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
13, // 62: daemon.DaemonService.Down:output_type -> daemon.DownResponse
15, // 63: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
24, // 64: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse
26, // 65: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse
26, // 66: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse
31, // 67: daemon.DaemonService.ForwardingRules:output_type -> daemon.ForwardingRulesResponse
33, // 68: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
35, // 69: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
37, // 70: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
40, // 71: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse
42, // 72: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse
44, // 73: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse
46, // 74: daemon.DaemonService.SetSyncResponsePersistence:output_type -> daemon.SetSyncResponsePersistenceResponse
50, // 75: daemon.DaemonService.TracePacket:output_type -> daemon.TracePacketResponse
52, // 76: daemon.DaemonService.SubscribeEvents:output_type -> daemon.SystemEvent
54, // 77: daemon.DaemonService.GetEvents:output_type -> daemon.GetEventsResponse
56, // 78: daemon.DaemonService.SwitchProfile:output_type -> daemon.SwitchProfileResponse
58, // 79: daemon.DaemonService.SetConfig:output_type -> daemon.SetConfigResponse
60, // 80: daemon.DaemonService.AddProfile:output_type -> daemon.AddProfileResponse
62, // 81: daemon.DaemonService.RemoveProfile:output_type -> daemon.RemoveProfileResponse
64, // 82: daemon.DaemonService.ListProfiles:output_type -> daemon.ListProfilesResponse
67, // 83: daemon.DaemonService.GetActiveProfile:output_type -> daemon.GetActiveProfileResponse
69, // 84: daemon.DaemonService.Logout:output_type -> daemon.LogoutResponse
58, // [58:85] is the sub-list for method output_type
31, // [31:58] is the sub-list for method input_type
70, // 58: daemon.DaemonService.GetFeatures:input_type -> daemon.GetFeaturesRequest
5, // 59: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
7, // 60: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
9, // 61: daemon.DaemonService.Up:output_type -> daemon.UpResponse
11, // 62: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
13, // 63: daemon.DaemonService.Down:output_type -> daemon.DownResponse
15, // 64: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
24, // 65: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse
26, // 66: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse
26, // 67: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse
31, // 68: daemon.DaemonService.ForwardingRules:output_type -> daemon.ForwardingRulesResponse
33, // 69: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
35, // 70: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
37, // 71: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
40, // 72: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse
42, // 73: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse
44, // 74: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse
46, // 75: daemon.DaemonService.SetSyncResponsePersistence:output_type -> daemon.SetSyncResponsePersistenceResponse
50, // 76: daemon.DaemonService.TracePacket:output_type -> daemon.TracePacketResponse
52, // 77: daemon.DaemonService.SubscribeEvents:output_type -> daemon.SystemEvent
54, // 78: daemon.DaemonService.GetEvents:output_type -> daemon.GetEventsResponse
56, // 79: daemon.DaemonService.SwitchProfile:output_type -> daemon.SwitchProfileResponse
58, // 80: daemon.DaemonService.SetConfig:output_type -> daemon.SetConfigResponse
60, // 81: daemon.DaemonService.AddProfile:output_type -> daemon.AddProfileResponse
62, // 82: daemon.DaemonService.RemoveProfile:output_type -> daemon.RemoveProfileResponse
64, // 83: daemon.DaemonService.ListProfiles:output_type -> daemon.ListProfilesResponse
67, // 84: daemon.DaemonService.GetActiveProfile:output_type -> daemon.GetActiveProfileResponse
69, // 85: daemon.DaemonService.Logout:output_type -> daemon.LogoutResponse
71, // 86: daemon.DaemonService.GetFeatures:output_type -> daemon.GetFeaturesResponse
59, // [59:87] is the sub-list for method output_type
31, // [31:59] is the sub-list for method input_type
31, // [31:31] is the sub-list for extension type_name
31, // [31:31] is the sub-list for extension extendee
0, // [0:31] is the sub-list for field type_name
@@ -5120,7 +5217,7 @@ func file_daemon_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_proto_rawDesc), len(file_daemon_proto_rawDesc)),
NumEnums: 3,
NumMessages: 70,
NumMessages: 72,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -82,6 +82,8 @@ service DaemonService {
// Logout disconnects from the network and deletes the peer from the management server
rpc Logout(LogoutRequest) returns (LogoutResponse) {}
rpc GetFeatures(GetFeaturesRequest) returns (GetFeaturesResponse) {}
}
@@ -624,4 +626,11 @@ message LogoutRequest {
optional string username = 2;
}
message LogoutResponse {}
message LogoutResponse {}
message GetFeaturesRequest{}
message GetFeaturesResponse{
bool disable_profiles = 1;
bool disable_update_settings = 2;
}

View File

@@ -63,6 +63,7 @@ type DaemonServiceClient interface {
GetActiveProfile(ctx context.Context, in *GetActiveProfileRequest, opts ...grpc.CallOption) (*GetActiveProfileResponse, error)
// Logout disconnects from the network and deletes the peer from the management server
Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error)
GetFeatures(ctx context.Context, in *GetFeaturesRequest, opts ...grpc.CallOption) (*GetFeaturesResponse, error)
}
type daemonServiceClient struct {
@@ -339,6 +340,15 @@ func (c *daemonServiceClient) Logout(ctx context.Context, in *LogoutRequest, opt
return out, nil
}
func (c *daemonServiceClient) GetFeatures(ctx context.Context, in *GetFeaturesRequest, opts ...grpc.CallOption) (*GetFeaturesResponse, error) {
out := new(GetFeaturesResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/GetFeatures", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DaemonServiceServer is the server API for DaemonService service.
// All implementations must embed UnimplementedDaemonServiceServer
// for forward compatibility
@@ -388,6 +398,7 @@ type DaemonServiceServer interface {
GetActiveProfile(context.Context, *GetActiveProfileRequest) (*GetActiveProfileResponse, error)
// Logout disconnects from the network and deletes the peer from the management server
Logout(context.Context, *LogoutRequest) (*LogoutResponse, error)
GetFeatures(context.Context, *GetFeaturesRequest) (*GetFeaturesResponse, error)
mustEmbedUnimplementedDaemonServiceServer()
}
@@ -476,6 +487,9 @@ func (UnimplementedDaemonServiceServer) GetActiveProfile(context.Context, *GetAc
func (UnimplementedDaemonServiceServer) Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
}
func (UnimplementedDaemonServiceServer) GetFeatures(context.Context, *GetFeaturesRequest) (*GetFeaturesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetFeatures not implemented")
}
func (UnimplementedDaemonServiceServer) mustEmbedUnimplementedDaemonServiceServer() {}
// UnsafeDaemonServiceServer may be embedded to opt out of forward compatibility for this service.
@@ -978,6 +992,24 @@ func _DaemonService_Logout_Handler(srv interface{}, ctx context.Context, dec fun
return interceptor(ctx, in, info, handler)
}
func _DaemonService_GetFeatures_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetFeaturesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DaemonServiceServer).GetFeatures(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/daemon.DaemonService/GetFeatures",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).GetFeatures(ctx, req.(*GetFeaturesRequest))
}
return interceptor(ctx, in, info, handler)
}
// DaemonService_ServiceDesc is the grpc.ServiceDesc for DaemonService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -1089,6 +1121,10 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{
MethodName: "Logout",
Handler: _DaemonService_Logout_Handler,
},
{
MethodName: "GetFeatures",
Handler: _DaemonService_GetFeatures_Handler,
},
},
Streams: []grpc.StreamDesc{
{

View File

@@ -46,8 +46,9 @@ const (
defaultMaxRetryTime = 14 * 24 * time.Hour
defaultRetryMultiplier = 1.7
errRestoreResidualState = "failed to restore residual state: %v"
errProfilesDisabled = "profiles are disabled, you cannot use this feature without profiles enabled"
errRestoreResidualState = "failed to restore residual state: %v"
errProfilesDisabled = "profiles are disabled, you cannot use this feature without profiles enabled"
errUpdateSettingsDisabled = "update settings are disabled, you cannot use this feature without update settings enabled"
)
var ErrServiceNotUp = errors.New("service is not up")
@@ -74,8 +75,9 @@ type Server struct {
persistSyncResponse bool
isSessionActive atomic.Bool
profileManager *profilemanager.ServiceManager
profilesDisabled bool
profileManager *profilemanager.ServiceManager
profilesDisabled bool
updateSettingsDisabled bool
}
type oauthAuthFlow struct {
@@ -86,14 +88,15 @@ type oauthAuthFlow struct {
}
// New server instance constructor.
func New(ctx context.Context, logFile string, configFile string, profilesDisabled bool) *Server {
func New(ctx context.Context, logFile string, configFile string, profilesDisabled bool, updateSettingsDisabled bool) *Server {
return &Server{
rootCtx: ctx,
logFile: logFile,
persistSyncResponse: true,
statusRecorder: peer.NewRecorder(""),
profileManager: profilemanager.NewServiceManager(configFile),
profilesDisabled: profilesDisabled,
rootCtx: ctx,
logFile: logFile,
persistSyncResponse: true,
statusRecorder: peer.NewRecorder(""),
profileManager: profilemanager.NewServiceManager(configFile),
profilesDisabled: profilesDisabled,
updateSettingsDisabled: updateSettingsDisabled,
}
}
@@ -322,8 +325,8 @@ func (s *Server) SetConfig(callerCtx context.Context, msg *proto.SetConfigReques
s.mutex.Lock()
defer s.mutex.Unlock()
if s.checkProfilesDisabled() {
return nil, gstatus.Errorf(codes.Unavailable, errProfilesDisabled)
if s.checkUpdateSettingsDisabled() {
return nil, gstatus.Errorf(codes.Unavailable, errUpdateSettingsDisabled)
}
profState := profilemanager.ActiveProfileState{
@@ -1330,10 +1333,31 @@ func (s *Server) GetActiveProfile(ctx context.Context, msg *proto.GetActiveProfi
}, nil
}
// GetFeatures returns the features supported by the daemon.
func (s *Server) GetFeatures(ctx context.Context, msg *proto.GetFeaturesRequest) (*proto.GetFeaturesResponse, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
features := &proto.GetFeaturesResponse{
DisableProfiles: s.checkProfilesDisabled(),
DisableUpdateSettings: s.checkUpdateSettingsDisabled(),
}
return features, nil
}
func (s *Server) checkProfilesDisabled() bool {
// Check if the environment variable is set to disable profiles
if s.profilesDisabled {
log.Warn("Profiles are disabled via NB_DISABLE_PROFILES environment variable")
return true
}
return false
}
func (s *Server) checkUpdateSettingsDisabled() bool {
// Check if the environment variable is set to disable profiles
if s.updateSettingsDisabled {
return true
}

View File

@@ -25,7 +25,6 @@ import (
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/profilemanager"
daemonProto "github.com/netbirdio/netbird/client/proto"
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
@@ -34,6 +33,7 @@ import (
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/shared/signal/proto"
signalServer "github.com/netbirdio/netbird/signal/server"
)
@@ -95,7 +95,7 @@ func TestConnectWithRetryRuns(t *testing.T) {
t.Fatalf("failed to set active profile state: %v", err)
}
s := New(ctx, "debug", "", false)
s := New(ctx, "debug", "", false, false)
s.config = config
@@ -152,7 +152,7 @@ func TestServer_Up(t *testing.T) {
t.Fatalf("failed to set active profile state: %v", err)
}
s := New(ctx, "console", "", false)
s := New(ctx, "console", "", false, false)
err = s.Start()
require.NoError(t, err)
@@ -228,7 +228,7 @@ func TestServer_SubcribeEvents(t *testing.T) {
t.Fatalf("failed to set active profile state: %v", err)
}
s := New(ctx, "console", "", false)
s := New(ctx, "console", "", false, false)
err = s.Start()
require.NoError(t, err)

View File

@@ -392,6 +392,16 @@ func (s *serviceClient) updateIcon() {
}
func (s *serviceClient) showSettingsUI() {
// Check if update settings are disabled by daemon
features, err := s.getFeatures()
if err != nil {
log.Errorf("failed to get features from daemon: %v", err)
// Continue with default behavior if features can't be retrieved
} else if features != nil && features.DisableUpdateSettings {
log.Warn("Update settings are disabled by daemon")
return
}
// add settings window UI elements.
s.wSettings = s.app.NewWindow("NetBird Settings")
s.wSettings.SetOnClosed(s.cancel)
@@ -447,6 +457,17 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
},
SubmitText: "Save",
OnSubmit: func() {
// Check if update settings are disabled by daemon
features, err := s.getFeatures()
if err != nil {
log.Errorf("failed to get features from daemon: %v", err)
// Continue with default behavior if features can't be retrieved
} else if features != nil && features.DisableUpdateSettings {
log.Warn("Configuration updates are disabled by daemon")
dialog.ShowError(fmt.Errorf("Configuration updates are disabled by daemon"), s.wSettings)
return
}
if s.iPreSharedKey.Text != "" && s.iPreSharedKey.Text != censoredPreSharedKey {
// validate preSharedKey if it added
if _, err := wgtypes.ParseKey(s.iPreSharedKey.Text); err != nil {
@@ -836,6 +857,20 @@ func (s *serviceClient) onTrayReady() {
s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr)
s.loadSettings()
// Disable settings menu if update settings are disabled by daemon
features, err := s.getFeatures()
if err != nil {
log.Errorf("failed to get features from daemon: %v", err)
// Continue with default behavior if features can't be retrieved
} else {
if features != nil && features.DisableUpdateSettings {
s.setSettingsEnabled(false)
}
if features != nil && features.DisableProfiles {
s.mProfile.setEnabled(false)
}
}
s.exitNodeMu.Lock()
s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr)
s.mExitNode.Disable()
@@ -876,6 +911,10 @@ func (s *serviceClient) onTrayReady() {
if err != nil {
log.Errorf("error while updating status: %v", err)
}
// Check features periodically to handle daemon restarts
s.checkAndUpdateFeatures()
time.Sleep(2 * time.Second)
}
}()
@@ -948,6 +987,59 @@ func (s *serviceClient) getSrvClient(timeout time.Duration) (proto.DaemonService
return s.conn, nil
}
// setSettingsEnabled enables or disables the settings menu based on the provided state
func (s *serviceClient) setSettingsEnabled(enabled bool) {
if s.mSettings != nil {
if enabled {
s.mSettings.Enable()
s.mSettings.SetTooltip(settingsMenuDescr)
} else {
s.mSettings.Hide()
s.mSettings.SetTooltip("Settings are disabled by daemon")
}
}
}
// checkAndUpdateFeatures checks the current features and updates the UI accordingly
func (s *serviceClient) checkAndUpdateFeatures() {
features, err := s.getFeatures()
if err != nil {
log.Errorf("failed to get features from daemon: %v", err)
return
}
// Update settings menu based on current features
if features != nil && features.DisableUpdateSettings {
s.setSettingsEnabled(false)
} else {
s.setSettingsEnabled(true)
}
// Update profile menu based on current features
if s.mProfile != nil {
if features != nil && features.DisableProfiles {
s.mProfile.setEnabled(false)
} else {
s.mProfile.setEnabled(true)
}
}
}
// getFeatures from the daemon to determine which features are enabled/disabled.
func (s *serviceClient) getFeatures() (*proto.GetFeaturesResponse, error) {
conn, err := s.getSrvClient(failFastTimeout)
if err != nil {
return nil, fmt.Errorf("get client for features: %w", err)
}
features, err := conn.GetFeatures(s.ctx, &proto.GetFeaturesRequest{})
if err != nil {
return nil, fmt.Errorf("get features from daemon: %w", err)
}
return features, nil
}
// getSrvConfig from the service to show it in the settings window.
func (s *serviceClient) getSrvConfig() {
s.managementURL = profilemanager.DefaultManagementURL

View File

@@ -654,6 +654,19 @@ func (p *profileMenu) clear(profiles []Profile) {
}
}
// setEnabled enables or disables the profile menu based on the provided state
func (p *profileMenu) setEnabled(enabled bool) {
if p.profileMenuItem != nil {
if enabled {
p.profileMenuItem.Enable()
p.profileMenuItem.SetTooltip("")
} else {
p.profileMenuItem.Hide()
p.profileMenuItem.SetTooltip("Profiles are disabled by daemon")
}
}
}
func (p *profileMenu) updateMenu() {
// check every second
ticker := time.NewTicker(time.Second)
@@ -662,7 +675,6 @@ func (p *profileMenu) updateMenu() {
for {
select {
case <-ticker.C:
// get profilesList
profiles, err := p.getProfiles()
if err != nil {