mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-14 12:49:57 +00:00
Compare commits
11 Commits
fix/remove
...
fix/test-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e59223c81f | ||
|
|
a343157c45 | ||
|
|
d80fe7cc5a | ||
|
|
a11f903aa2 | ||
|
|
ee85cfe21b | ||
|
|
90596dde61 | ||
|
|
41d4779867 | ||
|
|
e97f853909 | ||
|
|
70db8751d7 | ||
|
|
86a00ab4af | ||
|
|
3d4b502126 |
3
.github/workflows/golang-test-freebsd.yml
vendored
3
.github/workflows/golang-test-freebsd.yml
vendored
@@ -25,8 +25,7 @@ jobs:
|
|||||||
release: "14.2"
|
release: "14.2"
|
||||||
prepare: |
|
prepare: |
|
||||||
pkg install -y curl pkgconf xorg
|
pkg install -y curl pkgconf xorg
|
||||||
LATEST_VERSION=$(curl -s https://go.dev/VERSION?m=text|head -n 1)
|
GO_TARBALL="go1.23.12.freebsd-amd64.tar.gz"
|
||||||
GO_TARBALL="$LATEST_VERSION.freebsd-amd64.tar.gz"
|
|
||||||
GO_URL="https://go.dev/dl/$GO_TARBALL"
|
GO_URL="https://go.dev/dl/$GO_TARBALL"
|
||||||
curl -vLO "$GO_URL"
|
curl -vLO "$GO_URL"
|
||||||
tar -C /usr/local -vxzf "$GO_TARBALL"
|
tar -C /usr/local -vxzf "$GO_TARBALL"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ var (
|
|||||||
var debugCmd = &cobra.Command{
|
var debugCmd = &cobra.Command{
|
||||||
Use: "debug",
|
Use: "debug",
|
||||||
Short: "Debugging commands",
|
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{
|
var debugBundleCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import (
|
|||||||
|
|
||||||
var downCmd = &cobra.Command{
|
var downCmd = &cobra.Command{
|
||||||
Use: "down",
|
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 {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
SetFlagsFromEnvVars(rootCmd)
|
SetFlagsFromEnvVars(rootCmd)
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ func init() {
|
|||||||
|
|
||||||
var loginCmd = &cobra.Command{
|
var loginCmd = &cobra.Command{
|
||||||
Use: "login",
|
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 {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := setEnvAndFlags(cmd); err != nil {
|
if err := setEnvAndFlags(cmd); err != nil {
|
||||||
return fmt.Errorf("set env and flags: %v", err)
|
return fmt.Errorf("set env and flags: %v", err)
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import (
|
|||||||
var logoutCmd = &cobra.Command{
|
var logoutCmd = &cobra.Command{
|
||||||
Use: "deregister",
|
Use: "deregister",
|
||||||
Aliases: []string{"logout"},
|
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 {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
SetFlagsFromEnvVars(rootCmd)
|
SetFlagsFromEnvVars(rootCmd)
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ var appendFlag bool
|
|||||||
var networksCMD = &cobra.Command{
|
var networksCMD = &cobra.Command{
|
||||||
Use: "networks",
|
Use: "networks",
|
||||||
Aliases: []string{"routes"},
|
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.`,
|
Long: `Commands to list, select, or deselect networks. Replaces the "routes" command.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import (
|
|||||||
|
|
||||||
var profileCmd = &cobra.Command{
|
var profileCmd = &cobra.Command{
|
||||||
Use: "profile",
|
Use: "profile",
|
||||||
Short: "manage NetBird profiles",
|
Short: "Manage NetBird client profiles",
|
||||||
Long: `Manage NetBird profiles, allowing you to list, switch, and remove 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{
|
var profileListCmd = &cobra.Command{
|
||||||
Use: "list",
|
Use: "list",
|
||||||
Short: "list all profiles",
|
Short: "List all profiles",
|
||||||
Long: `List all available profiles in the NetBird client.`,
|
Long: `List all available profiles in the NetBird client.`,
|
||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
RunE: listProfilesFunc,
|
RunE: listProfilesFunc,
|
||||||
@@ -30,7 +30,7 @@ var profileListCmd = &cobra.Command{
|
|||||||
|
|
||||||
var profileAddCmd = &cobra.Command{
|
var profileAddCmd = &cobra.Command{
|
||||||
Use: "add <profile_name>",
|
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.`,
|
Long: `Add a new profile to the NetBird client. The profile name must be unique.`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: addProfileFunc,
|
RunE: addProfileFunc,
|
||||||
@@ -38,16 +38,16 @@ var profileAddCmd = &cobra.Command{
|
|||||||
|
|
||||||
var profileRemoveCmd = &cobra.Command{
|
var profileRemoveCmd = &cobra.Command{
|
||||||
Use: "remove <profile_name>",
|
Use: "remove <profile_name>",
|
||||||
Short: "remove a profile",
|
Short: "Remove a profile",
|
||||||
Long: `Remove a profile from the NetBird client. The profile must not be active.`,
|
Long: `Remove a profile from the NetBird client. The profile must not be inactive.`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: removeProfileFunc,
|
RunE: removeProfileFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
var profileSelectCmd = &cobra.Command{
|
var profileSelectCmd = &cobra.Command{
|
||||||
Use: "select <profile_name>",
|
Use: "select <profile_name>",
|
||||||
Short: "select a profile",
|
Short: "Select a profile",
|
||||||
Long: `Select a profile to be the active profile in the NetBird client. The profile must exist.`,
|
Long: `Make the specified profile active. This will switch the client to use the selected profile's configuration.`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: selectProfileFunc,
|
RunE: selectProfileFunc,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ var (
|
|||||||
dnsRouteInterval time.Duration
|
dnsRouteInterval time.Duration
|
||||||
lazyConnEnabled bool
|
lazyConnEnabled bool
|
||||||
profilesDisabled bool
|
profilesDisabled bool
|
||||||
|
updateSettingsDisabled bool
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "netbird",
|
Use: "netbird",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
|
|
||||||
var serviceCmd = &cobra.Command{
|
var serviceCmd = &cobra.Command{
|
||||||
Use: "service",
|
Use: "service",
|
||||||
Short: "manages NetBird service",
|
Short: "Manage the NetBird daemon service",
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -42,7 +42,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd, svcStatusCmd, installCmd, uninstallCmd, reconfigureCmd)
|
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")
|
rootCmd.PersistentFlags().StringVarP(&serviceName, "service", "s", defaultServiceName, "Netbird system service name")
|
||||||
serviceEnvDesc := `Sets extra environment variables for the service. ` +
|
serviceEnvDesc := `Sets extra environment variables for the service. ` +
|
||||||
|
|||||||
@@ -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 {
|
if err := serverInstance.Start(); err != nil {
|
||||||
log.Fatalf("failed to start daemon: %v", err)
|
log.Fatalf("failed to start daemon: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ func buildServiceArguments() []string {
|
|||||||
args = append(args, "--log-file", logFile)
|
args = append(args, "--log-file", logFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if profilesDisabled {
|
||||||
|
args = append(args, "--disable-profiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateSettingsDisabled {
|
||||||
|
args = append(args, "--disable-update-settings")
|
||||||
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +107,7 @@ func createServiceConfigForInstall() (*service.Config, error) {
|
|||||||
|
|
||||||
var installCmd = &cobra.Command{
|
var installCmd = &cobra.Command{
|
||||||
Use: "install",
|
Use: "install",
|
||||||
Short: "installs NetBird service",
|
Short: "Install NetBird service",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := setupServiceCommand(cmd); err != nil {
|
if err := setupServiceCommand(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -59,11 +60,20 @@ func waitForServiceStatus(expectedStatus service.Status, timeout time.Duration)
|
|||||||
|
|
||||||
// TestServiceLifecycle tests the complete service lifecycle
|
// TestServiceLifecycle tests the complete service lifecycle
|
||||||
func TestServiceLifecycle(t *testing.T) {
|
func TestServiceLifecycle(t *testing.T) {
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get current user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("--- Running service lifecycle test: %s, %s", runtime.GOOS, currentUser.Username)
|
||||||
|
|
||||||
// TODO: Add support for Windows and macOS
|
// TODO: Add support for Windows and macOS
|
||||||
if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" {
|
if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" {
|
||||||
|
t.Logf("--- Running service lifecycle test: %s", runtime.GOOS)
|
||||||
t.Skipf("Skipping service lifecycle test on unsupported OS: %s", runtime.GOOS)
|
t.Skipf("Skipping service lifecycle test on unsupported OS: %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Logf("--- Running service lifecycle test on: %s", os.Getenv("CONTAINER"))
|
||||||
if os.Getenv("CONTAINER") == "true" {
|
if os.Getenv("CONTAINER") == "true" {
|
||||||
t.Skip("Skipping service lifecycle test in container environment")
|
t.Skip("Skipping service lifecycle test in container environment")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ var sshCmd = &cobra.Command{
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Short: "connect to a remote SSH server",
|
Short: "Connect to a remote SSH server",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
SetFlagsFromEnvVars(rootCmd)
|
SetFlagsFromEnvVars(rootCmd)
|
||||||
SetFlagsFromEnvVars(cmd)
|
SetFlagsFromEnvVars(cmd)
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ var (
|
|||||||
|
|
||||||
var statusCmd = &cobra.Command{
|
var statusCmd = &cobra.Command{
|
||||||
Use: "status",
|
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,
|
RunE: statusFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import (
|
|||||||
|
|
||||||
clientProto "github.com/netbirdio/netbird/client/proto"
|
clientProto "github.com/netbirdio/netbird/client/proto"
|
||||||
client "github.com/netbirdio/netbird/client/server"
|
client "github.com/netbirdio/netbird/client/server"
|
||||||
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
|
||||||
mgmt "github.com/netbirdio/netbird/management/server"
|
mgmt "github.com/netbirdio/netbird/management/server"
|
||||||
|
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||||
sigProto "github.com/netbirdio/netbird/shared/signal/proto"
|
sigProto "github.com/netbirdio/netbird/shared/signal/proto"
|
||||||
sig "github.com/netbirdio/netbird/signal/server"
|
sig "github.com/netbirdio/netbird/signal/server"
|
||||||
)
|
)
|
||||||
@@ -136,7 +136,7 @@ func startClientDaemon(
|
|||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
|
|
||||||
server := client.New(ctx,
|
server := client.New(ctx,
|
||||||
"", "", false)
|
"", "", false, false)
|
||||||
if err := server.Start(); err != nil {
|
if err := server.Start(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ var (
|
|||||||
|
|
||||||
upCmd = &cobra.Command{
|
upCmd = &cobra.Command{
|
||||||
Use: "up",
|
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,
|
RunE: upFunc,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
versionCmd = &cobra.Command{
|
versionCmd = &cobra.Command{
|
||||||
Use: "version",
|
Use: "version",
|
||||||
Short: "prints NetBird version",
|
Short: "Print the NetBird's client application version",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
cmd.SetOut(cmd.OutOrStdout())
|
cmd.SetOut(cmd.OutOrStdout())
|
||||||
cmd.Println(version.NetbirdVersion())
|
cmd.Println(version.NetbirdVersion())
|
||||||
|
|||||||
@@ -4430,6 +4430,94 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) {
|
|||||||
return file_daemon_proto_rawDescGZIP(), []int{66}
|
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 {
|
type PortInfo_Range struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"`
|
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() {
|
func (x *PortInfo_Range) Reset() {
|
||||||
*x = PortInfo_Range{}
|
*x = PortInfo_Range{}
|
||||||
mi := &file_daemon_proto_msgTypes[68]
|
mi := &file_daemon_proto_msgTypes[70]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -4452,7 +4540,7 @@ func (x *PortInfo_Range) String() string {
|
|||||||
func (*PortInfo_Range) ProtoMessage() {}
|
func (*PortInfo_Range) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PortInfo_Range) ProtoReflect() protoreflect.Message {
|
func (x *PortInfo_Range) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_proto_msgTypes[68]
|
mi := &file_daemon_proto_msgTypes[70]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
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" +
|
"\busername\x18\x02 \x01(\tH\x01R\busername\x88\x01\x01B\x0e\n" +
|
||||||
"\f_profileNameB\v\n" +
|
"\f_profileNameB\v\n" +
|
||||||
"\t_username\"\x10\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" +
|
"\bLogLevel\x12\v\n" +
|
||||||
"\aUNKNOWN\x10\x00\x12\t\n" +
|
"\aUNKNOWN\x10\x00\x12\t\n" +
|
||||||
"\x05PANIC\x10\x01\x12\t\n" +
|
"\x05PANIC\x10\x01\x12\t\n" +
|
||||||
@@ -4881,7 +4973,7 @@ const file_daemon_proto_rawDesc = "" +
|
|||||||
"\x04WARN\x10\x04\x12\b\n" +
|
"\x04WARN\x10\x04\x12\b\n" +
|
||||||
"\x04INFO\x10\x05\x12\t\n" +
|
"\x04INFO\x10\x05\x12\t\n" +
|
||||||
"\x05DEBUG\x10\x06\x12\t\n" +
|
"\x05DEBUG\x10\x06\x12\t\n" +
|
||||||
"\x05TRACE\x10\a2\xc5\x0f\n" +
|
"\x05TRACE\x10\a2\x8f\x10\n" +
|
||||||
"\rDaemonService\x126\n" +
|
"\rDaemonService\x126\n" +
|
||||||
"\x05Login\x12\x14.daemon.LoginRequest\x1a\x15.daemon.LoginResponse\"\x00\x12K\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" +
|
"\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" +
|
"\rRemoveProfile\x12\x1c.daemon.RemoveProfileRequest\x1a\x1d.daemon.RemoveProfileResponse\"\x00\x12K\n" +
|
||||||
"\fListProfiles\x12\x1b.daemon.ListProfilesRequest\x1a\x1c.daemon.ListProfilesResponse\"\x00\x12W\n" +
|
"\fListProfiles\x12\x1b.daemon.ListProfilesRequest\x1a\x1c.daemon.ListProfilesResponse\"\x00\x12W\n" +
|
||||||
"\x10GetActiveProfile\x12\x1f.daemon.GetActiveProfileRequest\x1a .daemon.GetActiveProfileResponse\"\x00\x129\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 (
|
var (
|
||||||
file_daemon_proto_rawDescOnce sync.Once
|
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_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{
|
var file_daemon_proto_goTypes = []any{
|
||||||
(LogLevel)(0), // 0: daemon.LogLevel
|
(LogLevel)(0), // 0: daemon.LogLevel
|
||||||
(SystemEvent_Severity)(0), // 1: daemon.SystemEvent.Severity
|
(SystemEvent_Severity)(0), // 1: daemon.SystemEvent.Severity
|
||||||
@@ -4999,18 +5092,20 @@ var file_daemon_proto_goTypes = []any{
|
|||||||
(*GetActiveProfileResponse)(nil), // 67: daemon.GetActiveProfileResponse
|
(*GetActiveProfileResponse)(nil), // 67: daemon.GetActiveProfileResponse
|
||||||
(*LogoutRequest)(nil), // 68: daemon.LogoutRequest
|
(*LogoutRequest)(nil), // 68: daemon.LogoutRequest
|
||||||
(*LogoutResponse)(nil), // 69: daemon.LogoutResponse
|
(*LogoutResponse)(nil), // 69: daemon.LogoutResponse
|
||||||
nil, // 70: daemon.Network.ResolvedIPsEntry
|
(*GetFeaturesRequest)(nil), // 70: daemon.GetFeaturesRequest
|
||||||
(*PortInfo_Range)(nil), // 71: daemon.PortInfo.Range
|
(*GetFeaturesResponse)(nil), // 71: daemon.GetFeaturesResponse
|
||||||
nil, // 72: daemon.SystemEvent.MetadataEntry
|
nil, // 72: daemon.Network.ResolvedIPsEntry
|
||||||
(*durationpb.Duration)(nil), // 73: google.protobuf.Duration
|
(*PortInfo_Range)(nil), // 73: daemon.PortInfo.Range
|
||||||
(*timestamppb.Timestamp)(nil), // 74: google.protobuf.Timestamp
|
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{
|
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
|
22, // 1: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus
|
||||||
74, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
|
76, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
|
||||||
74, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
|
76, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
|
||||||
73, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration
|
75, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration
|
||||||
19, // 5: daemon.FullStatus.managementState:type_name -> daemon.ManagementState
|
19, // 5: daemon.FullStatus.managementState:type_name -> daemon.ManagementState
|
||||||
18, // 6: daemon.FullStatus.signalState:type_name -> daemon.SignalState
|
18, // 6: daemon.FullStatus.signalState:type_name -> daemon.SignalState
|
||||||
17, // 7: daemon.FullStatus.localPeerState:type_name -> daemon.LocalPeerState
|
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
|
21, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState
|
||||||
52, // 11: daemon.FullStatus.events:type_name -> daemon.SystemEvent
|
52, // 11: daemon.FullStatus.events:type_name -> daemon.SystemEvent
|
||||||
28, // 12: daemon.ListNetworksResponse.routes:type_name -> daemon.Network
|
28, // 12: daemon.ListNetworksResponse.routes:type_name -> daemon.Network
|
||||||
70, // 13: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry
|
72, // 13: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry
|
||||||
71, // 14: daemon.PortInfo.range:type_name -> daemon.PortInfo.Range
|
73, // 14: daemon.PortInfo.range:type_name -> daemon.PortInfo.Range
|
||||||
29, // 15: daemon.ForwardingRule.destinationPort:type_name -> daemon.PortInfo
|
29, // 15: daemon.ForwardingRule.destinationPort:type_name -> daemon.PortInfo
|
||||||
29, // 16: daemon.ForwardingRule.translatedPort:type_name -> daemon.PortInfo
|
29, // 16: daemon.ForwardingRule.translatedPort:type_name -> daemon.PortInfo
|
||||||
30, // 17: daemon.ForwardingRulesResponse.rules:type_name -> daemon.ForwardingRule
|
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
|
49, // 22: daemon.TracePacketResponse.stages:type_name -> daemon.TraceStage
|
||||||
1, // 23: daemon.SystemEvent.severity:type_name -> daemon.SystemEvent.Severity
|
1, // 23: daemon.SystemEvent.severity:type_name -> daemon.SystemEvent.Severity
|
||||||
2, // 24: daemon.SystemEvent.category:type_name -> daemon.SystemEvent.Category
|
2, // 24: daemon.SystemEvent.category:type_name -> daemon.SystemEvent.Category
|
||||||
74, // 25: daemon.SystemEvent.timestamp:type_name -> google.protobuf.Timestamp
|
76, // 25: daemon.SystemEvent.timestamp:type_name -> google.protobuf.Timestamp
|
||||||
72, // 26: daemon.SystemEvent.metadata:type_name -> daemon.SystemEvent.MetadataEntry
|
74, // 26: daemon.SystemEvent.metadata:type_name -> daemon.SystemEvent.MetadataEntry
|
||||||
52, // 27: daemon.GetEventsResponse.events:type_name -> daemon.SystemEvent
|
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
|
65, // 29: daemon.ListProfilesResponse.profiles:type_name -> daemon.Profile
|
||||||
27, // 30: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList
|
27, // 30: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList
|
||||||
4, // 31: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
|
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
|
63, // 55: daemon.DaemonService.ListProfiles:input_type -> daemon.ListProfilesRequest
|
||||||
66, // 56: daemon.DaemonService.GetActiveProfile:input_type -> daemon.GetActiveProfileRequest
|
66, // 56: daemon.DaemonService.GetActiveProfile:input_type -> daemon.GetActiveProfileRequest
|
||||||
68, // 57: daemon.DaemonService.Logout:input_type -> daemon.LogoutRequest
|
68, // 57: daemon.DaemonService.Logout:input_type -> daemon.LogoutRequest
|
||||||
5, // 58: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
|
70, // 58: daemon.DaemonService.GetFeatures:input_type -> daemon.GetFeaturesRequest
|
||||||
7, // 59: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
|
5, // 59: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
|
||||||
9, // 60: daemon.DaemonService.Up:output_type -> daemon.UpResponse
|
7, // 60: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
|
||||||
11, // 61: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
|
9, // 61: daemon.DaemonService.Up:output_type -> daemon.UpResponse
|
||||||
13, // 62: daemon.DaemonService.Down:output_type -> daemon.DownResponse
|
11, // 62: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
|
||||||
15, // 63: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
|
13, // 63: daemon.DaemonService.Down:output_type -> daemon.DownResponse
|
||||||
24, // 64: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse
|
15, // 64: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
|
||||||
26, // 65: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse
|
24, // 65: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse
|
||||||
26, // 66: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse
|
26, // 66: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse
|
||||||
31, // 67: daemon.DaemonService.ForwardingRules:output_type -> daemon.ForwardingRulesResponse
|
26, // 67: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse
|
||||||
33, // 68: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
|
31, // 68: daemon.DaemonService.ForwardingRules:output_type -> daemon.ForwardingRulesResponse
|
||||||
35, // 69: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
|
33, // 69: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
|
||||||
37, // 70: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
|
35, // 70: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
|
||||||
40, // 71: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse
|
37, // 71: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
|
||||||
42, // 72: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse
|
40, // 72: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse
|
||||||
44, // 73: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse
|
42, // 73: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse
|
||||||
46, // 74: daemon.DaemonService.SetSyncResponsePersistence:output_type -> daemon.SetSyncResponsePersistenceResponse
|
44, // 74: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse
|
||||||
50, // 75: daemon.DaemonService.TracePacket:output_type -> daemon.TracePacketResponse
|
46, // 75: daemon.DaemonService.SetSyncResponsePersistence:output_type -> daemon.SetSyncResponsePersistenceResponse
|
||||||
52, // 76: daemon.DaemonService.SubscribeEvents:output_type -> daemon.SystemEvent
|
50, // 76: daemon.DaemonService.TracePacket:output_type -> daemon.TracePacketResponse
|
||||||
54, // 77: daemon.DaemonService.GetEvents:output_type -> daemon.GetEventsResponse
|
52, // 77: daemon.DaemonService.SubscribeEvents:output_type -> daemon.SystemEvent
|
||||||
56, // 78: daemon.DaemonService.SwitchProfile:output_type -> daemon.SwitchProfileResponse
|
54, // 78: daemon.DaemonService.GetEvents:output_type -> daemon.GetEventsResponse
|
||||||
58, // 79: daemon.DaemonService.SetConfig:output_type -> daemon.SetConfigResponse
|
56, // 79: daemon.DaemonService.SwitchProfile:output_type -> daemon.SwitchProfileResponse
|
||||||
60, // 80: daemon.DaemonService.AddProfile:output_type -> daemon.AddProfileResponse
|
58, // 80: daemon.DaemonService.SetConfig:output_type -> daemon.SetConfigResponse
|
||||||
62, // 81: daemon.DaemonService.RemoveProfile:output_type -> daemon.RemoveProfileResponse
|
60, // 81: daemon.DaemonService.AddProfile:output_type -> daemon.AddProfileResponse
|
||||||
64, // 82: daemon.DaemonService.ListProfiles:output_type -> daemon.ListProfilesResponse
|
62, // 82: daemon.DaemonService.RemoveProfile:output_type -> daemon.RemoveProfileResponse
|
||||||
67, // 83: daemon.DaemonService.GetActiveProfile:output_type -> daemon.GetActiveProfileResponse
|
64, // 83: daemon.DaemonService.ListProfiles:output_type -> daemon.ListProfilesResponse
|
||||||
69, // 84: daemon.DaemonService.Logout:output_type -> daemon.LogoutResponse
|
67, // 84: daemon.DaemonService.GetActiveProfile:output_type -> daemon.GetActiveProfileResponse
|
||||||
58, // [58:85] is the sub-list for method output_type
|
69, // 85: daemon.DaemonService.Logout:output_type -> daemon.LogoutResponse
|
||||||
31, // [31:58] is the sub-list for method input_type
|
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 type_name
|
||||||
31, // [31:31] is the sub-list for extension extendee
|
31, // [31:31] is the sub-list for extension extendee
|
||||||
0, // [0:31] is the sub-list for field type_name
|
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(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_proto_rawDesc), len(file_daemon_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_proto_rawDesc), len(file_daemon_proto_rawDesc)),
|
||||||
NumEnums: 3,
|
NumEnums: 3,
|
||||||
NumMessages: 70,
|
NumMessages: 72,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ service DaemonService {
|
|||||||
|
|
||||||
// Logout disconnects from the network and deletes the peer from the management server
|
// Logout disconnects from the network and deletes the peer from the management server
|
||||||
rpc Logout(LogoutRequest) returns (LogoutResponse) {}
|
rpc Logout(LogoutRequest) returns (LogoutResponse) {}
|
||||||
|
|
||||||
|
rpc GetFeatures(GetFeaturesRequest) returns (GetFeaturesResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -624,4 +626,11 @@ message LogoutRequest {
|
|||||||
optional string username = 2;
|
optional string username = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LogoutResponse {}
|
message LogoutResponse {}
|
||||||
|
|
||||||
|
message GetFeaturesRequest{}
|
||||||
|
|
||||||
|
message GetFeaturesResponse{
|
||||||
|
bool disable_profiles = 1;
|
||||||
|
bool disable_update_settings = 2;
|
||||||
|
}
|
||||||
@@ -63,6 +63,7 @@ type DaemonServiceClient interface {
|
|||||||
GetActiveProfile(ctx context.Context, in *GetActiveProfileRequest, opts ...grpc.CallOption) (*GetActiveProfileResponse, error)
|
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 disconnects from the network and deletes the peer from the management server
|
||||||
Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error)
|
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 {
|
type daemonServiceClient struct {
|
||||||
@@ -339,6 +340,15 @@ func (c *daemonServiceClient) Logout(ctx context.Context, in *LogoutRequest, opt
|
|||||||
return out, nil
|
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.
|
// DaemonServiceServer is the server API for DaemonService service.
|
||||||
// All implementations must embed UnimplementedDaemonServiceServer
|
// All implementations must embed UnimplementedDaemonServiceServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
@@ -388,6 +398,7 @@ type DaemonServiceServer interface {
|
|||||||
GetActiveProfile(context.Context, *GetActiveProfileRequest) (*GetActiveProfileResponse, error)
|
GetActiveProfile(context.Context, *GetActiveProfileRequest) (*GetActiveProfileResponse, error)
|
||||||
// Logout disconnects from the network and deletes the peer from the management server
|
// Logout disconnects from the network and deletes the peer from the management server
|
||||||
Logout(context.Context, *LogoutRequest) (*LogoutResponse, error)
|
Logout(context.Context, *LogoutRequest) (*LogoutResponse, error)
|
||||||
|
GetFeatures(context.Context, *GetFeaturesRequest) (*GetFeaturesResponse, error)
|
||||||
mustEmbedUnimplementedDaemonServiceServer()
|
mustEmbedUnimplementedDaemonServiceServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,6 +487,9 @@ func (UnimplementedDaemonServiceServer) GetActiveProfile(context.Context, *GetAc
|
|||||||
func (UnimplementedDaemonServiceServer) Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) {
|
func (UnimplementedDaemonServiceServer) Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
|
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() {}
|
func (UnimplementedDaemonServiceServer) mustEmbedUnimplementedDaemonServiceServer() {}
|
||||||
|
|
||||||
// UnsafeDaemonServiceServer may be embedded to opt out of forward compatibility for this service.
|
// 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)
|
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.
|
// DaemonService_ServiceDesc is the grpc.ServiceDesc for DaemonService service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
@@ -1089,6 +1121,10 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "Logout",
|
MethodName: "Logout",
|
||||||
Handler: _DaemonService_Logout_Handler,
|
Handler: _DaemonService_Logout_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetFeatures",
|
||||||
|
Handler: _DaemonService_GetFeatures_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,8 +46,9 @@ const (
|
|||||||
defaultMaxRetryTime = 14 * 24 * time.Hour
|
defaultMaxRetryTime = 14 * 24 * time.Hour
|
||||||
defaultRetryMultiplier = 1.7
|
defaultRetryMultiplier = 1.7
|
||||||
|
|
||||||
errRestoreResidualState = "failed to restore residual state: %v"
|
errRestoreResidualState = "failed to restore residual state: %v"
|
||||||
errProfilesDisabled = "profiles are disabled, you cannot use this feature without profiles enabled"
|
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")
|
var ErrServiceNotUp = errors.New("service is not up")
|
||||||
@@ -74,8 +75,9 @@ type Server struct {
|
|||||||
persistSyncResponse bool
|
persistSyncResponse bool
|
||||||
isSessionActive atomic.Bool
|
isSessionActive atomic.Bool
|
||||||
|
|
||||||
profileManager *profilemanager.ServiceManager
|
profileManager *profilemanager.ServiceManager
|
||||||
profilesDisabled bool
|
profilesDisabled bool
|
||||||
|
updateSettingsDisabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type oauthAuthFlow struct {
|
type oauthAuthFlow struct {
|
||||||
@@ -86,14 +88,15 @@ type oauthAuthFlow struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New server instance constructor.
|
// 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{
|
return &Server{
|
||||||
rootCtx: ctx,
|
rootCtx: ctx,
|
||||||
logFile: logFile,
|
logFile: logFile,
|
||||||
persistSyncResponse: true,
|
persistSyncResponse: true,
|
||||||
statusRecorder: peer.NewRecorder(""),
|
statusRecorder: peer.NewRecorder(""),
|
||||||
profileManager: profilemanager.NewServiceManager(configFile),
|
profileManager: profilemanager.NewServiceManager(configFile),
|
||||||
profilesDisabled: profilesDisabled,
|
profilesDisabled: profilesDisabled,
|
||||||
|
updateSettingsDisabled: updateSettingsDisabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,8 +325,8 @@ func (s *Server) SetConfig(callerCtx context.Context, msg *proto.SetConfigReques
|
|||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
if s.checkProfilesDisabled() {
|
if s.checkUpdateSettingsDisabled() {
|
||||||
return nil, gstatus.Errorf(codes.Unavailable, errProfilesDisabled)
|
return nil, gstatus.Errorf(codes.Unavailable, errUpdateSettingsDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
profState := profilemanager.ActiveProfileState{
|
profState := profilemanager.ActiveProfileState{
|
||||||
@@ -1330,10 +1333,31 @@ func (s *Server) GetActiveProfile(ctx context.Context, msg *proto.GetActiveProfi
|
|||||||
}, nil
|
}, 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 {
|
func (s *Server) checkProfilesDisabled() bool {
|
||||||
// Check if the environment variable is set to disable profiles
|
// Check if the environment variable is set to disable profiles
|
||||||
if s.profilesDisabled {
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
||||||
daemonProto "github.com/netbirdio/netbird/client/proto"
|
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"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
|
"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/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
"github.com/netbirdio/netbird/management/server/types"
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||||
"github.com/netbirdio/netbird/shared/signal/proto"
|
"github.com/netbirdio/netbird/shared/signal/proto"
|
||||||
signalServer "github.com/netbirdio/netbird/signal/server"
|
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)
|
t.Fatalf("failed to set active profile state: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := New(ctx, "debug", "", false)
|
s := New(ctx, "debug", "", false, false)
|
||||||
|
|
||||||
s.config = config
|
s.config = config
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ func TestServer_Up(t *testing.T) {
|
|||||||
t.Fatalf("failed to set active profile state: %v", err)
|
t.Fatalf("failed to set active profile state: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := New(ctx, "console", "", false)
|
s := New(ctx, "console", "", false, false)
|
||||||
|
|
||||||
err = s.Start()
|
err = s.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -228,7 +228,7 @@ func TestServer_SubcribeEvents(t *testing.T) {
|
|||||||
t.Fatalf("failed to set active profile state: %v", err)
|
t.Fatalf("failed to set active profile state: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := New(ctx, "console", "", false)
|
s := New(ctx, "console", "", false, false)
|
||||||
|
|
||||||
err = s.Start()
|
err = s.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -392,6 +392,16 @@ func (s *serviceClient) updateIcon() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) showSettingsUI() {
|
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.
|
// add settings window UI elements.
|
||||||
s.wSettings = s.app.NewWindow("NetBird Settings")
|
s.wSettings = s.app.NewWindow("NetBird Settings")
|
||||||
s.wSettings.SetOnClosed(s.cancel)
|
s.wSettings.SetOnClosed(s.cancel)
|
||||||
@@ -447,6 +457,17 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
|
|||||||
},
|
},
|
||||||
SubmitText: "Save",
|
SubmitText: "Save",
|
||||||
OnSubmit: func() {
|
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 {
|
if s.iPreSharedKey.Text != "" && s.iPreSharedKey.Text != censoredPreSharedKey {
|
||||||
// validate preSharedKey if it added
|
// validate preSharedKey if it added
|
||||||
if _, err := wgtypes.ParseKey(s.iPreSharedKey.Text); err != nil {
|
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.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr)
|
||||||
s.loadSettings()
|
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.exitNodeMu.Lock()
|
||||||
s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr)
|
s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr)
|
||||||
s.mExitNode.Disable()
|
s.mExitNode.Disable()
|
||||||
@@ -876,6 +911,10 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error while updating status: %v", err)
|
log.Errorf("error while updating status: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check features periodically to handle daemon restarts
|
||||||
|
s.checkAndUpdateFeatures()
|
||||||
|
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -948,6 +987,59 @@ func (s *serviceClient) getSrvClient(timeout time.Duration) (proto.DaemonService
|
|||||||
return s.conn, nil
|
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.
|
// getSrvConfig from the service to show it in the settings window.
|
||||||
func (s *serviceClient) getSrvConfig() {
|
func (s *serviceClient) getSrvConfig() {
|
||||||
s.managementURL = profilemanager.DefaultManagementURL
|
s.managementURL = profilemanager.DefaultManagementURL
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"fyne.io/systray"
|
"fyne.io/systray"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -231,3 +232,19 @@ func (h *eventHandler) runSelfCommand(ctx context.Context, command, arg string)
|
|||||||
|
|
||||||
log.Printf("command '%s %s' completed successfully", command, arg)
|
log.Printf("command '%s %s' completed successfully", command, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *eventHandler) logout(ctx context.Context) error {
|
||||||
|
client, err := h.client.getSrvClient(defaultFailTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get service client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Logout(ctx, &proto.LogoutRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("logout failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.client.getSrvConfig()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,12 +40,13 @@ func (s *serviceClient) showProfilesUI() {
|
|||||||
list := widget.NewList(
|
list := widget.NewList(
|
||||||
func() int { return len(profiles) },
|
func() int { return len(profiles) },
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
// Each item: Selected indicator, Name, spacer, Select & Remove buttons
|
// Each item: Selected indicator, Name, spacer, Select, Logout & Remove buttons
|
||||||
return container.NewHBox(
|
return container.NewHBox(
|
||||||
widget.NewLabel(""), // indicator
|
widget.NewLabel(""), // indicator
|
||||||
widget.NewLabel(""), // profile name
|
widget.NewLabel(""), // profile name
|
||||||
layout.NewSpacer(),
|
layout.NewSpacer(),
|
||||||
widget.NewButton("Select", nil),
|
widget.NewButton("Select", nil),
|
||||||
|
widget.NewButton("Deregister", nil),
|
||||||
widget.NewButton("Remove", nil),
|
widget.NewButton("Remove", nil),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -55,7 +56,8 @@ func (s *serviceClient) showProfilesUI() {
|
|||||||
indicator := row.Objects[0].(*widget.Label)
|
indicator := row.Objects[0].(*widget.Label)
|
||||||
nameLabel := row.Objects[1].(*widget.Label)
|
nameLabel := row.Objects[1].(*widget.Label)
|
||||||
selectBtn := row.Objects[3].(*widget.Button)
|
selectBtn := row.Objects[3].(*widget.Button)
|
||||||
removeBtn := row.Objects[4].(*widget.Button)
|
logoutBtn := row.Objects[4].(*widget.Button)
|
||||||
|
removeBtn := row.Objects[5].(*widget.Button)
|
||||||
|
|
||||||
profile := profiles[i]
|
profile := profiles[i]
|
||||||
// Show a checkmark if selected
|
// Show a checkmark if selected
|
||||||
@@ -125,6 +127,12 @@ func (s *serviceClient) showProfilesUI() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logoutBtn.Show()
|
||||||
|
logoutBtn.SetText("Deregister")
|
||||||
|
logoutBtn.OnTapped = func() {
|
||||||
|
s.handleProfileLogout(profile.Name, refresh)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove profile
|
// Remove profile
|
||||||
removeBtn.SetText("Remove")
|
removeBtn.SetText("Remove")
|
||||||
removeBtn.OnTapped = func() {
|
removeBtn.OnTapped = func() {
|
||||||
@@ -324,6 +332,52 @@ func (s *serviceClient) getProfiles() ([]Profile, error) {
|
|||||||
return profiles, nil
|
return profiles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *serviceClient) handleProfileLogout(profileName string, refreshCallback func()) {
|
||||||
|
dialog.ShowConfirm(
|
||||||
|
"Deregister",
|
||||||
|
fmt.Sprintf("Are you sure you want to deregister from '%s'?", profileName),
|
||||||
|
func(confirm bool) {
|
||||||
|
if !confirm {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get service client: %v", err)
|
||||||
|
dialog.ShowError(fmt.Errorf("failed to connect to service"), s.wProfiles)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get current user: %v", err)
|
||||||
|
dialog.ShowError(fmt.Errorf("failed to get current user"), s.wProfiles)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username := currUser.Username
|
||||||
|
_, err = conn.Logout(s.ctx, &proto.LogoutRequest{
|
||||||
|
ProfileName: &profileName,
|
||||||
|
Username: &username,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("logout failed: %v", err)
|
||||||
|
dialog.ShowError(fmt.Errorf("deregister failed"), s.wProfiles)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.ShowInformation(
|
||||||
|
"Deregistered",
|
||||||
|
fmt.Sprintf("Successfully deregistered from '%s'", profileName),
|
||||||
|
s.wProfiles,
|
||||||
|
)
|
||||||
|
|
||||||
|
refreshCallback()
|
||||||
|
},
|
||||||
|
s.wProfiles,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type subItem struct {
|
type subItem struct {
|
||||||
*systray.MenuItem
|
*systray.MenuItem
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -339,6 +393,7 @@ type profileMenu struct {
|
|||||||
emailMenuItem *systray.MenuItem
|
emailMenuItem *systray.MenuItem
|
||||||
profileSubItems []*subItem
|
profileSubItems []*subItem
|
||||||
manageProfilesSubItem *subItem
|
manageProfilesSubItem *subItem
|
||||||
|
logoutSubItem *subItem
|
||||||
profilesState []Profile
|
profilesState []Profile
|
||||||
downClickCallback func() error
|
downClickCallback func() error
|
||||||
upClickCallback func() error
|
upClickCallback func() error
|
||||||
@@ -545,6 +600,30 @@ func (p *profileMenu) refresh() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Add Logout menu item
|
||||||
|
ctx2, cancel2 := context.WithCancel(context.Background())
|
||||||
|
logoutItem := p.profileMenuItem.AddSubMenuItem("Deregister", "")
|
||||||
|
p.logoutSubItem = &subItem{logoutItem, ctx2, cancel2}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx2.Done():
|
||||||
|
return
|
||||||
|
case _, ok := <-logoutItem.ClickedCh:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := p.eventHandler.logout(p.ctx); err != nil {
|
||||||
|
log.Errorf("logout failed: %v", err)
|
||||||
|
p.app.SendNotification(fyne.NewNotification("Error", "Failed to deregister"))
|
||||||
|
} else {
|
||||||
|
p.app.SendNotification(fyne.NewNotification("Success", "Deregistered successfully"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if activeProf.ProfileName == "default" || activeProf.Username == currUser.Username {
|
if activeProf.ProfileName == "default" || activeProf.Username == currUser.Username {
|
||||||
p.profileMenuItem.SetTitle(activeProf.ProfileName)
|
p.profileMenuItem.SetTitle(activeProf.ProfileName)
|
||||||
} else {
|
} else {
|
||||||
@@ -567,6 +646,25 @@ func (p *profileMenu) clear(profiles []Profile) {
|
|||||||
p.manageProfilesSubItem.cancel()
|
p.manageProfilesSubItem.cancel()
|
||||||
p.manageProfilesSubItem = nil
|
p.manageProfilesSubItem = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.logoutSubItem != nil {
|
||||||
|
p.logoutSubItem.Remove()
|
||||||
|
p.logoutSubItem.cancel()
|
||||||
|
p.logoutSubItem = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
func (p *profileMenu) updateMenu() {
|
||||||
@@ -577,7 +675,6 @@ func (p *profileMenu) updateMenu() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
|
||||||
// get profilesList
|
// get profilesList
|
||||||
profiles, err := p.getProfiles()
|
profiles, err := p.getProfiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -17,8 +18,9 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
"github.com/netbirdio/netbird/shared/relay/auth"
|
"github.com/netbirdio/netbird/relay/healthcheck"
|
||||||
"github.com/netbirdio/netbird/relay/server"
|
"github.com/netbirdio/netbird/relay/server"
|
||||||
|
"github.com/netbirdio/netbird/shared/relay/auth"
|
||||||
"github.com/netbirdio/netbird/signal/metrics"
|
"github.com/netbirdio/netbird/signal/metrics"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
@@ -34,12 +36,13 @@ type Config struct {
|
|||||||
LetsencryptDomains []string
|
LetsencryptDomains []string
|
||||||
// in case of using Route 53 for DNS challenge the credentials should be provided in the environment variables or
|
// in case of using Route 53 for DNS challenge the credentials should be provided in the environment variables or
|
||||||
// in the AWS credentials file
|
// in the AWS credentials file
|
||||||
LetsencryptAWSRoute53 bool
|
LetsencryptAWSRoute53 bool
|
||||||
TlsCertFile string
|
TlsCertFile string
|
||||||
TlsKeyFile string
|
TlsKeyFile string
|
||||||
AuthSecret string
|
AuthSecret string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
LogFile string
|
LogFile string
|
||||||
|
HealthcheckListenAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) Validate() error {
|
func (c Config) Validate() error {
|
||||||
@@ -87,6 +90,7 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().StringVarP(&cobraConfig.AuthSecret, "auth-secret", "s", "", "auth secret")
|
rootCmd.PersistentFlags().StringVarP(&cobraConfig.AuthSecret, "auth-secret", "s", "", "auth secret")
|
||||||
rootCmd.PersistentFlags().StringVar(&cobraConfig.LogLevel, "log-level", "info", "log level")
|
rootCmd.PersistentFlags().StringVar(&cobraConfig.LogLevel, "log-level", "info", "log level")
|
||||||
rootCmd.PersistentFlags().StringVar(&cobraConfig.LogFile, "log-file", "console", "log file")
|
rootCmd.PersistentFlags().StringVar(&cobraConfig.LogFile, "log-file", "console", "log file")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&cobraConfig.HealthcheckListenAddress, "health-listen-address", "H", ":9000", "listen address of healthcheck server")
|
||||||
|
|
||||||
setFlagsFromEnvVars(rootCmd)
|
setFlagsFromEnvVars(rootCmd)
|
||||||
}
|
}
|
||||||
@@ -102,6 +106,7 @@ func waitForExitSignal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func execute(cmd *cobra.Command, args []string) error {
|
func execute(cmd *cobra.Command, args []string) error {
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
err := cobraConfig.Validate()
|
err := cobraConfig.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("invalid config: %s", err)
|
log.Debugf("invalid config: %s", err)
|
||||||
@@ -120,7 +125,9 @@ func execute(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("setup metrics: %v", err)
|
return fmt.Errorf("setup metrics: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
log.Infof("running metrics server: %s%s", metricsServer.Addr, metricsServer.Endpoint)
|
log.Infof("running metrics server: %s%s", metricsServer.Addr, metricsServer.Endpoint)
|
||||||
if err := metricsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
if err := metricsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Fatalf("Failed to start metrics server: %v", err)
|
log.Fatalf("Failed to start metrics server: %v", err)
|
||||||
@@ -154,12 +161,31 @@ func execute(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("failed to create relay server: %v", err)
|
return fmt.Errorf("failed to create relay server: %v", err)
|
||||||
}
|
}
|
||||||
log.Infof("server will be available on: %s", srv.InstanceURL())
|
log.Infof("server will be available on: %s", srv.InstanceURL())
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if err := srv.Listen(srvListenerCfg); err != nil {
|
if err := srv.Listen(srvListenerCfg); err != nil {
|
||||||
log.Fatalf("failed to bind server: %s", err)
|
log.Fatalf("failed to bind server: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
hCfg := healthcheck.Config{
|
||||||
|
ListenAddress: cobraConfig.HealthcheckListenAddress,
|
||||||
|
ServiceChecker: srv,
|
||||||
|
}
|
||||||
|
httpHealthcheck, err := healthcheck.NewServer(hCfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to create healthcheck server: %v", err)
|
||||||
|
return fmt.Errorf("failed to create healthcheck server: %v", err)
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := httpHealthcheck.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalf("Failed to start healthcheck server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// it will block until exit signal
|
// it will block until exit signal
|
||||||
waitForExitSignal()
|
waitForExitSignal()
|
||||||
|
|
||||||
@@ -167,6 +193,10 @@ func execute(cmd *cobra.Command, args []string) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
var shutDownErrors error
|
var shutDownErrors error
|
||||||
|
if err := httpHealthcheck.Shutdown(ctx); err != nil {
|
||||||
|
shutDownErrors = multierror.Append(shutDownErrors, fmt.Errorf("failed to close healthcheck server: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
if err := srv.Shutdown(ctx); err != nil {
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
shutDownErrors = multierror.Append(shutDownErrors, fmt.Errorf("failed to close server: %s", err))
|
shutDownErrors = multierror.Append(shutDownErrors, fmt.Errorf("failed to close server: %s", err))
|
||||||
}
|
}
|
||||||
@@ -175,6 +205,8 @@ func execute(cmd *cobra.Command, args []string) error {
|
|||||||
if err := metricsServer.Shutdown(ctx); err != nil {
|
if err := metricsServer.Shutdown(ctx); err != nil {
|
||||||
shutDownErrors = multierror.Append(shutDownErrors, fmt.Errorf("failed to close metrics server: %v", err))
|
shutDownErrors = multierror.Append(shutDownErrors, fmt.Errorf("failed to close metrics server: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
return shutDownErrors
|
return shutDownErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
195
relay/healthcheck/healthcheck.go
Normal file
195
relay/healthcheck/healthcheck.go
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/relay/protocol"
|
||||||
|
"github.com/netbirdio/netbird/relay/server/listener/quic"
|
||||||
|
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusHealthy = "healthy"
|
||||||
|
statusUnhealthy = "unhealthy"
|
||||||
|
|
||||||
|
path = "/health"
|
||||||
|
|
||||||
|
cacheTTL = 3 * time.Second // Cache TTL for health status
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceChecker interface {
|
||||||
|
ListenerProtocols() []protocol.Protocol
|
||||||
|
ListenAddress() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthStatus struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
Listeners []protocol.Protocol `json:"listeners"`
|
||||||
|
CertificateValid bool `json:"certificate_valid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ListenAddress string
|
||||||
|
ServiceChecker ServiceChecker
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
config Config
|
||||||
|
httpServer *http.Server
|
||||||
|
|
||||||
|
cacheMu sync.Mutex
|
||||||
|
cacheStatus *HealthStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(config Config) (*Server, error) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
if config.ServiceChecker == nil {
|
||||||
|
return nil, errors.New("service checker is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &Server{
|
||||||
|
config: config,
|
||||||
|
httpServer: &http.Server{
|
||||||
|
Addr: config.ListenAddress,
|
||||||
|
Handler: mux,
|
||||||
|
ReadTimeout: 5 * time.Second,
|
||||||
|
WriteTimeout: 10 * time.Second,
|
||||||
|
IdleTimeout: 15 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc(path, server.handleHealthcheck)
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListenAndServe() error {
|
||||||
|
log.Infof("starting healthcheck server on: http://%s%s", dialAddress(s.config.ListenAddress), path)
|
||||||
|
return s.httpServer.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown gracefully shuts down the healthcheck server
|
||||||
|
func (s *Server) Shutdown(ctx context.Context) error {
|
||||||
|
log.Info("Shutting down healthcheck server")
|
||||||
|
return s.httpServer.Shutdown(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleHealthcheck(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
status *HealthStatus
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
// Cache check
|
||||||
|
s.cacheMu.Lock()
|
||||||
|
status = s.cacheStatus
|
||||||
|
s.cacheMu.Unlock()
|
||||||
|
|
||||||
|
if status != nil && time.Since(status.Timestamp) <= cacheTTL {
|
||||||
|
ok = status.Status == statusHealthy
|
||||||
|
} else {
|
||||||
|
status, ok = s.getHealthStatus(ctx)
|
||||||
|
// Update cache
|
||||||
|
s.cacheMu.Lock()
|
||||||
|
s.cacheStatus = status
|
||||||
|
s.cacheMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(w)
|
||||||
|
if err := encoder.Encode(status); err != nil {
|
||||||
|
log.Errorf("Failed to encode healthcheck response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getHealthStatus(ctx context.Context) (*HealthStatus, bool) {
|
||||||
|
healthy := true
|
||||||
|
status := &HealthStatus{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Status: statusHealthy,
|
||||||
|
CertificateValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners, ok := s.validateListeners()
|
||||||
|
if !ok {
|
||||||
|
status.Status = statusUnhealthy
|
||||||
|
healthy = false
|
||||||
|
}
|
||||||
|
status.Listeners = listeners
|
||||||
|
|
||||||
|
if ok := s.validateCertificate(ctx); !ok {
|
||||||
|
status.Status = statusUnhealthy
|
||||||
|
status.CertificateValid = false
|
||||||
|
healthy = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, healthy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) validateListeners() ([]protocol.Protocol, bool) {
|
||||||
|
listeners := s.config.ServiceChecker.ListenerProtocols()
|
||||||
|
if len(listeners) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return listeners, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) validateCertificate(ctx context.Context) bool {
|
||||||
|
listenAddress := s.config.ServiceChecker.ListenAddress()
|
||||||
|
if listenAddress == "" {
|
||||||
|
log.Warn("listen address is empty")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
dAddr := dialAddress(listenAddress)
|
||||||
|
|
||||||
|
for _, proto := range s.config.ServiceChecker.ListenerProtocols() {
|
||||||
|
switch proto {
|
||||||
|
case ws.Proto:
|
||||||
|
if err := dialWS(ctx, dAddr); err != nil {
|
||||||
|
log.Errorf("failed to dial WebSocket listener: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case quic.Proto:
|
||||||
|
if err := dialQUIC(ctx, dAddr); err != nil {
|
||||||
|
log.Errorf("failed to dial QUIC listener: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Warnf("unknown protocol for healthcheck: %s", proto)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialAddress(listenAddress string) string {
|
||||||
|
host, port, err := net.SplitHostPort(listenAddress)
|
||||||
|
if err != nil {
|
||||||
|
return listenAddress // fallback, might be invalid for dialing
|
||||||
|
}
|
||||||
|
|
||||||
|
if host == "" || host == "::" || host == "0.0.0.0" {
|
||||||
|
host = "0.0.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.JoinHostPort(host, port)
|
||||||
|
}
|
||||||
31
relay/healthcheck/quic.go
Normal file
31
relay/healthcheck/quic.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
|
||||||
|
tlsnb "github.com/netbirdio/netbird/shared/relay/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dialQUIC(ctx context.Context, address string) error {
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
InsecureSkipVerify: false, // Keep certificate validation enabled
|
||||||
|
NextProtos: []string{tlsnb.NBalpn},
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := quic.DialAddr(ctx, address, tlsConfig, &quic.Config{
|
||||||
|
MaxIdleTimeout: 30 * time.Second,
|
||||||
|
KeepAlivePeriod: 10 * time.Second,
|
||||||
|
EnableDatagrams: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect to QUIC server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = conn.CloseWithError(0, "availability check complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
28
relay/healthcheck/ws.go
Normal file
28
relay/healthcheck/ws.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/coder/websocket"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/shared/relay"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dialWS(ctx context.Context, address string) error {
|
||||||
|
url := fmt.Sprintf("wss://%s%s", address, relay.WebSocketURLPath)
|
||||||
|
|
||||||
|
conn, resp, err := websocket.Dial(ctx, url, nil)
|
||||||
|
if resp != nil {
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect to websocket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = conn.Close(websocket.StatusNormalClosure, "availability check complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
3
relay/protocol/protocol.go
Normal file
3
relay/protocol/protocol.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
type Protocol string
|
||||||
@@ -3,9 +3,12 @@ package listener
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/relay/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Listener interface {
|
type Listener interface {
|
||||||
Listen(func(conn net.Conn)) error
|
Listen(func(conn net.Conn)) error
|
||||||
Shutdown(ctx context.Context) error
|
Shutdown(ctx context.Context) error
|
||||||
|
Protocol() protocol.Protocol
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/relay/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const Proto protocol.Protocol = "quic"
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
// Address is the address to listen on
|
// Address is the address to listen on
|
||||||
Address string
|
Address string
|
||||||
@@ -50,6 +54,10 @@ func (l *Listener) Listen(acceptFn func(conn net.Conn)) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Protocol() protocol.Protocol {
|
||||||
|
return Proto
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Listener) Shutdown(ctx context.Context) error {
|
func (l *Listener) Shutdown(ctx context.Context) error {
|
||||||
if l.listener == nil {
|
if l.listener == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ import (
|
|||||||
"github.com/coder/websocket"
|
"github.com/coder/websocket"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/relay/protocol"
|
||||||
"github.com/netbirdio/netbird/shared/relay"
|
"github.com/netbirdio/netbird/shared/relay"
|
||||||
)
|
)
|
||||||
|
|
||||||
// URLPath is the path for the websocket connection.
|
const (
|
||||||
const URLPath = relay.WebSocketURLPath
|
Proto protocol.Protocol = "ws"
|
||||||
|
URLPath = relay.WebSocketURLPath
|
||||||
|
)
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
// Address is the address to listen on.
|
// Address is the address to listen on.
|
||||||
@@ -51,6 +54,10 @@ func (l *Listener) Listen(acceptFn func(conn net.Conn)) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Protocol() protocol.Protocol {
|
||||||
|
return Proto
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Listener) Shutdown(ctx context.Context) error {
|
func (l *Listener) Shutdown(ctx context.Context) error {
|
||||||
if l.server == nil {
|
if l.server == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
nberrors "github.com/netbirdio/netbird/client/errors"
|
nberrors "github.com/netbirdio/netbird/client/errors"
|
||||||
|
"github.com/netbirdio/netbird/relay/protocol"
|
||||||
"github.com/netbirdio/netbird/relay/server/listener"
|
"github.com/netbirdio/netbird/relay/server/listener"
|
||||||
"github.com/netbirdio/netbird/relay/server/listener/quic"
|
"github.com/netbirdio/netbird/relay/server/listener/quic"
|
||||||
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
"github.com/netbirdio/netbird/relay/server/listener/ws"
|
||||||
quictls "github.com/netbirdio/netbird/shared/relay/tls"
|
quictls "github.com/netbirdio/netbird/shared/relay/tls"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListenerConfig is the configuration for the listener.
|
// ListenerConfig is the configuration for the listener.
|
||||||
@@ -26,8 +28,11 @@ type ListenerConfig struct {
|
|||||||
// It is the gate between the WebSocket listener and the Relay server logic.
|
// It is the gate between the WebSocket listener and the Relay server logic.
|
||||||
// In a new HTTP connection, the server will accept the connection and pass it to the Relay server via the Accept method.
|
// In a new HTTP connection, the server will accept the connection and pass it to the Relay server via the Accept method.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
relay *Relay
|
listenAddr string
|
||||||
listeners []listener.Listener
|
|
||||||
|
relay *Relay
|
||||||
|
listeners []listener.Listener
|
||||||
|
listenerMux sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates and returns a new relay server instance.
|
// NewServer creates and returns a new relay server instance.
|
||||||
@@ -57,10 +62,14 @@ func NewServer(config Config) (*Server, error) {
|
|||||||
|
|
||||||
// Listen starts the relay server.
|
// Listen starts the relay server.
|
||||||
func (r *Server) Listen(cfg ListenerConfig) error {
|
func (r *Server) Listen(cfg ListenerConfig) error {
|
||||||
|
r.listenAddr = cfg.Address
|
||||||
|
|
||||||
wSListener := &ws.Listener{
|
wSListener := &ws.Listener{
|
||||||
Address: cfg.Address,
|
Address: cfg.Address,
|
||||||
TLSConfig: cfg.TLSConfig,
|
TLSConfig: cfg.TLSConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.listenerMux.Lock()
|
||||||
r.listeners = append(r.listeners, wSListener)
|
r.listeners = append(r.listeners, wSListener)
|
||||||
|
|
||||||
tlsConfigQUIC, err := quictls.ServerQUICTLSConfig(cfg.TLSConfig)
|
tlsConfigQUIC, err := quictls.ServerQUICTLSConfig(cfg.TLSConfig)
|
||||||
@@ -85,6 +94,8 @@ func (r *Server) Listen(cfg ListenerConfig) error {
|
|||||||
}(l)
|
}(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.listenerMux.Unlock()
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(errChan)
|
close(errChan)
|
||||||
var multiErr *multierror.Error
|
var multiErr *multierror.Error
|
||||||
@@ -100,12 +111,15 @@ func (r *Server) Listen(cfg ListenerConfig) error {
|
|||||||
func (r *Server) Shutdown(ctx context.Context) error {
|
func (r *Server) Shutdown(ctx context.Context) error {
|
||||||
r.relay.Shutdown(ctx)
|
r.relay.Shutdown(ctx)
|
||||||
|
|
||||||
|
r.listenerMux.Lock()
|
||||||
var multiErr *multierror.Error
|
var multiErr *multierror.Error
|
||||||
for _, l := range r.listeners {
|
for _, l := range r.listeners {
|
||||||
if err := l.Shutdown(ctx); err != nil {
|
if err := l.Shutdown(ctx); err != nil {
|
||||||
multiErr = multierror.Append(multiErr, err)
|
multiErr = multierror.Append(multiErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r.listeners = r.listeners[:0]
|
||||||
|
r.listenerMux.Unlock()
|
||||||
return nberrors.FormatErrorOrNil(multiErr)
|
return nberrors.FormatErrorOrNil(multiErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,3 +127,18 @@ func (r *Server) Shutdown(ctx context.Context) error {
|
|||||||
func (r *Server) InstanceURL() string {
|
func (r *Server) InstanceURL() string {
|
||||||
return r.relay.instanceURL
|
return r.relay.instanceURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Server) ListenerProtocols() []protocol.Protocol {
|
||||||
|
result := make([]protocol.Protocol, 0)
|
||||||
|
|
||||||
|
r.listenerMux.Lock()
|
||||||
|
for _, l := range r.listeners {
|
||||||
|
result = append(result, l.Protocol())
|
||||||
|
}
|
||||||
|
r.listenerMux.Unlock()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Server) ListenAddress() string {
|
||||||
|
return r.listenAddr
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
const nbalpn = "nb-quic"
|
const NBalpn = "nb-quic"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func ClientQUICTLSConfig() *tls.Config {
|
|||||||
|
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
InsecureSkipVerify: true, // Debug mode allows insecure connections
|
InsecureSkipVerify: true, // Debug mode allows insecure connections
|
||||||
NextProtos: []string{nbalpn}, // Ensure this matches the server's ALPN
|
NextProtos: []string{NBalpn}, // Ensure this matches the server's ALPN
|
||||||
RootCAs: certPool,
|
RootCAs: certPool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func ClientQUICTLSConfig() *tls.Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
NextProtos: []string{nbalpn},
|
NextProtos: []string{NBalpn},
|
||||||
RootCAs: certPool,
|
RootCAs: certPool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func ServerQUICTLSConfig(originTLSCfg *tls.Config) (*tls.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg := originTLSCfg.Clone()
|
cfg := originTLSCfg.Clone()
|
||||||
cfg.NextProtos = []string{nbalpn}
|
cfg.NextProtos = []string{NBalpn}
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +74,6 @@ func generateTestTLSConfig() (*tls.Config, error) {
|
|||||||
|
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
Certificates: []tls.Certificate{tlsCert},
|
Certificates: []tls.Certificate{tlsCert},
|
||||||
NextProtos: []string{nbalpn},
|
NextProtos: []string{NBalpn},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ func ServerQUICTLSConfig(originTLSCfg *tls.Config) (*tls.Config, error) {
|
|||||||
return nil, fmt.Errorf("valid TLS config is required for QUIC listener")
|
return nil, fmt.Errorf("valid TLS config is required for QUIC listener")
|
||||||
}
|
}
|
||||||
cfg := originTLSCfg.Clone()
|
cfg := originTLSCfg.Clone()
|
||||||
cfg.NextProtos = []string{nbalpn}
|
cfg.NextProtos = []string{NBalpn}
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user